Skip to content

feat(cc-230): state-store writer + codex-dispatch runs_append wiring#159

Merged
screenleon merged 6 commits into
mainfrom
feat/cc-230-state-store
May 25, 2026
Merged

feat(cc-230): state-store writer + codex-dispatch runs_append wiring#159
screenleon merged 6 commits into
mainfrom
feat/cc-230-state-store

Conversation

@screenleon
Copy link
Copy Markdown
Owner

Summary

  • New scripts/lib/state-writer.sh: XDG-compliant state store at ~/.local/share/pm-dispatch/state/; per-project partitioning via sha1(git rev-parse --show-toplevel); serialize_with_lock-backed runs_append / events_append / task_upsert; all functions best-effort (never propagate errors to callers)
  • scripts/codex-dispatch.sh: sources state-writer.sh; after each dispatch appends a Run row to runs.jsonl using jq -cn for safe JSON encoding; routes to target project partition via _SW_REPO_ROOT=WORK_DIR; entire block is { … } 2>/dev/null || true — EXIT code unaffected
  • scripts/test-state-store.sh: 18-case regression suite (store root resolution, dir init, append/upsert JSONL, dispatch integration with fake codex, partition key correctness, invalid ID rejection, failed dispatch recording)
  • scripts/run-all-tests.sh + .github/workflows/lint.yml: suite registration and CI job

Dual-write strategy: routing_log.md stays unchanged in M1; runs.jsonl is added in parallel. M2 will cut hook-routing-log.sh.

Gate

Full tier — Final: GO (gate-20260525-155943.md)
Remaining advisories (non-blocking, follow-up tracked in BACKLOG):

  • sha1sum portability — consider portable hash helper in portable.sh (CC-263)
  • Run-row JSON construction inline in dispatch adapter — extract to writer helper before second executor (deferred to M2/M3)

Test plan

  • Review gate result: .gate-results/gate-20260525-155943.md
  • Check bash scripts/test-state-store.sh — 18 passed
  • Check bash scripts/run-all-tests.sh — 30 passed
  • Verify scripts/lib/state-writer.sh best-effort invariant: any function failure leaves caller exit code = 0
  • Verify codex-dispatch.sh EXIT code preservation (existing test-codex-dispatch.sh covers this)

🤖 Generated with Claude Code

screenleon and others added 6 commits May 25, 2026 15:11
M1 state store writer (designated-writer boundary per core/state/layout.yaml):
- scripts/lib/state-writer.sh: _sw_store_root (XDG path resolution),
  _sw_project_key (sha1 of git toplevel), state_store_init (mkdir + VERSION),
  runs_append, events_append, task_upsert — all best-effort, never fatal
- scripts/codex-dispatch.sh: source state-writer.sh; after EXIT=$? add
  guarded block that builds Run JSON and calls runs_append (task_id
  extracted from brief file/inline, fallback UNKN-0)
- hook-routing-log.sh unchanged (dual-write: routing_log.md stays in M1)
- scripts/test-state-store.sh: 10 test cases (path resolution, append,
  lock, non-fatal failure, dispatch self-containment)
- run-all-tests.sh + test-run-all-tests.sh + lint.yml: register new suite

30 suites pass (0 failed).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… dispatch tests

- _sw_project_key: check _SW_REPO_ROOT env var first so runs_append resolves
  the target repo partition from WORK_DIR, not the caller's cwd
- codex-dispatch.sh: set _SW_REPO_ROOT=WORK_DIR before runs_append; build Run
  JSON via jq -cn to safely escape quotes/backslashes in path/model fields
- task_upsert: validate task_id against ^[A-Z]{1,4}-[0-9]+[a-z]?$ before using
  it in a filesystem path (prevents path traversal)
- test-state-store.sh: add 3 integration tests (cases 11–13): fake-codex dispatch
  creates runs.jsonl, correct project partition for --cd target, valid JSON with
  special chars in model field — suite now 13 cases, all passing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…vents fixture, regressions

- _sw_project_key: use git -C "$_SW_REPO_ROOT" rev-parse --show-toplevel to
  normalize subdirectory paths to the repo root before hashing the partition key
- codex-dispatch.sh: anchor both task_id: grep patterns with ^ so parent_task_id
  and similar prefixed keys are not mistaken for the real task attribution
- test-state-store.sh: remove invalid "type" field from events_append fixture
  (event.schema.json has additionalProperties: false)
- test-state-store.sh: add case_dispatch_subdir_partition_key (case 14) and
  case_dispatch_task_id_anchor (case 15) — suite now 15 cases, all passing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… jq failure log

- test-state-store.sh: add Verifies/Steps docstrings to cases 1–10 per QA rules
- test-state-store.sh: add case 16 (inline -- <brief> task_id extraction) and
  case 17 (failed dispatch records state:failed + exit code) — suite now 17 cases
- codex-dispatch.sh: log _sw_log_error when jq Run JSON construction produces
  empty output so missing run rows have forensic evidence

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…end JSON validity

- Add case_task_upsert_invalid_id: calls task_upsert with "../evil", asserts exit 0
  and no file is created — covers the path-traversal guard added in round 1
- case_events_append: add jq parse assertion to verify written JSONL is valid JSON,
  matching the existing case_runs_append_valid_jsonl pattern (medium sweep)

Suite: 18 passed, 0 failed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…dium advisory)

Raised by critic/architecture-reviewer/risk-reviewer in CC-230 gate 5:
sha1sum (GNU only) used directly in _sw_project_key without fallback to
shasum -a 1 (macOS) — platforms without either silently route to global
partition, violating core/state/layout.yaml per-project partitioning.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@screenleon screenleon merged commit 529b719 into main May 25, 2026
22 checks passed
screenleon added a commit that referenced this pull request May 25, 2026
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant