2026-03-02 00:32:35 +01:00
|
|
|
.PHONY: install install-cli db db-tools migrate seed api dashboard check start clean register-project validate-adr add-domain rename-domain add-repo list-repos cleanup-stale
|
Add state-hub v0.1 — local-first state service for the Custodian
Implements the first live layer of the Custodian cognitive infrastructure:
PostgreSQL schema, FastAPI REST API, FastMCP stdio server, and Observable
Framework telemetry dashboard.
- state-hub/: full stack (docker-compose, FastAPI, Alembic, MCP server, dashboard)
- 5 DB tables: topics, workstreams, tasks, decisions, progress_events
- 11 MCP tools + 5 resources registered in .mcp.json
- Observable dashboard: Overview, Workstreams, Decisions, Progress pages
- CLAUDE.md: session protocol (get_state_summary / add_progress_event ritual)
- ~/.claude/CLAUDE.md: global cross-project reference to the hub
- scripts/pull_image.py: WSL2 TLS-resilient Docker image downloader
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 17:47:49 +01:00
|
|
|
|
|
|
|
|
COMPOSE = docker compose -f infra/docker-compose.yml --env-file .env
|
|
|
|
|
|
|
|
|
|
install:
|
|
|
|
|
uv sync
|
|
|
|
|
|
2026-02-24 22:55:41 +01:00
|
|
|
## Symlink the custodian CLI into ~/.local/bin so it's on PATH system-wide
|
|
|
|
|
install-cli: install
|
|
|
|
|
mkdir -p ~/.local/bin
|
|
|
|
|
ln -sf "$(shell pwd)/.venv/bin/custodian" ~/.local/bin/custodian
|
|
|
|
|
@echo "Installed: custodian → $$(readlink -f ~/.local/bin/custodian)"
|
|
|
|
|
@echo "Make sure ~/.local/bin is on your PATH:"
|
|
|
|
|
@echo " echo 'export PATH=\"\$$HOME/.local/bin:\$$PATH\"' >> ~/.bashrc && source ~/.bashrc"
|
|
|
|
|
|
Add state-hub v0.1 — local-first state service for the Custodian
Implements the first live layer of the Custodian cognitive infrastructure:
PostgreSQL schema, FastAPI REST API, FastMCP stdio server, and Observable
Framework telemetry dashboard.
- state-hub/: full stack (docker-compose, FastAPI, Alembic, MCP server, dashboard)
- 5 DB tables: topics, workstreams, tasks, decisions, progress_events
- 11 MCP tools + 5 resources registered in .mcp.json
- Observable dashboard: Overview, Workstreams, Decisions, Progress pages
- CLAUDE.md: session protocol (get_state_summary / add_progress_event ritual)
- ~/.claude/CLAUDE.md: global cross-project reference to the hub
- scripts/pull_image.py: WSL2 TLS-resilient Docker image downloader
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 17:47:49 +01:00
|
|
|
db:
|
|
|
|
|
$(COMPOSE) up -d postgres
|
|
|
|
|
|
|
|
|
|
db-tools:
|
|
|
|
|
$(COMPOSE) --profile tools up -d
|
|
|
|
|
|
|
|
|
|
migrate:
|
|
|
|
|
uv run alembic upgrade head
|
|
|
|
|
|
|
|
|
|
seed:
|
|
|
|
|
uv run python scripts/seed.py
|
|
|
|
|
|
|
|
|
|
api:
|
|
|
|
|
uv run uvicorn api.main:app --reload --host 127.0.0.1 --port 8000
|
|
|
|
|
|
|
|
|
|
dashboard:
|
|
|
|
|
cd dashboard && npm run dev
|
|
|
|
|
|
|
|
|
|
check:
|
|
|
|
|
curl -sf http://127.0.0.1:8000/state/health | python3 -m json.tool
|
|
|
|
|
|
|
|
|
|
start: db
|
|
|
|
|
sleep 3
|
|
|
|
|
$(MAKE) migrate
|
|
|
|
|
$(MAKE) api
|
|
|
|
|
|
2026-02-24 22:22:53 +01:00
|
|
|
## Register a project: make register-project DOMAIN=railiance PROJECT_PATH=/home/worsch/railiance
|
|
|
|
|
register-project:
|
|
|
|
|
@test -n "$(DOMAIN)" || (echo "ERROR: DOMAIN is required. Usage: make register-project DOMAIN=<domain> PROJECT_PATH=<path>"; exit 1)
|
|
|
|
|
@test -n "$(PROJECT_PATH)" || (echo "ERROR: PROJECT_PATH is required."; exit 1)
|
|
|
|
|
scripts/register_project.sh "$(DOMAIN)" "$(PROJECT_PATH)"
|
|
|
|
|
|
feat(state-hub): implement v0.5 — dynamic domains & multi-repo
Replaces the hardcoded 6-domain PostgreSQL ENUM with a first-class
`domains` DB table, and adds a `managed_repos` table for multi-repo
support per domain.
P1 — Domain as a DB entity:
- Migration b1c2d3e4f5a6: creates `domains` table, migrates topics.domain
ENUM column to domain_id FK, drops the domain ENUM type
- Domain ORM model (api/models/domain.py) + Pydantic schemas
- Domain API router: GET/POST /domains/, GET/PATCH /domains/{slug}/,
rename and archive endpoints with EP/TD cascade on rename
- Topic model updated: domain_id FK + @property domain_slug for
backwards-compatible JSON serialization (field renamed domain → domain_slug)
- TopicCreate/TopicRead updated; seed.py rewritten to use FK lookup
P2 — Multi-repo support:
- ManagedRepo ORM model (api/models/managed_repo.py) + schemas
- Repo API router: GET/POST /repos/, GET/PATCH /repos/{slug}/, archive
- Makefile: add-domain, rename-domain, add-repo, list-repos targets
- register_project.sh: verify domain via /domains/ API + POST /repos/
P3 — MCP tools & live validation:
- 6 new MCP tools: list_domains, create_domain, rename_domain,
archive_domain, list_domain_repos, register_repo
- EP/TD routers: replace hardcoded VALID_DOMAINS set with per-request
DB lookup — returns 422 with list of valid slugs on unknown domain
- State summary: adds domains: list[DomainSummary] (slug, name,
repo_count, active_workstream_count, ep_count, td_count)
- TOOLS.md updated with domain management section
P4 — Dashboard:
- New domains.md page with KPI row + domain cards + repo lists
- domains.json.py + repos.json.py data loaders
- Domains page added to observablehq.config.js nav
- workstreams.md, extensions.md, techdept.md: domain_slug fix +
dynamic domain list loaded from /domains/ API (no longer hardcoded)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 15:20:15 +01:00
|
|
|
## Add a second repo to an existing domain: make add-repo DOMAIN=railiance REPO_PATH=/home/worsch/railiance-infra
|
|
|
|
|
add-repo:
|
|
|
|
|
@test -n "$(DOMAIN)" || (echo "ERROR: DOMAIN is required."; exit 1)
|
|
|
|
|
@test -n "$(REPO_PATH)" || (echo "ERROR: REPO_PATH is required."; exit 1)
|
|
|
|
|
scripts/register_project.sh "$(DOMAIN)" "$(REPO_PATH)" --additional
|
|
|
|
|
|
|
|
|
|
## Create a new domain: make add-domain DOMAIN=my_domain NAME="My Domain"
|
|
|
|
|
add-domain:
|
|
|
|
|
@test -n "$(DOMAIN)" || (echo "ERROR: DOMAIN is required (slug)."; exit 1)
|
|
|
|
|
@test -n "$(NAME)" || (echo "ERROR: NAME is required (display name)."; exit 1)
|
|
|
|
|
curl -sf -X POST http://127.0.0.1:8000/domains/ \
|
|
|
|
|
-H "Content-Type: application/json" \
|
|
|
|
|
-d "{\"slug\": \"$(DOMAIN)\", \"name\": \"$(NAME)\"}" | python3 -m json.tool
|
|
|
|
|
|
|
|
|
|
## Rename a domain: make rename-domain DOMAIN=old_slug NEW_SLUG=new_slug NEW_NAME="New Name"
|
|
|
|
|
rename-domain:
|
|
|
|
|
@test -n "$(DOMAIN)" || (echo "ERROR: DOMAIN (old slug) is required."; exit 1)
|
|
|
|
|
@test -n "$(NEW_SLUG)" || (echo "ERROR: NEW_SLUG is required."; exit 1)
|
|
|
|
|
@test -n "$(NEW_NAME)" || (echo "ERROR: NEW_NAME is required."; exit 1)
|
|
|
|
|
curl -sf -X PATCH http://127.0.0.1:8000/domains/$(DOMAIN)/rename \
|
|
|
|
|
-H "Content-Type: application/json" \
|
|
|
|
|
-d "{\"new_slug\": \"$(NEW_SLUG)\", \"new_name\": \"$(NEW_NAME)\"}" | python3 -m json.tool
|
|
|
|
|
|
|
|
|
|
## List repos for a domain: make list-repos DOMAIN=railiance
|
|
|
|
|
list-repos:
|
|
|
|
|
@test -n "$(DOMAIN)" || (echo "ERROR: DOMAIN is required."; exit 1)
|
|
|
|
|
curl -sf "http://127.0.0.1:8000/repos/?domain=$(DOMAIN)" | python3 -m json.tool
|
|
|
|
|
|
feat(sbom): scan mode, domain grouping dashboard, SBOM convention doc
- ingest_sbom.py: add --scan flag (recursive lockfile discovery) +
--lockfile repeatable for explicit multi-file ingestion; skip
.venv/node_modules/.git/dist/etc; Makefile gains SCAN= and REPO_PATH= vars
- sbom.md: add /domains/ fetch; domain-level summary table; per-repo
accordion with details/summary; domain filter on package table; dual-
licence false-positive note; +1 KPI card (Domains Covered)
- canon/standards/sbom-convention_v0.1.md: authoritative lockfile table,
ingest workflow (single/scan/explicit), snapshot semantics, direct-vs-
transitive caveats, licence governance + copyleft escalation, update
cadence, multi-repo domain pattern, planned enhancements
First ingest: the-custodian — 420 pkgs (88 python + 332 node), 13 licence
groups, 1 copyleft flag (jszip dual-licensed MIT OR GPL-3.0-or-later)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 16:15:40 +01:00
|
|
|
## Ingest SBOM data for a repo.
|
|
|
|
|
## Single lockfile (explicit): make ingest-sbom REPO=the-custodian LOCKFILE=/path/to/uv.lock
|
|
|
|
|
## Scan all lockfiles in tree: make ingest-sbom REPO=the-custodian SCAN=1 REPO_PATH=/home/worsch/the-custodian
|
|
|
|
|
## Auto-detect at repo root: make ingest-sbom REPO=the-custodian REPO_PATH=/home/worsch/the-custodian
|
2026-02-28 17:28:49 +01:00
|
|
|
ingest-sbom:
|
feat(sbom): scan mode, domain grouping dashboard, SBOM convention doc
- ingest_sbom.py: add --scan flag (recursive lockfile discovery) +
--lockfile repeatable for explicit multi-file ingestion; skip
.venv/node_modules/.git/dist/etc; Makefile gains SCAN= and REPO_PATH= vars
- sbom.md: add /domains/ fetch; domain-level summary table; per-repo
accordion with details/summary; domain filter on package table; dual-
licence false-positive note; +1 KPI card (Domains Covered)
- canon/standards/sbom-convention_v0.1.md: authoritative lockfile table,
ingest workflow (single/scan/explicit), snapshot semantics, direct-vs-
transitive caveats, licence governance + copyleft escalation, update
cadence, multi-repo domain pattern, planned enhancements
First ingest: the-custodian — 420 pkgs (88 python + 332 node), 13 licence
groups, 1 copyleft flag (jszip dual-licensed MIT OR GPL-3.0-or-later)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 16:15:40 +01:00
|
|
|
@test -n "$(REPO)" || (echo "ERROR: REPO is required."; exit 1)
|
|
|
|
|
uv run python scripts/ingest_sbom.py --repo "$(REPO)" \
|
|
|
|
|
$(if $(LOCKFILE),--lockfile "$(LOCKFILE)") \
|
|
|
|
|
$(if $(SCAN),--scan) \
|
|
|
|
|
$(if $(REPO_PATH),--repo-path "$(REPO_PATH)")
|
2026-02-28 17:28:49 +01:00
|
|
|
|
feat(custodian): add ADR-001 compliance validator
Scripts, Makefile target, and MCP tool for checking a repository
against ADR-001 (workplans as repo artefacts, state-hub as cache).
Checks performed:
File-side: workplans/ dir exists, valid YAML frontmatter (required
fields, type, status, id format), filename matches id, embedded
task blocks have id/status/priority.
State-hub cross-reference: state_hub_workstream_id references
resolve to real DB records; orphan detection flags active DB
workstreams with no backing workplan file.
Usage:
make validate-adr REPO=<path> [DOMAIN=<slug>]
validate_repo_adr(repo_path, domain_slug?) # MCP tool
Running against the-custodian itself correctly surfaces the 4
pre-ADR-001 workstreams that still need workplan files written.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 12:00:09 +01:00
|
|
|
## Check a repo for ADR-001 compliance: make validate-adr REPO=/path/to/repo [DOMAIN=custodian]
|
|
|
|
|
validate-adr:
|
|
|
|
|
@test -n "$(REPO)" || (echo "ERROR: REPO is required. Usage: make validate-adr REPO=<path> [DOMAIN=<slug>]"; exit 1)
|
|
|
|
|
uv run python scripts/validate_repo_adr.py "$(REPO)" $(if $(DOMAIN),--domain "$(DOMAIN)",)
|
|
|
|
|
|
2026-03-02 00:32:35 +01:00
|
|
|
## Cancel open tasks belonging to completed/archived workstreams.
|
|
|
|
|
## 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
|
|
|
|
|
cleanup-stale:
|
|
|
|
|
uv run python scripts/cleanup_stale_tasks.py
|
|
|
|
|
|
Add state-hub v0.1 — local-first state service for the Custodian
Implements the first live layer of the Custodian cognitive infrastructure:
PostgreSQL schema, FastAPI REST API, FastMCP stdio server, and Observable
Framework telemetry dashboard.
- state-hub/: full stack (docker-compose, FastAPI, Alembic, MCP server, dashboard)
- 5 DB tables: topics, workstreams, tasks, decisions, progress_events
- 11 MCP tools + 5 resources registered in .mcp.json
- Observable dashboard: Overview, Workstreams, Decisions, Progress pages
- CLAUDE.md: session protocol (get_state_summary / add_progress_event ritual)
- ~/.claude/CLAUDE.md: global cross-project reference to the hub
- scripts/pull_image.py: WSL2 TLS-resilient Docker image downloader
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 17:47:49 +01:00
|
|
|
clean:
|
|
|
|
|
$(COMPOSE) down -v
|