Task — engineering-spec@1

"Ship run-state manifest (ship-manifest@1) - resumable 31-step chain + depends_on-aware queue selection"

doneTASK-CUO-206
module cuo · class product · priority p0 · created 2026-07-12 · shipped 2026-07-12
depends on none · blocks none

TASK-CUO-206: Ship run-state manifest

§1 - Description

Give /ship-tasks the same re-entrancy anchor its authoring sibling already has: a per-task manifest that records which of the 31 steps completed, over which artefacts, so a new session resumes instead of re-deriving - plus deterministic queue selection when no task id is given.

Normative clauses:

  1. A contract ship-manifest@1 MUST be defined at modules/skill/contracts/task/SHIP-MANIFEST.md with fields: manifest_version (const ship-manifest@1), task_id, task_sha256 (hash of the task spec file at run start - a later mismatch marks the whole manifest stale), workflow_version (from the workflow doc), started_at, updated_at, current_step (1..31), routed_back_count, steps[] - each {index, skill, status (pending|done|failed|skipped-conditional), artefact_path, artefact_sha256, verdict, completed_at} - and hitl ({gate: null|review_approval|final_acceptance, requested_at}).
  2. The ship workflow MUST write the manifest to docs/tasks/.workflow/<task-ID>.ship.json after EVERY completed, failed, or conditionally-skipped step, using two-phase atomic writes (.tmp.<nonce> then rename), mirroring the memory-protocol write discipline.
  3. On invocation for a task whose manifest exists with matching workflow_version, ship MUST resume at the first non-done step AFTER re-verifying every recorded artefact_sha256 against disk; a mismatch marks that step and all later steps stale (redo from the earliest stale step). A workflow_version mismatch MUST route to needs_human, never a silent mixed-version run.
  4. Invoked WITHOUT a task id, ship MUST select deterministically: among tasks at ready_to_implement whose depends_on are all done, order by priority (MUST before SHOULD before COULD), then created ascending, then id ascending; the selection and its reasoning line MUST be echoed to the operator before step 1 runs.
  5. Manifests MUST be gitignored via a scaffolded docs/tasks/.workflow/.gitignore (content: *.ship.json); task frontmatter and BACKLOG.md remain the only committed state. /install MUST scaffold the same ignore file in target repos.
  6. On the task reaching done (HITL gate 2 passed), ship MUST delete the manifest; on route-back to ready_to_implement, ship MUST keep it with routed_back_count incremented (the next run starts fresh at step 1 by §1 #3's staleness rule but retains the count and history).
  7. The workflow doc MUST gain a ## Resume semantics section and EXECUTION-DISCIPLINE.md a pointer to it; the plugin wrapper SKILL.md MUST mention resume-on-restart so agents look for the manifest before starting step 1.
  8. HITL gates MUST NOT be inferable from the manifest alone: resuming at a gate step re-requests the human approval; a recorded hitl.requested_at never substitutes for the approval itself.

§2 - Why this design

The manifest is a cache of proven work, never an authority: every resume re-hashes artefacts, and human gates always re-ask. That keeps the two-source-of-truth risk (manifest vs backlog) at zero - if the manifest lies or is deleted, the worst case is redoing work, the exact status quo. JSON-on-disk with atomic writes copies the pattern already proven by the authoring manifest; queue selection turns "pick the next eligible one" from prose into an algorithm agents apply identically across repos.

§3 - Contract

{
  "manifest_version": "ship-manifest@1",
  "task_id": "TASK-TEN-208",
  "task_sha256": "4c1e...",
  "workflow_version": "2.3.1",
  "started_at": "2026-07-12T10:00:00+07:00",
  "updated_at": "2026-07-12T11:42:10+07:00",
  "current_step": 11,
  "routed_back_count": 0,
  "steps": [
    {"index": 1, "skill": "repo-context-map-author", "status": "done",
     "artefact_path": "docs/tasks/.workflow/TASK-TEN-208.rcm.md",
     "artefact_sha256": "9f2c...", "verdict": "pass", "completed_at": "2026-07-12T10:12:00+07:00"},
    {"index": 3, "skill": "architecture-decision-record-author", "status": "skipped-conditional",
     "artefact_path": null, "artefact_sha256": null, "verdict": null, "completed_at": "2026-07-12T10:13:00+07:00"}
  ],
  "hitl": {"gate": null, "requested_at": null}
}

§4 - Acceptance criteria

  1. Schema is normative and validated (§1 #1) - SHIP-MANIFEST.md defines every field with types/enums; the §3 example and the fixtures validate against it programmatically.
  2. Write-after-every-step, atomically (§1 #2) - the workflow doc mandates the write points and the tmp+rename discipline; no step's completion is unrecorded.
  3. Resume skips proven work (§1 #3) - fixture: manifest with steps 1-10 done and artefacts intact -> resume plan says step 11; corrupting step 5's artefact makes the resume plan restart at 5 with 5..31 stale.
  4. Version mismatch halts (§1 #3) - manifest at 2.3.0 vs workflow 2.3.1 -> needs_human, no auto-run.
  5. Queue selection is total and deterministic (§1 #4) - fixture backlog (mixed statuses, unmet depends_on, tied priorities) yields one defined winner; re-running yields the same; the reasoning line matches the fixture expectation.
  6. Gitignore scaffolding (§1 #5) - the ignore file exists with *.ship.json; git status in a fixture repo shows no manifest after a simulated run; install.sh scaffolds it in a scratch target.
  7. Terminal handling (§1 #6) - done deletes the manifest; route-back keeps it with the incremented count (fixture pair).
  8. Human gates re-ask on resume (§1 #8) - resuming a manifest parked at step 19/31 (gates) produces a fresh approval request; the doc forbids treating requested_at as approval.

§5 - Verification

# modules/cuo/tests/test_ship_manifest.py
def test_schema_fields_and_example_validate():      # AC 1
def test_atomic_write_discipline_documented():      # AC 2  (workflow doc contains the write-point + tmp/rename clauses)
def test_resume_plan_intact_and_stale():            # AC 3  (pure function over fixture manifests + artefact dir)
def test_workflow_version_mismatch_needs_human():   # AC 4
def test_queue_selection_total_order():             # AC 5  (fixture task set -> expected id; idempotent)
def test_gitignore_scaffold():                      # AC 6
def test_done_deletes_routeback_keeps():            # AC 7
def test_hitl_reask_on_resume():                    # AC 8  (doc assertion + fixture plan marks gate pending)

(The resume planner and queue selector are specified in the workflow doc precisely enough to implement as small pure helpers under modules/cuo for testability; agents follow the same algorithm doc-driven in reduced profile.)

§6 - Implementation skeleton

SHIP-MANIFEST.md mirrors MANIFEST_SCHEMA.md's structure (field table, lifecycle, atomicity, staleness). Workflow doc: add manifest write-points to the step protocol preamble, the Resume semantics section (staleness rule, version rule, gate re-ask), and the queue algorithm where the doc currently says "next eligible task".

§7 - Dependencies

None hard. TASK-CUO-207's config later adds nothing here (manifest location is fixed). Interacts with TASK-CUO-205 only at the shared backlog-write skill, unchanged for ship. TASK-SKILL-118's coverage-gate rubric constants are read at their steps regardless of resume.

§8 - Example payloads

Resume echo line (operator-facing):

resume TASK-TEN-208: steps 1-10 verified (10 artefacts, hashes OK), continuing at step 11/31 (observability-injection-author). routed_back_count=0

§9 - Open questions

None blocking. Cross-repo parallel shipping (two agents, two different tasks, one repo) is naturally safe - one manifest per task; two agents on the SAME task is out of scope and remains an operator error the backlog's status cell already surfaces.

§10 - Failure modes inventory

  1. Crash between artefact write and manifest write - resume re-verifies hashes; the missing manifest entry means the step re-runs, idempotent by skill design.
  2. Manifest edited by hand to skip a gate - §1 #8: gates re-ask regardless of manifest content; the manifest cannot authorize anything.
  3. Stale manifest after task spec edits (task re-audited mid-flight) - covered by the schema's task_sha256 root field (§1 #1): mismatch at resume marks every step stale, forcing a clean re-run against the revised spec.
  4. .workflow dir deleted - clean restart from step 1; no correctness loss (cache semantics).
  5. Clock skew across sessions - ordering uses step indices, not timestamps; timestamps are informational only.

§11 - Implementation notes

Keep the manifest strictly derived (cache) - the words "record of truth" appear only next to task frontmatter in every doc touched. The queue reasoning line format is part of the contract (operators grep session logs for it).

End of TASK-CUO-206.

Audit

TASK-CUO-206 audit

§1 - Verdict summary

Audited hardest on the one danger a run-state manifest introduces: becoming a second source of truth. The revised spec keeps it strictly a cache (hash-verified on resume, gates always re-ask, deletable at zero correctness cost). Queue selection moved from prose to a total order. Traceability closes over the eight tests in modules/cuo/tests/test_ship_manifest.py (in new_files).

§2 - Findings (all resolved)

ISS-001 two-sources-of-truth risk

A trusted manifest could contradict the backlog. Resolved: cache-only doctrine (§2), resume re-verifies every artefact hash (§1 #3), HITL gates re-ask regardless of manifest content (§1 #8, AC 8), and deletion is always safe (§10 #4).

ISS-002 task spec edits mid-flight were invisible

Steps proven against version N of the spec would resume against version N+1. Resolved: task_sha256 root field in the schema (§1 #1) with all-stale semantics; §3 example updated; §10 #3 cites the field instead of deferring it.

ISS-003 mixed-workflow-version resume

2.3.0 manifest under a 2.3.1 workflow silently blends step semantics. Resolved: §1 #3 needs_human on version mismatch, AC 4.

ISS-004 queue selection had undefined ties

"Next eligible" without a total order produces different picks per agent. Resolved: §1 #4 priority -> created -> id ordering with an operator-visible reasoning line, AC 5 determinism assertion.

ISS-005 committed-or-ignored ambiguity

Manifests in git would churn every ship run; unstated either way invites both. Resolved: §1 #5 gitignore scaffold (repo + install.sh), AC 6.

ISS-006 terminal-state handling

Manifests of done tasks would accumulate; route-backs would lose history. Resolved: §1 #6 delete-on-done, keep-with-incremented-count on route-back, AC 7 fixture pair.

§3 - Resolution

All six findings addressed as cited. The task upgrades ship from restartable to resumable without moving any authority off task frontmatter. Score = 10/10.

End of TASK-CUO-206 audit.

§10 - Ship record (2026-07-12)

  • §10.1 Implementation: contract + helpers + tests + workflow v2.4.0 Resume semantics + scaffolds, commit f06ff65 (rebased onto PR #44 version-reset mid-flight; original hash 46911d8 superseded); phase artefacts at docs/tasks/.workflow/TASK-CUO-206/.
  • §10.2 Review: clause-by-clause pass (packet in phase-bundle); human verdict at gate 1: APPROVE + pre-authorize done (Stephen Cheng, in-chat).
  • §10.3 Testing: 8/8 AC tests, 100.0% statement coverage on modules/cuo/cuo/ship_manifest.py (raised from 77.3% by covering validate error branches + write_atomic failure cleanup), 5/5 cyberos-install suites, git check-ignore proof. Gate 2 recorded per pre-authorization.
  • §10.4 Field finding folded back: TASK-CUO-209 t08 temporal-scope guard amended to durable workflows_vendored_intact (TASK-CUO-209 §1 #8, AC 8, audit §11).

Verdict unchanged: PASS, Score = 10/10.