CUST-WP-0061-T03: the promotion transition (statehub promote-intake)
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 3s

The mechanism named in canon/standards/work-record-types_v0.1.md:
"Promotion is a first-class transition... manual transcription of an
intake item into other kinds is a process defect." This is what AWQ-010
needed and didn't have -- a human/agent had to notice, transcribe, and
re-register it by hand. One call now does what that manual pass did.

scripts/promote_intake.py: intake.routed -> workplan | task | decision |
engagement.
  - workplan: new ADR-001 file at workplans/{ID}-{slug}.md, registered
    against the hub (repo+topic resolution, POST /workplans, frontmatter
    id write-back)
  - task: appended as a ```task``` block to an existing --workplan-file,
    registered via POST /tasks, reuses the existing
    _inject_task_id_into_block writeback helper
  - decision: appended as a ```yaml``` block with a fresh
    {PREFIX}-DEC-{YYYY}-{NNN} id to --target-file, registered the same
    way C-32 registers decisions (reuses _inject_yaml_block_field)
  - engagement: appended as a ```yaml``` block with a fresh
    {PREFIX}-ENG-{YYYY}-{NNN} id -- file-only, no hub entity exists yet
    (same honest deferral as C-32), reported not silently skipped

In every case the intake is closed with outcome=promoted and
promoted_to=<new canonical id>; the new record carries an
origin: "intake:<id>" back-link.

Wired as `statehub promote-intake <intake-id> --to <kind> --repo-slug
<slug> --repo-path <path> --domain <domain> [--target-file ...]
[--workplan-file ...]`, matching the CLI shape named in the workplan text.

17 tests: pure helpers (_slugify, _next_number, _append_yaml_block,
frontmatter injection) offline; full promote_intake() flow with the hub
API mocked.

Live-verified against the real running API/DB and a real repo
(binky-control), not just mocks -- and the live proof caught a real bug:
the first workplan-promotion run silently produced a false success (the
intake was closed outcome=promoted, but /workplans/ actually 422'd on a
missing repo_id that the code never resolved, so no workstream was ever
created). Fixed to resolve repo_id via /repos/{slug} and to raise loudly
on registration failure instead of writing a half-registered file
silently; locked in as two regression tests. Re-verified clean:
workplan promotion (CLI direct + through `statehub promote-intake`
itself) and decision promotion both proven end-to-end against the live
hub, with all scratch artefacts (files + hub rows) cleaned up afterward.
No regressions: full state-hub suite (271 tests across
test_promote_intake, test_intake, test_work_record_registration,
test_work_record_check, test_routers_core, test_consistency_check,
test_consistency_sweep, test_mcp_smoke, test_mcp_write_tools) green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
tegwick 2026-07-21 01:06:01 +02:00
parent aade470f4a
commit 3dbbc753bc
3 changed files with 694 additions and 0 deletions

View file

@ -422,6 +422,30 @@ def cmd_fix_consistency(args: argparse.Namespace) -> None:
sys.exit(exit_code)
def cmd_promote_intake(args: argparse.Namespace) -> None:
"""Promote a routed intake into a workplan, task, decision, or engagement."""
script = STATE_HUB_DIR / "scripts" / "promote_intake.py"
if not script.exists():
print(f"ERROR: promote_intake.py not found at {script}")
sys.exit(1)
cmd = [
sys.executable, str(script), args.intake_id,
"--to", args.to,
"--repo-path", str(Path(args.repo_path).expanduser().resolve()),
"--repo-slug", args.repo_slug,
"--domain", args.domain,
"--api-base", args.api_base,
]
if args.target_file:
cmd.extend(["--target-file", args.target_file])
if args.workplan_file:
cmd.extend(["--workplan-file", args.workplan_file])
result = subprocess.run(cmd)
sys.exit(result.returncode)
def cmd_create_workstream(args: argparse.Namespace) -> None:
"""Create a workstream under a domain's topic."""
_api_get("/state/health")
@ -684,6 +708,20 @@ def main() -> None:
help="Preserve checker exit code 2 for warnings-only runs",
)
# promote-intake
promote = sub.add_parser(
"promote-intake",
help="Promote a routed intake into a workplan, task, decision, or engagement (CUST-WP-0061-T03)",
)
promote.add_argument("intake_id", help="UUID of the routed intake")
promote.add_argument("--to", required=True, choices=["workplan", "task", "decision", "engagement"])
promote.add_argument("--repo-path", default=os.getcwd(), help="Local checkout of the target repo")
promote.add_argument("--repo-slug", required=True, help="Registered repo slug")
promote.add_argument("--domain", required=True, help="Market domain slug, e.g. infotech")
promote.add_argument("--target-file", default=None, help="Repo-relative path (required for decision/engagement)")
promote.add_argument("--workplan-file", default=None, help="Repo-relative workplan file path (required for task)")
promote.add_argument("--api-base", default=API_BASE, help="State Hub API base URL")
# create-workstream
cws = sub.add_parser("create-workstream", help="Create a workstream under a domain topic")
cws.add_argument("--domain", required=True, help="Domain slug to create the workstream under")
@ -767,6 +805,8 @@ def main() -> None:
cmd_ingest_sbom(args)
elif args.command == "fix-consistency":
cmd_fix_consistency(args)
elif args.command == "promote-intake":
cmd_promote_intake(args)
elif args.command == "create-workstream":
cmd_create_workstream(args)
elif args.command == "create-task":