"Bring the six thin vendored pairs to full contract parity (RUBRIC, PIPELINE, INVARIANTS, envelopes, references, acceptance)"
TASK-SKILL-118: Thin-pair contract parity
§1 - Description
Six pairs at the heart of /ship-tasks run on a SKILL.md and trigger tests alone: no rubric an auditor can score against, no pipeline, no envelopes, no invariants. The audits therefore re-derive their bar from prose every run. This task raises all six to the file-level contract the four full pairs already have.
Normative clauses:
- Each of the six author skills (
repo-context-map,edge-case-matrix,mock-contract-test,observability-injection,backlog-state-update,coverage-gate) MUST gain:PIPELINE.md(phased steps incl. HALT points),INVARIANTS.md,envelopes/input.json+envelopes/output.json,references/FAILURE_MODES.md, andacceptance/README.md- matching the file classes oftask-author. - Each of the six audit skills MUST gain:
RUBRIC.md(versioned<name>_rubric@1.0, rule families with stable rule IDs, /10 scoring, 10/10 pass bar),AUDIT_LOOP.md,REPORT_FORMAT.md,envelopes/input.json+envelopes/output.json, andacceptance/README.md- matching the file classes oftask-audit. - The rubrics MUST encode exactly the gates already normative in each pair's SKILL.md prose, with rule IDs: edge-case-matrix (>= 1 row per category; SECURITY rows point at real test paths; DEGRADATION rows carry detection + recovery; total_rows >= 8 for MUST tasks), observability-injection (>= 1 log point per state transition, >= 1 span per external IO, >= 1 counter per error branch, branch_coverage >= 80%, redaction policy when PII in scope), coverage-gate (tests_failed == 0; files_below_90pct empty; ecm_rows_uncovered empty; raw terminal present + non-truncated; §1-clause test closure), backlog-state-update (status in the 10-value enum; line_number resolves; old_line byte-match; evidence rows resolve; mutation_kind == status-cell-only), mock-contract-test (>= 1 request/response pair; error_modes cover every SECURITY/DEGRADATION matrix row; swap_target is a real symbol; sunset criterion observable; contract tests pass against the mock), repo-context-map (three baseline patterns present; pinned_in references resolve; schemas present when task declares migrations; module-placement warning null or escalated).
- Numeric gates MUST be expressed as named constants at the top of each RUBRIC.md, with the coverage threshold noted as overridable by
.cyberos/config.yamlcoverage_thresholdonce TASK-CUO-207 lands (default 90 preserved). - Artefact schemas MUST stay at @1 - additive documentation only; no change to any emitted artefact shape, so already-shipped tasks' artefacts remain valid.
- A script
tools/install/check-pair-parity.sh <skills-dir>MUST verify, for every author/audit pair present: authors carry the #1 file classes, audits carry the #2 file classes; missing files exit 10 asPARITY <skill>: missing <file>.build.shMUST run it over the vendored set after the chain-coverage check (TASK-SKILL-116 ordering). - Each rewritten SKILL.md MUST keep its existing trigger description contract (TASK-SKILL-111/112/113 conventions) - descriptions, trigger tests, and frontmatter shape unchanged except for pointers to the new files.
§2 - Why this design
Parity is defined by pointing at the four existing full pairs rather than inventing a new standard - the repo already voted. Encoding prose gates as rubric rule IDs is what makes audit verdicts reproducible across sessions and agents (the auditor cites SPK-style IDs instead of paraphrasing prose). The parity checker turns the definition of done into a machine gate, and covers TASK-SKILL-117's new pair and TASK-CUO-209's expansion at no extra cost.
§3 - Contract
Rubric header convention (all six):
# <name>_rubric@1.0
constants: TOTAL_ROWS_MIN=8 (MUST tasks) | BRANCH_COVERAGE_MIN=80 | COVERAGE_THRESHOLD=90 (config-overridable, TASK-CUO-207)
families: <ABBR>-STRUCT | <ABBR>-GATE | <ABBR>-TRACE (per-pair families as needed)
verdict: pass requires 10/10; any family failure -> fail; ambiguity -> needs_human
check-pair-parity.sh <skills-dir>: exit 0 | exit 10 with PARITY lines | exit 2 unreadable dir.
§4 - Acceptance criteria
- All 12 skill dirs carry their file classes (§1 #1, #2) -
check-pair-parity.sh modules/skillreports zero PARITY lines for the six pairs (and the four full pairs still pass). - Every prose gate has a rule ID (§1 #3) - for each of the six audits, every numeric/enum gate quoted from its SKILL.md appears in RUBRIC.md with a stable rule ID; spot-list in each RUBRIC.md's changelog block maps prose -> rule.
- Constants are declared once (§1 #4) - the numeric gates appear as named constants; coverage rubric names the TASK-CUO-207 override hook.
- No artefact shape change (§1 #5) - each pair's SKILL.md artefact section is diff-identical for the artefact fields before/after (documentation-only diff).
- Parity checker catches a miss (§1 #6) - deleting one RUBRIC.md in a scratch copy exits 10 naming the pair and file;
build.shpropagates the failure. - Trigger contracts untouched (§1 #7) - all six author trigger-test suites pass unmodified (descriptions and trigger tests byte-identical except added file pointers).
§5 - Verification
# tools/install/tests/test_pair_parity.sh
t01_all_pairs_parity_clean() # AC 1
t02_prose_gate_rule_ids() # AC 2 (grep each rubric for its constants + rule-ID table)
t03_constants_block() # AC 3
t04_artefact_sections_stable() # AC 4 (git diff scoped to artefact-spec heading ranges is empty)
t05_checker_catches_missing() # AC 5
t06_trigger_tests_unchanged() # AC 6 (sha256 of the six TRIGGER_TESTS.md before/after)
§6 - Implementation skeleton
Full file matrix = 72 files across the 12 skill dirs (6 per author, 6 per audit, per §1 #1-#2); frontmatter new_files lists the rubric/pipeline spine plus tooling, and the parity checker is the completeness authority (AC 1 gates the whole matrix, so nothing hides behind the abbreviated list). Per pair: derive PIPELINE.md phases from the SKILL.md's own step prose; INVARIANTS.md lifts MUST/MUST NOT lines; envelopes copy the shape of the nearest full pair (backlog-state-update mirrors task-audit's, coverage-gate mirrors implementation-plan-audit's); RUBRIC.md per §3 header + one family table per gate group. Order of work: backlog-state-update and coverage-gate first (they gate every ship run), then edge-case-matrix, observability-injection, mock-contract-test, repo-context-map.
§7 - Dependencies
None upstream. TASK-CUO-207 later flips COVERAGE_THRESHOLD to config-driven (the rubric already names the hook). TASK-CUO-209 vendors the enriched pairs as-is. TASK-CUO-205 will bump backlog-state-update to @2 (insert-row) - land THIS task first so the @2 change edits a full contract, not a thin one.
§8 - Example payloads
$ bash tools/install/check-pair-parity.sh modules/skill
PARITY coverage-gate-audit: missing RUBRIC.md
PARITY coverage-gate-audit: missing REPORT_FORMAT.md
$ echo $?
10
§9 - Open questions
None blocking. Shared references (HITL_PROTOCOL.md, ANTI_FABRICATION.md, UNTRUSTED_CONTENT.md) stay per-skill copies for now, matching the existing full pairs; deduplication into a shared dir is a separate refactor candidate, deliberately not smuggled in here.
§10 - Failure modes inventory
- Rubric invents a stricter bar than the SKILL.md prose - AC 2's prose->rule mapping table makes each rule cite its prose source; unsourced rules are review findings.
- Envelope drift from actual artefacts - envelopes are copied from the nearest full pair then field-checked against each SKILL.md artefact section (AC 4 protects the section).
- Parity checker too rigid for legitimately different skills (backlog-state-update has no references/ need) - the checker checks FILE CLASSES per side as listed in §1; the list IS the policy, adjustable only by editing this task's clauses.
- Vendored copies go stale - build.sh copies from modules/skill on every build; TASK-IMP-068's gate rebuilds on every touch of modules/skill/**.
- Six-pair scope creep into content rewrites - clause #7 pins descriptions and trigger tests byte-stable; the task adds files, it does not re-author skills.
§11 - Implementation notes
Keep rule-ID prefixes distinct per pair (RCM-, ECM-, MCT-, OBS-, BSU-, COV-) so audit reports stay greppable across a ship run's artefact trail. The parity checker's file-class lists live at the top of the script as two arrays - one place to evolve the convention.
Post-ship amendment (2026-07-12, TASK-IMP-071 leg): AC 4's t04 guard false-fired on three legitimate mid-flight citation mutations (the point-in-time-guard class). Amended to an at-rest guard: dirty-worktree files warn and defer to the committed state; CI always checks clean trees, so the additive-only guarantee holds where it matters.
End of TASK-SKILL-118.
Audit
TASK-SKILL-118 audit
§1 - Verdict summary
Audited with special attention to scope integrity (six pairs, additive-only) and to the honesty of an abbreviated new_files list against a 72-file real matrix. The decisive property - rubrics ENCODE existing prose gates rather than inventing policy - is enforced by the prose->rule mapping requirement. Traceability closes over t01-t06 in tools/install/tests/test_pair_parity.sh.
§2 - Findings (all resolved)
ISS-001 rubrics could silently raise the bar
Nothing stopped a rubric from being stricter than the SKILL.md prose it encodes. Resolved: AC 2 requires a prose->rule mapping table per rubric; unsourced rules become review findings (§10 #1).
ISS-002 new_files understated the change surface
13 listed files vs 72 real ones reads as evasion. Resolved: §6 states the full matrix explicitly and names the parity checker as the completeness authority gating AC 1 - the abbreviated list cannot hide a missing file.
ISS-003 coverage threshold hardcode
Encoding 90 as a literal would collide with TASK-CUO-207's config override a wave later. Resolved: §1 #4 named constants + the override hook cited in the rubric header contract (§3).
ISS-004 artefact stability unguarded
"Additive only" needed a check, not a promise. Resolved: AC 4 diff-scope guard over each pair's artefact-spec section.
ISS-005 trigger contracts at risk
Rewriting SKILL.md files invites description drift that would break TASK-SKILL-111/112 trigger tests. Resolved: §1 #7 byte-stability rule + AC 6 sha256 assertion on the six TRIGGER_TESTS.md.
ISS-006 checker rigidity
Skills legitimately differ (backlog-state-update needs no references/ tree in the same shape). Resolved: the file-class arrays at the top of the checker ARE the policy, changeable only by editing this task's clauses (§10 #3) - no per-skill exceptions smuggled in code.
§3 - Resolution
All six findings addressed as cited. Sequencing note (land before TASK-CUO-205's @2 bump) is recorded on both tasks. Score = 10/10.
End of TASK-SKILL-118 audit.
§4 - Ship record (2026-07-12)
- Implementation: 86 files across 8 pairs (six §1 pairs + debugging-cycle full-raise + spike acceptance READMEs), check-pair-parity.sh + build.sh hookup + test_pair_parity.sh; commits 247f021, e63f0fd. Phase artefacts: docs/tasks/.workflow/TASK-SKILL-118/.
- Recorded deviation (newest wins): BSU rubric versioned @2.0 - TASK-CUO-205 landed first; its §7 migration path followed. ISS-003's override hook present in every rubric header.
- Review: human verdict at gate 1 APPROVE + pre-authorize done (Stephen Cheng, in-chat).
- Testing: t01-t06 6/6, all 6 cyberos-install suites, full-profile build green with parity gate live (52 skills, plugin 1.09 MB < 2 MB). Gate 2 recorded per pre-authorization.
- Field findings folded back: t04B repo-VERSION dependence fixed (TASK-IMP-070 audit note); first manifest-tracked ship run (TASK-CUO-206 dogfood) - manifest created, stepped, finalized-deleted at done.
Verdict unchanged: PASS, Score = 10/10.
- 2026-07-12: t04 amended to at-rest semantics (warn+defer on dirty worktree) after three mid-flight false-fires. Verdict unchanged: PASS, Score = 10/10.