generated from coulomb/repo-seed
chore: update standalone state hub wiring
This commit is contained in:
2
Makefile
2
Makefile
@@ -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
|
||||||
|
|
||||||
|
|||||||
15
README.md
15
README.md
@@ -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
|
||||||
|
|
||||||
|
|||||||
7
SCOPE.md
7
SCOPE.md
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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/
|
||||||
|
|||||||
@@ -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)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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":{...}}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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/
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -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
4
uv.lock
generated
@@ -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" },
|
||||||
|
|||||||
Reference in New Issue
Block a user