chore: update standalone state hub wiring

This commit is contained in:
2026-05-17 20:01:21 +02:00
parent d444462de0
commit fdc395a600
23 changed files with 40 additions and 42 deletions

View File

@@ -257,7 +257,7 @@ fix-consistency-all:
## Cancel open tasks belonging to completed/archived workstreams. ## Cancel open tasks belonging to completed/archived workstreams.
## Safe to run at any time; also suitable for a daily cron job. ## Safe to run at any time; also suitable for a daily cron job.
## Cron example: 0 3 * * * cd ~/the-custodian/state-hub && make cleanup-stale ## Cron example: 0 3 * * * cd ~/state-hub && make cleanup-stale
cleanup-stale: cleanup-stale:
uv run python scripts/cleanup_stale_tasks.py uv run python scripts/cleanup_stale_tasks.py

View File

@@ -4,8 +4,8 @@ State Hub is the live coordination service for the Custodian ecosystem:
PostgreSQL persistence, FastAPI API, FastMCP server, Observable dashboard, PostgreSQL persistence, FastAPI API, FastMCP server, Observable dashboard,
consistency tooling, and repo/workplan synchronization. consistency tooling, and repo/workplan synchronization.
This repository is the standalone home for the service. It replaces the former This repository is the standalone home for the service. It was extracted from
embedded implementation at: the former embedded implementation at:
```text ```text
/home/worsch/the-custodian/state-hub /home/worsch/the-custodian/state-hub
@@ -15,13 +15,12 @@ embedded implementation at:
The repo is being prepared by `CUST-WP-0043 - State Hub Repo Extraction`. The repo is being prepared by `CUST-WP-0043 - State Hub Repo Extraction`.
During extraction: During the extraction window:
- The live implementation still exists in `the-custodian/state-hub/`. - The implementation has been imported here with subtree history.
- This repo owns the standalone baseline and will become authoritative after - `CUST-WP-0042` has been re-homed into this repository.
the implementation move and verification gate. - The old embedded tree in `the-custodian` should remain only as a pointer once
- State Hub implementation work should land here once registration and the verification gate passes.
workplan re-homing are complete.
## Workplans ## Workplans

View File

@@ -29,15 +29,14 @@ telemetry.
## Current Extraction Note ## Current Extraction Note
This repo is being established as the standalone State Hub home under This repo is being established as the standalone State Hub home under
`CUST-WP-0043`. Until the extraction verification gate passes, the prior live `CUST-WP-0043`. The implementation has been imported here from:
implementation remains at:
```text ```text
/home/worsch/the-custodian/state-hub /home/worsch/the-custodian/state-hub
``` ```
After verification, this repository becomes the authoritative implementation After verification, this repository is authoritative and the embedded copy in
tree and the embedded copy should be removed or replaced with a pointer. `the-custodian` should be removed or replaced with a pointer.
## Workplan Convention ## Workplan Convention

View File

@@ -24,7 +24,7 @@ from typing import Any
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
PUBLISHER = "the-custodian/state-hub" PUBLISHER = "state-hub"
class EventEnvelope(BaseModel): class EventEnvelope(BaseModel):

View File

@@ -38,7 +38,7 @@ Two services are exposed through the tunnel:
### Start the services ### Start the services
```bash ```bash
cd ~/the-custodian/state-hub cd ~/state-hub
make api # FastAPI on :8000 make api # FastAPI on :8000
make mcp-http # MCP SSE server on :8001 (separate terminal) make mcp-http # MCP SSE server on :8001 (separate terminal)

View File

@@ -317,7 +317,7 @@ CSS variables, so they adapt correctly to both light (`air`) and dark
## Running the dashboard ## Running the dashboard
```bash ```bash
cd ~/the-custodian/state-hub/dashboard cd ~/state-hub/dashboard
npm run dev # Preview server on :3000 with hot reload npm run dev # Preview server on :3000 with hot reload
npm run build # Static build into dist/ npm run build # Static build into dist/

View File

@@ -38,7 +38,7 @@ If the API goes offline while you are viewing a page:
To restart the API: To restart the API:
```bash ```bash
cd ~/the-custodian/state-hub cd ~/state-hub
make api # db + migrate + uvicorn (restarts if already running) make api # db + migrate + uvicorn (restarts if already running)
``` ```

View File

@@ -157,7 +157,7 @@ The CLI is installed via `make install-cli` from the state-hub directory.
Ensure `~/.local/bin` is on your `PATH`. Ensure `~/.local/bin` is on your `PATH`.
**`ERROR: Cannot reach API`** **`ERROR: Cannot reach API`**
The state hub API must be running: `cd ~/the-custodian/state-hub && make api` The state hub API must be running: `cd ~/state-hub && make api`
**`CLAUDE.custodian.md` already exists** **`CLAUDE.custodian.md` already exists**
Re-running `custodian register-project` overwrites it with a fresh Re-running `custodian register-project` overwrites it with a fresh

View File

@@ -71,7 +71,7 @@ custodian register-project --domain <slug>
```bash ```bash
# Auto-detects lockfile at repo root # Auto-detects lockfile at repo root
cd ~/the-custodian/state-hub cd ~/state-hub
make ingest-sbom REPO=<slug> REPO_PATH=/absolute/path make ingest-sbom REPO=<slug> REPO_PATH=/absolute/path
# Multi-ecosystem repo — scan all lockfiles recursively # Multi-ecosystem repo — scan all lockfiles recursively

View File

@@ -205,7 +205,7 @@ The hub's schema is organised in concentric layers:
## Running the hub ## Running the hub
```bash ```bash
cd ~/the-custodian/state-hub cd ~/state-hub
make db # Start PostgreSQL (Docker) make db # Start PostgreSQL (Docker)
make migrate # Alembic upgrade head make migrate # Alembic upgrade head

View File

@@ -107,7 +107,7 @@ const _liveEl = html`<div class="live-indicator">
<span style="color:${_ok ? 'var(--theme-foreground-focus)' : 'red'}">●</span> <span style="color:${_ok ? 'var(--theme-foreground-focus)' : 'red'}">●</span>
${_ok ${_ok
? `Live · updated ${_ts?.toLocaleTimeString()}` ? `Live · updated ${_ts?.toLocaleTimeString()}`
: html`<span style="color:red">Offline — run: <code>cd ~/the-custodian/state-hub && make api</code></span>`} : html`<span style="color:red">Offline — run: <code>cd ~/state-hub && make api</code></span>`}
</div>`; </div>`;
withDocHelp(_liveEl, "/docs/live-data"); withDocHelp(_liveEl, "/docs/live-data");
injectTocTop("live-indicator", _liveEl); injectTocTop("live-indicator", _liveEl);

View File

@@ -78,7 +78,7 @@ display(html`<div class="app-grid">
icon: "🐘", name: "pgAdmin", status: pgadminUp, icon: "🐘", name: "pgAdmin", status: pgadminUp,
desc: "PostgreSQL admin UI for the state-hub database. Start with: make db in state-hub/.", desc: "PostgreSQL admin UI for the state-hub database. Start with: make db in state-hub/.",
url: "http://127.0.0.1:5050", label: "127.0.0.1:5050", url: "http://127.0.0.1:5050", label: "127.0.0.1:5050",
note: pgadminUp ? "" : "Start with: cd ~/the-custodian/state-hub && docker compose --profile pgadmin up -d", note: pgadminUp ? "" : "Start with: cd ~/state-hub && docker compose --profile pgadmin up -d",
})} })}
${appCard({ ${appCard({
icon: "🌉", name: "ops-bridge", status: null, icon: "🌉", name: "ops-bridge", status: null,

View File

@@ -24,7 +24,7 @@ events** on NATS JetStream so activity-core can react.
POST /domain-goals/*/activate │ │ │ POST /domain-goals/*/activate │ │ │
scripts/cleanup_stale_tasks.py │ │ ▼ scripts/cleanup_stale_tasks.py │ │ ▼
└──────────────────────┘ RunActivityWorkflow └──────────────────────┘ RunActivityWorkflow
the-custodian/state-hub (creates tasks in state-hub (creates tasks in
issue-core, etc.) issue-core, etc.)
``` ```
@@ -147,5 +147,5 @@ curl -X POST http://127.0.0.1:8000/repos/<slug>/sync
# Observe the envelope on the subscriber. Sample shape: # Observe the envelope on the subscriber. Sample shape:
# {"id":"...","type":"org.statehub.workstream.completed","version":"1.0", # {"id":"...","type":"org.statehub.workstream.completed","version":"1.0",
# "timestamp":"...","publisher":"the-custodian/state-hub","attributes":{...}} # "timestamp":"...","publisher":"state-hub","attributes":{...}}
``` ```

View File

@@ -39,7 +39,7 @@ run them on a schedule.
### A. `state-hub-consistency-sweep` ### A. `state-hub-consistency-sweep`
```yaml ```yaml
# activity-definitions/the-custodian/state-hub-consistency-sweep.yaml # activity-definitions/state-hub-consistency-sweep.yaml
id: the-custodian.state-hub-consistency-sweep id: the-custodian.state-hub-consistency-sweep
description: | description: |
Sweep all registered repos: pull, reconcile workplan files ↔ DB, Sweep all registered repos: pull, reconcile workplan files ↔ DB,
@@ -60,7 +60,7 @@ rule:
instruction: instruction:
kind: shell kind: shell
cmd: >- cmd: >-
cd /home/worsch/the-custodian/state-hub && cd /home/worsch/state-hub &&
.venv/bin/python scripts/consistency_check.py --remote --all --max-seconds 300 .venv/bin/python scripts/consistency_check.py --remote --all --max-seconds 300
on_failure: log_and_continue # warn-only sweeps must not page on transient failures on_failure: log_and_continue # warn-only sweeps must not page on transient failures
``` ```
@@ -75,7 +75,7 @@ Notes:
### B. `state-hub-stale-task-cleanup` ### B. `state-hub-stale-task-cleanup`
```yaml ```yaml
# activity-definitions/the-custodian/state-hub-stale-task-cleanup.yaml # activity-definitions/state-hub-stale-task-cleanup.yaml
id: the-custodian.state-hub-stale-task-cleanup id: the-custodian.state-hub-stale-task-cleanup
description: | description: |
Daily sweep that cancels tasks still 'todo|in_progress|blocked' inside Daily sweep that cancels tasks still 'todo|in_progress|blocked' inside
@@ -88,7 +88,7 @@ trigger:
instruction: instruction:
kind: shell kind: shell
cmd: >- cmd: >-
cd /home/worsch/the-custodian/state-hub && cd /home/worsch/state-hub &&
.venv/bin/python scripts/cleanup_stale_tasks.py .venv/bin/python scripts/cleanup_stale_tasks.py
``` ```

View File

@@ -59,13 +59,13 @@ Each message body conforms to the `EventEnvelope` schema in
"type": "org.statehub.repo.registered", "type": "org.statehub.repo.registered",
"version": "1.0", "version": "1.0",
"timestamp": "2026-05-17T14:00:00Z", "timestamp": "2026-05-17T14:00:00Z",
"publisher": "the-custodian/state-hub", "publisher": "state-hub",
"attributes": { "...": "event-specific" } "attributes": { "...": "event-specific" }
} }
``` ```
`type` matches the subject. `publisher` is always `type` matches the subject. `publisher` is always
`the-custodian/state-hub` for events emitted from this repo. `state-hub` for events emitted from this repo.
--- ---

View File

@@ -74,7 +74,7 @@ If systemd is not available, fall back to crontab:
```bash ```bash
# Crontab fallback (run crontab -e and add): # Crontab fallback (run crontab -e and add):
*/15 * * * * curl -sf http://127.0.0.1:8000/state/health && cd ~/the-custodian/state-hub && .venv/bin/python scripts/consistency_check.py --remote --all >> /tmp/custodian-sync.log 2>&1 */15 * * * * curl -sf http://127.0.0.1:8000/state/health && cd ~/state-hub && .venv/bin/python scripts/consistency_check.py --remote --all >> /tmp/custodian-sync.log 2>&1
``` ```
--- ---

View File

@@ -4,7 +4,7 @@ cleanup_stale_tasks.py — cancel tasks that are still open in completed/archive
Run manually: python3 scripts/cleanup_stale_tasks.py Run manually: python3 scripts/cleanup_stale_tasks.py
Run via make: make cleanup-stale Run via make: make cleanup-stale
Cron example: 0 3 * * * cd ~/the-custodian/state-hub && .venv/bin/python scripts/cleanup_stale_tasks.py Cron example: 0 3 * * * cd ~/state-hub && .venv/bin/python scripts/cleanup_stale_tasks.py
Exit codes: Exit codes:
0 — ran successfully (zero or more tasks cancelled) 0 — ran successfully (zero or more tasks cancelled)
@@ -78,7 +78,7 @@ def main() -> int:
workstreams = get("/workstreams/") workstreams = get("/workstreams/")
except urllib.error.URLError as e: except urllib.error.URLError as e:
print(f"[cleanup-stale] ERROR: API unreachable — {e}", file=sys.stderr) print(f"[cleanup-stale] ERROR: API unreachable — {e}", file=sys.stderr)
print("[cleanup-stale] Start the API with: cd ~/the-custodian/state-hub && make api", file=sys.stderr) print("[cleanup-stale] Start the API with: cd ~/state-hub && make api", file=sys.stderr)
return 1 return 1
closed_ws = {w["id"]: w for w in workstreams if w["status"] in CLOSED_WS_STATUS} closed_ws = {w["id"]: w for w in workstreams if w["status"] in CLOSED_WS_STATUS}

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
cd /home/worsch/the-custodian/state-hub cd /home/worsch/state-hub
API_BASE="${API_BASE:-http://127.0.0.1:8000}" API_BASE="${API_BASE:-http://127.0.0.1:8000}"
HEALTH_URL="${API_BASE%/}/state/health" HEALTH_URL="${API_BASE%/}/state/health"

View File

@@ -93,7 +93,7 @@ curl -s -X PATCH "http://127.0.0.1:8000/tasks/<task_id>" \
1. Update workplan file task statuses to reflect progress 1. Update workplan file task statuses to reflect progress
2. Log: `POST /progress/` with a summary of what changed 2. Log: `POST /progress/` with a summary of what changed
3. Note for the custodian operator: after workplan file changes, run from 3. Note for the custodian operator: after workplan file changes, run from
`~/the-custodian/state-hub`: `~/state-hub`:
```bash ```bash
make fix-consistency REPO={REPO_SLUG} make fix-consistency REPO={REPO_SLUG}
``` ```

View File

@@ -5,4 +5,4 @@
## Quick Reference ## Quick Reference
`~/the-custodian/state-hub/mcp_server/TOOLS.md` — MCP tool reference `~/state-hub/mcp_server/TOOLS.md` — MCP tool reference

View File

@@ -4,5 +4,5 @@ This repo owns **{PROJECT_NAME}** only. It does not own:
<!-- TODO: List what belongs in adjacent repos, e.g.: <!-- TODO: List what belongs in adjacent repos, e.g.:
- SSH key management → railiance-infra/ - SSH key management → railiance-infra/
- State hub code → the-custodian/state-hub/ - State hub code → state-hub/
--> -->

View File

@@ -12,7 +12,7 @@ Then call the MCP tool for richer cross-domain context (skip if unreachable):
``` ```
get_domain_summary("{DOMAIN}") get_domain_summary("{DOMAIN}")
``` ```
If the hub is offline: `cd ~/the-custodian/state-hub && make api` If the hub is offline: `cd ~/state-hub && make api`
**Step 2 — Check inbox** **Step 2 — Check inbox**
``` ```
@@ -51,12 +51,12 @@ add_progress_event(summary="...", topic_id="{TOPIC_ID}", workstream_id="<uuid>")
If workplan files were modified, ensure the local copy is up to date first: If workplan files were modified, ensure the local copy is up to date first:
```bash ```bash
git -C <repo_path> pull --ff-only git -C <repo_path> pull --ff-only
cd ~/the-custodian/state-hub && make fix-consistency REPO={REPO_SLUG} cd ~/state-hub && make fix-consistency REPO={REPO_SLUG}
``` ```
For repos where implementation runs on a remote machine (e.g. CoulombCore), For repos where implementation runs on a remote machine (e.g. CoulombCore),
use the combined target which pulls before fixing: use the combined target which pulls before fixing:
```bash ```bash
cd ~/the-custodian/state-hub && make fix-consistency-remote REPO={REPO_SLUG} cd ~/state-hub && make fix-consistency-remote REPO={REPO_SLUG}
``` ```
**C-15** (DB task ahead of file) is normal in multi-machine workflows — writeback **C-15** (DB task ahead of file) is normal in multi-machine workflows — writeback
will sync the file to match DB. **C-16** (repo behind remote) blocks all writes will sync the file to match DB. **C-16** (repo behind remote) blocks all writes

4
uv.lock generated
View File

@@ -672,7 +672,7 @@ wheels = [
[[package]] [[package]]
name = "llm-connect" name = "llm-connect"
version = "0.1.0" version = "0.1.0"
source = { editable = "../../llm-connect" } source = { editable = "../llm-connect" }
dependencies = [ dependencies = [
{ name = "toml" }, { name = "toml" },
] ]
@@ -1468,7 +1468,7 @@ requires-dist = [
{ name = "fastapi", specifier = ">=0.115.0" }, { name = "fastapi", specifier = ">=0.115.0" },
{ name = "fastmcp", specifier = ">=2.0.0" }, { name = "fastmcp", specifier = ">=2.0.0" },
{ name = "httpx", specifier = ">=0.28.0" }, { name = "httpx", specifier = ">=0.28.0" },
{ name = "llm-connect", editable = "../../llm-connect" }, { name = "llm-connect", editable = "../llm-connect" },
{ name = "nats-py", specifier = ">=2.7.0" }, { name = "nats-py", specifier = ">=2.7.0" },
{ name = "psycopg2-binary", specifier = ">=2.9.0" }, { name = "psycopg2-binary", specifier = ">=2.9.0" },
{ name = "pydantic", specifier = ">=2.10.0" }, { name = "pydantic", specifier = ">=2.10.0" },