From 3481a15e7f9311feeef49865d7f79844c1afdbff Mon Sep 17 00:00:00 2001 From: tegwick Date: Sun, 26 Apr 2026 13:17:50 +0200 Subject: [PATCH] feat(registration): add --codex flag and AGENTS.md template MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - register_project.sh: parse --additional/--codex as named flags (not positional), skip MCP check in codex mode, generate AGENTS.md from agents-codex.template instead of CLAUDE.md + .claude/rules/ - agents-codex.template: new template for Codex repos — HTTP REST session protocol, inbox/progress curl examples, ADR-001 workplan convention - Makefile: add register-codex-project target Driven by onboarding repo-registry (first non-Claude-Code repo, first repo under the capabilities domain). Co-Authored-By: Claude Sonnet 4.6 --- state-hub/Makefile | 10 +- .../project_rules/agents-codex.template | 149 ++++++++++++++++ state-hub/scripts/register_project.sh | 167 +++++++++++------- 3 files changed, 264 insertions(+), 62 deletions(-) create mode 100644 state-hub/scripts/project_rules/agents-codex.template diff --git a/state-hub/Makefile b/state-hub/Makefile index 8ffd59d..2950c1f 100644 --- a/state-hub/Makefile +++ b/state-hub/Makefile @@ -1,4 +1,4 @@ -.PHONY: install install-cli db db-tools migrate seed api dashboard check test clean register-project validate-adr add-domain rename-domain add-repo list-repos register-path cleanup-stale tunnels-up tunnels-status tunnels-check bridges install-hooks install-hooks-all gitea-inventory +.PHONY: install install-cli db db-tools migrate seed api dashboard check test clean register-project register-codex-project validate-adr add-domain rename-domain add-repo list-repos register-path cleanup-stale tunnels-up tunnels-status tunnels-check bridges install-hooks install-hooks-all gitea-inventory COMPOSE = docker compose -f infra/docker-compose.yml --env-file .env @@ -88,12 +88,18 @@ api: db @fuser -k 8000/tcp 2>/dev/null && echo "Stopped running API" || true uv run uvicorn api.main:app --reload --host 127.0.0.1 --port 8000 -## Register a project: make register-project DOMAIN=railiance PROJECT_PATH=/home/worsch/railiance +## Register a project (Claude Code): 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= PROJECT_PATH="; exit 1) @test -n "$(PROJECT_PATH)" || (echo "ERROR: PROJECT_PATH is required."; exit 1) scripts/register_project.sh "$(DOMAIN)" "$(PROJECT_PATH)" +## Register a Codex project (AGENTS.md + HTTP API): make register-codex-project DOMAIN=capabilities PROJECT_PATH=/home/worsch/my-repo +register-codex-project: + @test -n "$(DOMAIN)" || (echo "ERROR: DOMAIN is required. Usage: make register-codex-project DOMAIN= PROJECT_PATH="; exit 1) + @test -n "$(PROJECT_PATH)" || (echo "ERROR: PROJECT_PATH is required."; exit 1) + scripts/register_project.sh "$(DOMAIN)" "$(PROJECT_PATH)" --codex + ## 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) diff --git a/state-hub/scripts/project_rules/agents-codex.template b/state-hub/scripts/project_rules/agents-codex.template new file mode 100644 index 0000000..64ee108 --- /dev/null +++ b/state-hub/scripts/project_rules/agents-codex.template @@ -0,0 +1,149 @@ +# {PROJECT_NAME} — Agent Instructions + +## Repo Identity + +**Purpose:** {PROJECT_DESCRIPTION} + +**Domain:** {DOMAIN} +**Repo slug:** {REPO_SLUG} +**Topic ID:** `{TOPIC_ID}` +**Workplan prefix:** `{WP_PREFIX}-` + +--- + +## State Hub Integration + +The Custodian State Hub tracks work across all domains. Interact via HTTP REST — +there is no MCP server for Codex agents. + +| Context | URL | +|---------|-----| +| Local workstation | `http://127.0.0.1:8000` | +| Remote via tunnel | `http://127.0.0.1:18000` | + +### Orient at session start + +```bash +# Offline brief — works without hub connection +cat .custodian-brief.md + +# Active workstreams for this domain +curl -s "http://127.0.0.1:8000/workstreams/?topic_id={TOPIC_ID}&status=active" \ + | python3 -m json.tool + +# Check inbox +curl -s "http://127.0.0.1:8000/messages/?to_agent={REPO_SLUG}&unread_only=true" \ + | python3 -m json.tool +``` + +Mark a message read: +```bash +curl -s -X PATCH "http://127.0.0.1:8000/messages//read" \ + -H "Content-Type: application/json" -d '{}' +``` + +### Log progress (required at session close) + +```bash +curl -s -X POST http://127.0.0.1:8000/progress/ \ + -H "Content-Type: application/json" \ + -d '{ + "summary": "what was done", + "event_type": "note", + "author": "codex", + "workstream_id": "", + "task_id": "" + }' +``` + +Omit `workstream_id` / `task_id` when not applicable. + +### Update task status + +```bash +curl -s -X PATCH "http://127.0.0.1:8000/tasks/" \ + -H "Content-Type: application/json" \ + -d '{"status": "in_progress"}' +# values: todo | in_progress | done | blocked +``` + +### Flag a task for human review + +```bash +curl -s -X PATCH "http://127.0.0.1:8000/tasks/" \ + -H "Content-Type: application/json" \ + -d '{"needs_human": true, "intervention_note": "reason"}' +``` + +--- + +## Session Protocol + +**Start:** +1. `cat .custodian-brief.md` — domain goal and open workstreams (offline-safe) +2. Check inbox: `GET /messages/?to_agent={REPO_SLUG}&unread_only=true`; mark read +3. Scan workplans: `ls workplans/` — note `status: active` files and open tasks +4. Check blocked tasks: `GET /tasks/?needs_human=true` + +**During work:** +- Update task statuses in workplan files as tasks progress +- Record significant decisions via `POST /decisions/` + +**Close:** +1. Update workplan file task statuses to reflect progress +2. Log: `POST /progress/` with a summary of what changed +3. Note for the custodian operator: after workplan file changes, run from + `~/the-custodian/state-hub`: + ```bash + make fix-consistency REPO={REPO_SLUG} + ``` + This syncs task status from files into the hub DB. + +--- + +## Workplan Convention (ADR-001) + +Work items originate as files in this repo — not in the hub. The hub is a +read/cache/index layer that rebuilds from files. + +**File location:** `workplans/{WP_PREFIX}-NNNN-.md` + +**Frontmatter:** + +```yaml +--- +id: {WP_PREFIX}-NNNN +type: workplan +title: "..." +domain: {DOMAIN} +repo: {REPO_SLUG} +status: active | done +owner: codex +topic_slug: ... +created: "YYYY-MM-DD" +updated: "YYYY-MM-DD" +state_hub_workstream_id: "" # written by fix-consistency — do not edit +--- +``` + +**Task block format** (one per `##` section): + +``` +## Task Title + +` ` `task +id: {WP_PREFIX}-NNNN-T01 +status: todo | in_progress | done | blocked +priority: high | medium | low +state_hub_task_id: "" # written by fix-consistency — do not edit +` ` ` + +Task description text. +``` + +Status progression: `todo` → `in_progress` → `done` (or `blocked`) + +To create a new workplan: +1. Write the file following the format above +2. Notify the custodian operator to run `make fix-consistency REPO={REPO_SLUG}` + (or send a message to the hub agent via `POST /messages/`) diff --git a/state-hub/scripts/register_project.sh b/state-hub/scripts/register_project.sh index 849358b..7b98172 100755 --- a/state-hub/scripts/register_project.sh +++ b/state-hub/scripts/register_project.sh @@ -1,23 +1,24 @@ #!/usr/bin/env bash # register_project.sh — register a project/repo with the Custodian State Hub # -# Usage: scripts/register_project.sh [--additional] +# Usage: scripts/register_project.sh [flags] # domain: slug of an active domain (e.g. custodian, railiance) # project_path: absolute path to the project directory -# --additional: add a second repo to an existing domain; skip CLAUDE.md generation +# --additional: add a second repo to an existing domain; skip agent file generation +# --codex: generate AGENTS.md (HTTP API) instead of CLAUDE.md + .claude/rules/ # -# Example: +# Examples: # scripts/register_project.sh railiance /home/worsch/railiance # scripts/register_project.sh railiance /home/worsch/railiance-infra --additional +# scripts/register_project.sh capabilities /home/worsch/my-repo --codex # # What it does: # 1. Verify the API is reachable # 2. Verify the domain exists via GET /domains/{slug}/ # 3. Look up the topic ID for the domain (first active topic) -# 4. Check that state-hub is in ~/.claude.json; warn if missing -# 5. Write CLAUDE.md + .claude/rules/*.md (modular @-import structure) -# - If CLAUDE.md already exists, writes .claude/rules/ only and appends -# an @-import comment block to the existing CLAUDE.md +# 4. Check that state-hub is in ~/.claude.json (skipped with --codex) +# 5a. [default] Write CLAUDE.md + .claude/rules/*.md (modular @-import structure) +# 5b. [--codex] Write AGENTS.md from agents-codex.template + SCOPE.md # 6. POST to /repos/ to register the repo # 7. POST /repos/{slug}/paths/ to register this machine's local path (host_paths) # 8. POST a progress event recording the registration @@ -26,7 +27,14 @@ set -euo pipefail DOMAIN="${1:-}" PROJECT_PATH="${2:-}" -ADDITIONAL="${3:-}" +ADDITIONAL=false +CODEX_MODE=false +for arg in "${@:3}"; do + case "$arg" in + --additional) ADDITIONAL=true ;; + --codex) CODEX_MODE=true ;; + esac +done SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" STATE_HUB_DIR="$(dirname "$SCRIPT_DIR")" RULES_TEMPLATES_DIR="$SCRIPT_DIR/project_rules" @@ -92,8 +100,11 @@ else fi # ── Step 4: Check MCP registration ──────────────────────────────────────────── -echo "==> Checking MCP server registration ..." -MCP_OK="$(python3 -c " +if [[ "$CODEX_MODE" == "true" ]]; then + echo "==> Codex mode: skipping MCP registration check (Claude Code-specific)" +else + echo "==> Checking MCP server registration ..." + MCP_OK="$(python3 -c " import json from pathlib import Path f = Path.home() / '.claude.json' @@ -103,11 +114,12 @@ else: d = json.loads(f.read_text()) print('OK' if 'state-hub' in d.get('mcpServers', {}) else 'NOT_REGISTERED') ")" -case "$MCP_OK" in - MISSING_FILE) echo "WARNING: ~/.claude.json not found. MCP server not registered." ;; - NOT_REGISTERED) echo "WARNING: 'state-hub' not in ~/.claude.json. See global CLAUDE.md §MCP Server Registration." ;; - *) echo " MCP OK" ;; -esac + case "$MCP_OK" in + MISSING_FILE) echo "WARNING: ~/.claude.json not found. MCP server not registered." ;; + NOT_REGISTERED) echo "WARNING: 'state-hub' not in ~/.claude.json. See global CLAUDE.md §MCP Server Registration." ;; + *) echo " MCP OK" ;; + esac +fi # ── Helper: render a template with variable substitution ────────────────────── render_template() { @@ -122,39 +134,65 @@ render_template() { "$tmpl" } -# ── Step 5: Write .claude/rules/ + CLAUDE.md ────────────────────────────────── -if [[ "$ADDITIONAL" != "--additional" ]]; then - RULES_DIR="$PROJECT_PATH/.claude/rules" - CLAUDE_MD="$PROJECT_PATH/CLAUDE.md" - - echo "==> Writing .claude/rules/ ..." - mkdir -p "$RULES_DIR" - - for rule in repo-identity session-protocol first-session workplan-convention \ - stack-and-commands architecture repo-boundary agents; do - tmpl="$RULES_TEMPLATES_DIR/${rule}.template" - out="$RULES_DIR/${rule}.md" - if [[ -f "$tmpl" ]]; then - render_template "$tmpl" > "$out" - echo " .claude/rules/${rule}.md" - else - echo "WARNING: template missing: $tmpl" - fi - done +# ── Step 5: Write agent instruction files ───────────────────────────────────── +if [[ "$ADDITIONAL" != "true" ]]; then SCOPE_MD="$PROJECT_PATH/SCOPE.md" if [[ ! -f "$SCOPE_MD" ]]; then echo "==> Writing SCOPE.md stub ..." render_template "$RULES_TEMPLATES_DIR/scope.template" > "$SCOPE_MD" - echo " SCOPE.md written (stub — fill with scope-analyst agent)." + echo " SCOPE.md written (stub — fill in purpose and boundaries)." else echo "==> SCOPE.md already exists — skipping." fi - if [[ -f "$CLAUDE_MD" ]]; then + if [[ "$CODEX_MODE" == "true" ]]; then + # ── 5b: Codex — write AGENTS.md from HTTP-API template ──────────────── + AGENTS_MD="$PROJECT_PATH/AGENTS.md" + CODEX_TMPL="$RULES_TEMPLATES_DIR/agents-codex.template" + + if [[ ! -f "$CODEX_TMPL" ]]; then + echo "ERROR: agents-codex.template not found at $CODEX_TMPL" + exit 1 + fi + + if [[ -f "$AGENTS_MD" ]]; then + echo "==> AGENTS.md already exists — skipping (manual merge may be needed)." + else + echo "==> Writing AGENTS.md (Codex / HTTP API mode) ..." + render_template "$CODEX_TMPL" > "$AGENTS_MD" + echo " AGENTS.md written." + fi + echo "" - echo "==> CLAUDE.md already exists — appending @-import suggestion." - cat >> "$CLAUDE_MD" << 'SUGGESTION' + echo "Files needing manual content:" + echo " AGENTS.md — fill in purpose description and stack/commands sections" + echo " SCOPE.md — fill in scope boundaries" + + else + # ── 5a: Claude Code — write .claude/rules/ + CLAUDE.md ──────────────── + RULES_DIR="$PROJECT_PATH/.claude/rules" + CLAUDE_MD="$PROJECT_PATH/CLAUDE.md" + + echo "==> Writing .claude/rules/ ..." + mkdir -p "$RULES_DIR" + + for rule in repo-identity session-protocol first-session workplan-convention \ + stack-and-commands architecture repo-boundary agents; do + tmpl="$RULES_TEMPLATES_DIR/${rule}.template" + out="$RULES_DIR/${rule}.md" + if [[ -f "$tmpl" ]]; then + render_template "$tmpl" > "$out" + echo " .claude/rules/${rule}.md" + else + echo "WARNING: template missing: $tmpl" + fi + done + + if [[ -f "$CLAUDE_MD" ]]; then + echo "" + echo "==> CLAUDE.md already exists — appending @-import suggestion." + cat >> "$CLAUDE_MD" << 'SUGGESTION' SUGGESTION - echo " Suggestion appended to existing CLAUDE.md." - else - echo "==> Writing CLAUDE.md ..." - render_template "$RULES_TEMPLATES_DIR/claude-md.template" > "$CLAUDE_MD" - echo " CLAUDE.md written." - fi + echo " Suggestion appended to existing CLAUDE.md." + else + echo "==> Writing CLAUDE.md ..." + render_template "$RULES_TEMPLATES_DIR/claude-md.template" > "$CLAUDE_MD" + echo " CLAUDE.md written." + fi - echo "" - echo "Rule files needing manual content:" - echo " .claude/rules/repo-identity.md — update purpose description" - echo " .claude/rules/stack-and-commands.md — language, deps, dev commands" - echo " .claude/rules/architecture.md — design overview" - echo " .claude/rules/repo-boundary.md — what this repo does NOT own" - echo " .claude/rules/workplan-convention.md — verify WP prefix: $WP_PREFIX" + echo "" + echo "Rule files needing manual content:" + echo " .claude/rules/repo-identity.md — update purpose description" + echo " .claude/rules/stack-and-commands.md — language, deps, dev commands" + echo " .claude/rules/architecture.md — design overview" + echo " .claude/rules/repo-boundary.md — what this repo does NOT own" + echo " .claude/rules/workplan-convention.md — verify WP prefix: $WP_PREFIX" + fi fi # ── Step 6: Register repo in State Hub ──────────────────────────────────────── @@ -246,18 +285,26 @@ echo " Event recorded." echo "" echo "Registration complete!" -echo " Project: $PROJECT_NAME" -echo " Domain: $DOMAIN" -echo " Repo slug: $REPO_SLUG" -[[ "$TOPIC_ID" != "(none)" ]] && echo " Topic ID: $TOPIC_ID" -echo " WP prefix: $WP_PREFIX (update .claude/rules/workplan-convention.md if wrong)" +echo " Project: $PROJECT_NAME" +echo " Domain: $DOMAIN" +echo " Repo slug: $REPO_SLUG" +[[ "$TOPIC_ID" != "(none)" ]] && echo " Topic ID: $TOPIC_ID" +echo " WP prefix: $WP_PREFIX" +echo " Agent mode: $([[ "$CODEX_MODE" == "true" ]] && echo "codex (AGENTS.md)" || echo "claude (CLAUDE.md + .claude/rules/)")" echo "" -echo "Next steps:" -echo " 1. Fill in TODO stubs in .claude/rules/" -echo " 2. Restart Claude Code for the MCP server to be available in this project" +if [[ "$CODEX_MODE" == "true" ]]; then + echo "Next steps:" + echo " 1. Fill in purpose description in AGENTS.md" + echo " 2. Fill in stack and build commands in AGENTS.md" + echo " 3. Write workplan files, then run: make fix-consistency REPO=$REPO_SLUG" +else + echo "Next steps:" + echo " 1. Fill in TODO stubs in .claude/rules/" + echo " 2. Restart Claude Code for the MCP server to be available in this project" +fi # ── Optional: SBOM ingest ───────────────────────────────────────────────────── -if [[ "$ADDITIONAL" != "--additional" ]]; then +if [[ "$ADDITIONAL" != "true" ]]; then echo "" read -r -p "==> Run SBOM ingest now? [y/N] " INGEST_NOW