refactor(agents): store each contract once, as a preloaded skill #83

Merged
crenshawdev merged 3 commits from cadence/74-contract-skills into main 2026-07-28 20:07:56 +00:00
crenshawdev commented 2026-07-28 20:07:10 +00:00 (Migrated from github.com)

Each agent's contract now lives in exactly one place, a skill preloaded via the skills: frontmatter field, instead of being restated in the agent body. Storage change, not a behaviour change.

What moved

Six contract skills, one per role rather than per file — cad-plan-checker and cad-plan-checker-high share one, which is what that pair always claimed to be. Five of six moved byte-identical; the seven agent files dropped from 1154-8786 B to 464-592 B and are now frontmatter plus a pointer.

The visibility question

Contract skills must not land in the user's / menu. The answer is user-invocable: false, which hides a skill from the slash-command menu while leaving it model-invocable — which is what skills: preloading requires, and why disable-model-invocation: true is unusable here. All six carry it; the 23 user-facing skills are untouched. Honest caveat recorded in the issue: the description still appears in the model's skill listing, and there is no documented flag that hides from both and stays preloadable.

The self-verify check

A missing or disabled skill is skipped silently, producing an agent running with no contract at all — a failure that reads as an agent ignoring its instructions. Check 6 asserts every skills: reference resolves, and additionally flags a resolved skill that sets disable-model-invocation: true (same end state, different cause). The tools lint now also scans preloaded contracts as agent prose; without that this refactor would have silently emptied its input.

Verified by breaking a reference on purpose: {"ok":false,..."missing-agent-skill"...}, clean again on revert.

cad-plan-checker-high

The @-include workaround is retired — no more runtime Read of another agent file. The file itself stays: it exists to carry effort: high, which is frozen in frontmatter and named by string in route-table.json's escalate_effort_variant. Retiring it depends on #63/#81.

One judgement call: the high variant carried real behaviour (stricter on borderline BLOCKER vs WARNING). Shipping behaviour in a rung file is exactly the failure this issue names, so it moved into the shared contract as a <rung> section and both files now state only which rung they are. That is the single non-verbatim adaptation in the change.

Verification

  • node --test cadence-core/bin/*.test.mjs → 745 pass, 0 fail (6 new)
  • npx tsc -p tsconfig.ci.json → exit 0
  • node cadence-core/bin/self-verify.mjsok:true, checks now include agent-skills

Also repointed METHOD.md's "Where each rule lives" table and two references/ citations at the contract skills; left alone they would have cited two-line stubs.

Closes #74

Each agent's contract now lives in exactly one place, a skill preloaded via the `skills:` frontmatter field, instead of being restated in the agent body. Storage change, not a behaviour change. ## What moved Six contract skills, one per **role** rather than per file — `cad-plan-checker` and `cad-plan-checker-high` share one, which is what that pair always claimed to be. Five of six moved byte-identical; the seven agent files dropped from 1154-8786 B to 464-592 B and are now frontmatter plus a pointer. ## The visibility question Contract skills must not land in the user's `/` menu. The answer is `user-invocable: false`, which hides a skill from the slash-command menu while leaving it model-invocable — which is what `skills:` preloading requires, and why `disable-model-invocation: true` is unusable here. All six carry it; the 23 user-facing skills are untouched. Honest caveat recorded in the issue: the description still appears in the model's skill listing, and there is no documented flag that hides from both and stays preloadable. ## The self-verify check A missing or disabled skill is skipped **silently**, producing an agent running with no contract at all — a failure that reads as an agent ignoring its instructions. Check 6 asserts every `skills:` reference resolves, and additionally flags a resolved skill that sets `disable-model-invocation: true` (same end state, different cause). The tools lint now also scans preloaded contracts as agent prose; without that this refactor would have silently emptied its input. Verified by breaking a reference on purpose: `{"ok":false,..."missing-agent-skill"...}`, clean again on revert. ## cad-plan-checker-high The `@`-include workaround is retired — no more runtime `Read` of another agent file. The file itself stays: it exists to carry `effort: high`, which is frozen in frontmatter and named by string in `route-table.json`'s `escalate_effort_variant`. Retiring it depends on #63/#81. One judgement call: the high variant carried real behaviour (stricter on borderline BLOCKER vs WARNING). Shipping behaviour in a rung file is exactly the failure this issue names, so it moved into the shared contract as a `<rung>` section and both files now state only which rung they are. That is the single non-verbatim adaptation in the change. ## Verification - `node --test cadence-core/bin/*.test.mjs` → 745 pass, 0 fail (6 new) - `npx tsc -p tsconfig.ci.json` → exit 0 - `node cadence-core/bin/self-verify.mjs` → `ok:true`, checks now include `agent-skills` Also repointed `METHOD.md`'s "Where each rule lives" table and two `references/` citations at the contract skills; left alone they would have cited two-line stubs. Closes #74
Sign in to join this conversation.
No description provided.