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:
@@ -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.
|
||||
18
state-hub-inbox/2026-03-08-railiance01-bootstrap.yaml
Normal file
18
state-hub-inbox/2026-03-08-railiance01-bootstrap.yaml
Normal 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
35
state-hub-inbox/README.md
Normal 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`
|
||||
Reference in New Issue
Block a user