Status: Accepted
Date: 2026-10-04
Deciders: Stijn Dejongh (owner). Operator triage ruling on #5298 (2026-09-28) and the waiver-shape ruling in the Mission brief (2026-10-04).
Technical Story: #5298, Mission friction-remediation-01M43DRV (WP02). Parent #2017.
Context and Problem Statement
The software-dev Mission type declares paths.deliverables: "contracts/" in packs/built-in/missions/software-dev/mission.yaml. Strict spec-kitty accept turns a missing contracts/ into a blocking path-convention violation (acceptance/summary_core.py::evaluate_path_conventions).
Many legitimate software-dev Missions define no interfaces: test remediation, refactors and docs-adjacent fixes. The operator ruled that roughly a third of Missions hit this. Their only escapes were:
accept --lenient, which downgrades every path convention, not just this one;- an empty
contracts/placeholder, which #1892 called unintended.
The same file lists contracts/ under artifacts.optional. That is a separate question (is the artifact expected to exist?) and does not settle whether the deliverable convention applies.
Decision
A Mission declares that it defines no interface contracts in its meta.json:
{
"contracts": "none",
"contracts_rationale": "Test-drift remediation; this Mission defines no interfaces."
}
- One authority.
specify_cli.core.paths.read_contracts_waiver_from_metais the only reader. It is a thin adapter overload_meta_fail_closed, likeread_retention_from_meta. There is no spec-frontmatter or plan-frontmatter source, so no second, disagreeing declaration can exist. - Fail-closed. Each of the following is NOT a waiver: any
contractsvalue other than the exact string"none"(includingnull, booleans and other casing), and a missing, blank or non-stringcontracts_rationale. Such a declaration keeps the requirement in force and produces a warning naming the problem. A corruptmeta.jsonraisesMissionMetaReadError, as every fail-closed meta read does. - Where it applies.
evaluate_path_conventionsremains the only caller ofvalidate_mission_paths(gated bytest_lifted_root_validate_mission_paths_single_caller.py). It passes the waived token down aswaived_artifact_tokens. The validator skips a declared path only when that path is a mission-artifact path. A build or repository path (src/,tests/,docs/) can never be waived. - Auditable. An honoured waiver's rationale is surfaced in the accept warnings ("contracts/ requirement waived by meta.json (contracts: none): …"). A malformed waiver's warning is attached to the blocking violation text.
- The field is consulted only when
contractsis both a declared path convention and a declared mission artifact (the case the validator can waive), so accept never claims a waiver it did not apply.
The fields are recorded in MissionMetaOptional (src/specify_cli/mission_metadata.py). The software-dev plan prompt (packs/built-in/missions/mission-steps/software-dev/plan/prompt.md) tells the planning agent to write the two fields when the Mission defines no interfaces. No CLI flag mints them yet.
Considered Options
- Treat
deliverablesas satisfied when the artifact is declared optional. Rejected by the operator ruling. It silently makescontracts/optional for every software-dev Mission, with no explicit, auditable statement that this Mission defines no interfaces. - Per-Mission declaration in plan or spec frontmatter. Rejected. A planning-document field would be a second source of truth beside
meta.json, which already carries Mission-level policy flags (retain_branches,commit_to_target). - Per-Mission declaration in
meta.json(chosen). Explicit and auditable, read by one helper, and consistent with the existing flat policy fields.
Consequences
- A no-interface Mission passes strict
acceptwithout--lenientand without a placeholder directory. contracts/stays required by default; an unwaived or malformed Mission still blocks.- A waived
contracts/is also dropped from the "Optional artifacts missing" warning: the Mission declared it absent on purpose, and the waiver note already says so. - Follow-up candidate, not in scope: a
mission create/setup-planflag to mint the waiver.