chore: add state-hub offline inbox and FR for ingest support

- state-hub-inbox/: convention for queuing progress events during
  degraded-mode sessions (no tunnel to State Hub)
- First pending event: Railiance01 bootstrap milestone (T03-T05)
- contrib/feature-requests/: FR for automated inbox ingest in state-hub
- README documents the drain procedure until automation is in place

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-08 22:58:51 +00:00
parent 679d0d67b1
commit ea7270368f
3 changed files with 141 additions and 0 deletions

View File

@@ -0,0 +1,88 @@
---
type: feature-request
id: fr-2026-03-08--threephoenix--state-hub--offline-inbox-ingest
target_org: threephoenix
target_repo: state-hub
status: draft
created: "2026-03-08"
source_repo: railiance-hosts
related_workstream_id: bf40b47e-be5b-4930-a7d2-362e76b943bb
---
# FR: Offline Inbox Ingest for Degraded-Mode Sessions
## Problem
When a Claude session runs on a remote host (e.g. HostEurope) without an active
SSH reverse tunnel, the State Hub MCP server is unreachable. Any progress events,
decision records, or task status updates that would normally be written via
`add_progress_event()` / `record_decision()` are silently lost unless the operator
manually replays them from their local machine after the session.
This creates a gap in the audit trail and requires manual follow-up that is easy
to forget.
## Proposed Solution
### 1. Inbox convention in domain repos
Domain repos write pending events to a `state-hub-inbox/` directory as YAML files:
```
state-hub-inbox/
YYYY-MM-DD-<slug>.yaml
```
Each file is a structured event:
```yaml
type: progress_event # or: decision, task_status_update
topic_id: <uuid>
workstream_id: <uuid>
event_type: milestone # matches add_progress_event() event_type values
summary: "..."
detail: {}
status: pending # state-hub sets to "ingested" after processing
recorded_at: "YYYY-MM-DD"
source_repo: <repo-slug>
```
Files are committed to git so they are never lost.
### 2. Ingest command in state-hub
Add a `make ingest-inbox` target (or equivalent CLI command) that:
1. Scans registered domain repos for `state-hub-inbox/*.yaml` files with `status: pending`
2. Calls the appropriate API endpoint for each (`add_progress_event`, `record_decision`, etc.)
3. Updates `status: ingested` and commits back (or opens a PR) so files are not replayed
Alternatively, the MCP server could expose a `ingest_inbox_file(path)` tool that
Claude calls at session start when the hub is reachable, to drain any queued events
from previous degraded sessions.
### 3. Session start behaviour
At orientation (Step 1), after `get_domain_summary()` succeeds, Claude should:
- Glob `state-hub-inbox/*.yaml` in the current repo
- For each file with `status: pending`, call the appropriate write tool and mark it ingested
This keeps the drain logic in Claude rather than requiring a separate make target.
## Acceptance Criteria
- `state-hub-inbox/` files with `status: pending` are reliably ingested on next
connected session with no manual intervention
- Ingested files are marked so they are not replayed
- Works for at minimum: `progress_event`, `decision`, `task_status_update`
## Example
See: `state-hub-inbox/2026-03-08-railiance01-bootstrap.yaml` in this repo —
the first real offline event that motivated this request.
## Priority
Medium. The workaround (manual replay) works but degrades auditability for
sessions run from remote hosts, which is the intended production workflow for
this project.

View File

@@ -0,0 +1,18 @@
type: progress_event
topic_id: ca369340-a64e-442e-98f1-a4fa7dc74a38
workstream_id: bf40b47e-be5b-4930-a7d2-362e76b943bb
event_type: milestone
summary: "HostEurope server (Railiance01) bootstrapped and hardened"
detail:
tasks_completed: [T03, T04, T05]
server: Railiance01
ip: 92.205.62.239
changes:
- Extended base role with fail2ban, UFW k3s/Flannel rules, HISTCONTROL
- Fixed dynamic inventory script JSON format
- Added roles_path to ansible.cfg
- Bootstrapped server: UFW active, fail2ban running, SSH hardened
smoke_tests_passed: true
status: pending # state-hub sets to "ingested" after processing
recorded_at: "2026-03-08"
source_repo: railiance-hosts

35
state-hub-inbox/README.md Normal file
View File

@@ -0,0 +1,35 @@
# state-hub-inbox
Offline event queue for State Hub sessions run without an active reverse tunnel.
When the State Hub MCP server is unreachable, Claude writes pending events here
as YAML files instead of dropping them. On the next connected session, these are
drained by calling the appropriate State Hub write tools and marking each file
`status: ingested`.
## File format
```yaml
type: progress_event # or: decision, task_status_update
topic_id: <uuid>
workstream_id: <uuid>
event_type: milestone
summary: "..."
detail: {}
status: pending # → ingested after processing
recorded_at: "YYYY-MM-DD"
source_repo: railiance-hosts
```
## Drain procedure (manual until automated)
At session start, if `get_domain_summary()` succeeds, check for pending files:
```bash
grep -l "status: pending" state-hub-inbox/*.yaml
```
For each, call `add_progress_event()` with the file's fields, then update
`status: pending``status: ingested` and commit.
See also: `contrib/feature-requests/fr-2026-03-08--threephoenix--state-hub--offline-inbox-ingest.md`