Task — engineering-spec@1

"Vendor the debugging-cycle pair + chain-coverage check so no ship-referenced skill can be missing from the payload"

doneTASK-SKILL-116
module skill · class product · priority p0 · created 2026-07-12 · shipped 2026-07-12
depends on none · blocks TASK-CUO-209

TASK-SKILL-116: Vendor debugging-cycle + chain-coverage check

§1 - Description

The ship workflow's 31-step skill_chain is the contract for what the payload must carry, but the vendored skill list in build.sh is a hand-maintained string that silently drifted from it. This task vendors the missing pair and adds a coverage check that fails the build whenever the two disagree.

Normative clauses:

  1. The vendored skill set in build.sh MUST include debugging-cycle-author and debugging-cycle-audit, copied into both cuo/skills/ and plugin/skills/ like the existing pairs.
  2. A script tools/install/check-chain-coverage.sh <payload-dir> MUST extract every skill referenced by the vendored workflow docs and verify each has a directory containing a SKILL.md in BOTH <payload>/cuo/skills/ and <payload>/plugin/skills/. Extraction is defined per doc kind: skill: <name> entries in <payload>/cuo/ship-tasks.md's skill_chain, and backtick-quoted tokens matching [a-z0-9]+(-[a-z0-9]+)*-(author|audit) in <payload>/plugin/commands/*.md (deterministic token grammar, not prose guessing). Any miss MUST exit 10 listing MISSING <skill> (referenced by <doc>) per line.
  3. Exemptions MUST be declared in tools/install/chain-allowlist.txt (one name + one # reason per line; initial entries: awh-gate, caf-gate). An allowlist entry exempts its name from BOTH rule kinds - MISSING (script-backed steps with no skill dir) and UNPAIRED (intentionally single skills, e.g. the four NFR skills once TASK-CUO-209 vendors them) - the reason string says which. An allowlisted name that nothing references and no payload dir matches MUST warn (stderr) so the allowlist cannot rot silently.
  4. The check MUST also enforce pair completeness: for every vendored <name>-author there MUST be a vendored <name>-audit and vice versa; non-allowlisted violations exit 10 as UNPAIRED <skill>.
  5. build.sh MUST run check-chain-coverage.sh against its own output as its final step and propagate a failure - a payload that under-covers its own workflow can no longer be produced. A reduced-profile payload (zero vendored skills - the documented doc-driven floor) is exempt: the check prints chain SKIP: reduced profile and exits 0; partial vendoring still fails. (Amended post-ship 2026-07-12: TASK-CUO-209 t07 surfaced that the check as first shipped broke the reduced floor.)
  6. The check MUST be pure read-only over the payload dir (no repo access needed), so TASK-IMP-068's CI gate and TASK-IMP-069's release job get it for free via the build.

§4 - Acceptance criteria

  1. The pair is vendored (§1 #1) - after build.sh, debugging-cycle-author/SKILL.md and debugging-cycle-audit/SKILL.md exist under both cuo/skills/ and plugin/skills/ in the payload.
  2. Chain extraction is real parsing, not a fixed list (§1 #2) - adding a fake skill: nonexistent-author line to the workflow doc in a scratch payload makes the check exit 10 naming nonexistent-author and the doc.
  3. A dropped pair fails the build (§1 #2, #5) - removing debugging-cycle-author debugging-cycle-audit from the vendored set makes build.sh itself exit non-zero with the two MISSING lines.
  4. Allowlist works both ways (§1 #3) - awh-gate/caf-gate produce no failure; an allowlist entry naming a skill no doc references produces a stderr warning and exit stays 0.
  5. Pair completeness (§1 #4) - a scratch payload with only repo-context-map-author present exits 10 with UNPAIRED repo-context-map-author.
  6. Read-only over the payload (§1 #6) - running the check from an empty cwd against a copied payload dir succeeds; the payload's mtimes/bytes are unchanged after a run.

§5 - Verification

# tools/install/tests/test_chain_coverage.sh
# Builds one scratch payload, then mutates copies of it per case.

t01_pair_vendored()              # AC 1
t02_parses_chain_not_list()      # AC 2
t03_dropped_pair_fails_build()   # AC 3  (patched build.sh in a temp checkout)
t04_allowlist_both_ways()        # AC 4
t05_unpaired_detected()          # AC 5
t06_readonly_check()             # AC 6  (sha256 of payload tree before/after)

§2 - Why this design

Deriving the vendored set FROM the chain automatically was considered and rejected: the payload legitimately carries skills no chain references yet (TASK-CUO-209 vendors the full SDP set), so the honest relation is "chain is a subset of payload", enforced by a checker, with an explicit allowlist for script-backed steps. Checking pairs (#4) in the same pass kills the sibling bug class (author without audit) at zero extra cost.

§3 - Contract

check-chain-coverage.sh <payload-dir>
  exit 0   chain covered, pairs complete (prints "chain OK: N referenced, M vendored, K allowlisted")
  exit 10  MISSING <skill> (referenced by <doc>) | UNPAIRED <skill>   (one per line)
  exit 2   payload dir or workflow doc unreadable

§6 - Implementation skeleton

Extraction: grep -Eo 'skill: *[a-z0-9-]+' <doc> | awk '{print $2}' | sort -u over the chain doc + command docs; set-compare against ls <payload>/cuo/skills/; suffix swap for pair check. build.sh gains the pair in its set string and the final check invocation.

§7 - Dependencies

None upstream. Blocks TASK-CUO-209 (the expanded vendored set lands behind this checker). Related TASK-IMP-068 (gate runs the build, so this check rides along) and TASK-SKILL-118 (parity of pair CONTENTS; this task guarantees pair PRESENCE).

§8 - Example payloads

$ bash tools/install/check-chain-coverage.sh dist/cyberos
MISSING debugging-cycle-author (referenced by cuo/ship-tasks.md)
MISSING debugging-cycle-audit (referenced by cuo/ship-tasks.md)
$ echo $?
10

§9 - Open questions

None blocking. When TASK-CUO-209 vendors additional workflow docs, clause #2's doc list grows by construction (it scans plugin/commands/*.md and the vendored cuo workflow file set, not a hardcoded pair).

§10 - Failure modes inventory

  1. Workflow doc renames its skill: key or format - extraction finds zero references; the check MUST treat "0 referenced skills" as exit 2 (structure changed under it), never as a pass.
  2. Skill dir exists but is empty - presence test is SKILL.md inside the dir, not the dir itself.
  3. Allowlist typo (awhgate) - the referenced awh-gate is then unmatched -> exit 10; typo cannot cause silent skips.
  4. Case drift (Debugging-Cycle-Author) - names are compared lowercase-exact; a mismatch is a MISSING, surfacing the drift.
  5. Payload built by an older build.sh (no check embedded) - CI (TASK-IMP-068) runs the current checkout's check against the fresh build, so stale build scripts cannot bypass it on main.

§11 - Implementation notes

Keep the output grep-stable (MISSING /UNPAIRED prefixes); TASK-CUO-209's expansion test keys on them. The allowlist file ships in the repo, not the payload - the check runs where the build runs.

End of TASK-SKILL-116.

Audit

TASK-SKILL-116 audit

§1 - Verdict summary

Audited for extraction determinism (the checker must parse, not guess), allowlist semantics, and forward-compatibility with TASK-CUO-209's expanded set. Two contract holes closed (prose-fuzzy extraction; allowlist covering only one rule kind). Traceability closes over t01-t06 in tools/install/tests/test_chain_coverage.sh.

§2 - Findings (all resolved)

ISS-001 command-doc extraction was prose-fuzzy

"Every skill named by plugin/commands/*.md" had no grammar - a checker cannot grep intent. Resolved: §1 #2 defines the deterministic backtick token grammar <name>-(author|audit) for command docs, skill: keys for the chain doc.

ISS-002 allowlist exempted MISSING but not UNPAIRED

TASK-CUO-209 vendors four intentionally single NFR skills; the pair rule would have failed the build the day they land. Resolved: §1 #3 allowlist entries exempt both rule kinds with a reason string; §1 #4 scopes UNPAIRED to non-allowlisted names.

ISS-003 zero-reference degeneracy

A workflow-doc format change yielding zero extracted skills would pass vacuously. Resolved: §10 #1 makes 0 references exit 2 (structure changed), never a pass.

ISS-004 empty-dir false positive

A skill directory without SKILL.md counted as present. Resolved: presence = SKILL.md inside the dir (§10 #2).

ISS-005 side-effect-free guarantee missing

CI reuse (TASK-IMP-068) requires read-only behavior; nothing said so. Resolved: §1 #6 + AC 6 hash-tree-before/after assertion.

ISS-006 build-integration failure path untested

Clause #5 (build fails on violation) had no test. Resolved: AC 3 / t03 run a patched checkout whose set drops the pair and assert build.sh itself fails.

§3 - Resolution

All six findings addressed as cited. The task now kills the bug class (hardcoded set drifting from the chain) rather than the single instance. Score = 10/10.

End of TASK-SKILL-116 audit.

§10 - Post-implementation gates (2026-07-12, ship run)

  • §10.4 coverage gate: PASS - t01-t06 green on fresh testing-phase rerun; TASK-IMP-068 suite green as regression. Report: docs/tasks/.workflow/TASK-SKILL-116/coverage-and-review.md.
  • TRACE-004 closure: PASS - every §1 clause's cited test passed (table in the bundled artefact).
  • §10.5 awh gate: N/A (no sealed goldenset for this tooling path - declared). §10.6 caf gate: N/A (no audit-profile); floor = bash -n clean + both suites green.
  • HITL gate 1: APPROVED by Stephen Cheng 2026-07-12. HITL gate 2: ACCEPTED same date via explicit operator pre-authorization at the review gate; gates stayed green.
  • Live proof: payload now vendors 22 skills; build prints chain OK: 24 referenced, 22 vendored, 2 allowlisted and the commit hook ran both checks green.

TASK-SKILL-116 shipped 2026-07-12.

§11 - Post-ship amendment (2026-07-12, surfaced by TASK-CUO-209 t07)

Field finding: the chain-coverage check failed REDUCED-profile builds (zero vendored skills = every chain reference "missing"), breaking the documented doc-driven floor. Amendment: zero-vendored payloads skip with chain SKIP: reduced profile (exit 0); partial vendoring still fails - the drift case the check exists for. §1 #5 amended; t07_reduced_profile_skips added to the suite.