Read-only verification that cross-checks intent-tree, intent-compass, and packets (+ the export draft) and reports contradictions, coverage gaps, boundary inconsistencies, and normative violations with severity. Fixes remain proposals.
Instrucciones de origen · Vista previa de solo lectura
name
intent-validate
description
Read-only verification that cross-checks intent-tree, intent-compass, and packets (+ the export draft) and reports contradictions, coverage gaps, boundary inconsistencies, and normative violations with severity. Fixes remain proposals.
allowed-tools
Read, Glob, Grep
argument-hint
none
intent-validate Skill
Core Mission
Success Criteria:
intent-tree, intent-compass, and packets (+ the export draft) are cross-checked, applying all checks in the check catalog (the set of checks, their categories, and their severities are authoritatively defined by the table in rules/validate-checks.md)
Findings are classified by severity (must-fix / recommended / info), and every item carries its check ID (the ID column of the table in rules/validate-checks.md), its evidence (file and the relevant statement), and a fix proposal (the skill to re-run or the fix direction)
Unverified targets (missing / unfilled deliverables, plus the IDs of skipped checks) are stated explicitly together with the reason
Packets are read statically from the four PBR perspectives (user / operations / test / maintenance), confirming read-only whether each perspective's breakdown conditions are documented
No file has been created, changed, or deleted at all (read-only, one-way reporting)
Execution Steps
Step 1: Confirm the prerequisites
If .intent/ is absent, guide the user through the intent-planner setup steps (running npx intent-planner) and finish.
Partial absence of intent-tree.md / intent-compass.md / .intent/packets/ is non-blocking: do not stop; run the checks within the verifiable scope and report the missing deliverables as unverified targets (packets count as missing when .intent/packets/ is absent or active/ is empty; in that case skip the packet-dependent checks).
Step 2: Read the deliverables
Read .intent/intent-tree.md, .intent/intent-compass.md, .intent/packets/index.md and .intent/packets/plan.md, and the packet files subject to the checks (for cross-packet checks, read all files under active/; do not read archive/), plus .intent/cc-sdd/<slug>/*.md (the per-packet export drafts, if present) and the inherited issue directory's discovery/<slug>-<rand>/mode.md → else the single .intent/mode.local.md (legacy) → else .intent/mode.md for the mode state (the CONTRACT.md read fallback contract). For symbol-existence checks, a symbol counts as existing when reachable in either the split store .intent/compass/ (when present) or the legacy body (dual-path; DR133). Intent-tree case records (機能追記/機能撤去/履歴/再起案) count as existing when reachable in either the split store .intent/tree/ (when present) or the legacy form at the body tail (dual-path; the skeleton L0–L4 is in the body; tree-normalize / DR133).
You may partially load compass by domain scope (opt-in; federated-governance / INV101): when the target domain is determined by the domain argument or the case context, read and apply rules/domain-scope.md. Do not full-load compass; pull only "the case's domain tag + always" with grep + inline tags (extending the INV47 pull discipline into the verification layer). However, axes that inherently need a full scan (compass-rule-decay / stale-questions / stale-assumptions / cross-cutting matching axes whose population is all always symbols) target all symbols, not narrowed by domain scope (do not weaken detection; B-fed5). For a legacy scaffold with no determinable domain and no tags, fall back to the legacy full read (backward-compatible; O3). Do not change the check catalog's axes, sections, severities, or temperature (only the range of symbols each axis loads changes).
Observe the downstream spec deliverables (.kiro/specs/<feature>/*.md) read-only, solely as the counterpart for draft-content-dropped (Step 3.17) — read them when present; skip that check when absent. Never rewrite an external tool's deliverables (INV1, the add-on principle). The - feature: appended line used to identify the counterpart lives in .intent/export-log/<packet-slug>.md.
If both mode.local.md and mode.md are absent, continue with the standard default and announce it (do not stop).
Step 3: Apply the check catalog
Read rules/validate-checks.md and apply all checks in the check catalog (the set of checks, their categories, and their severities are authoritatively defined by the table in rules/validate-checks.md).
Severity classification (including the must-fix / recommended decision for the L3 mismatch) follows the criteria in the rules.
The target of the boundary checks is the directory of the packet on the latest row of .intent/export-log.md (identification is authoritatively the exact match of the ## Source Packet heading in the directory's requirements.md). Drafts of past packets coexist by design and are not treated as violations. If export-log is absent or uninterpretable, fall back to the drafts' Source Packet headings (when multiple directories exist, present them as candidates without asserting one) and report that fact.
Step 3.5: Statically confirm the four PBR perspectives (read-only; search for breakdown conditions)
Read the packets (under active/, plus the export drafts if present) from the four perspectives below, and statically confirm whether each perspective's breakdown conditions are documented. If the documentation is missing, list it as a finding for that perspective in the severity-grouped list (perform no automatic fix or interactive confirmation).
User perspective: whether the breakdown conditions of the behavior for the user (inputs / situations under which the expected behavior collapses) are documented.
Operations perspective: whether a fail-safe design (behaviors such as degradation, rejection, retry) against faults, timeouts, and malicious input is documented.
Test perspective: whether test cases based on equivalence partitioning and boundary-value analysis can be created. If they cannot, report it as a missing boundary condition.
Maintenance perspective: whether the packet holds How internally (fixing a ceiling) and thereby obstructs the free design of the implementing agent.
For every perspective, do not stop at confirming the happy path; actively search for "the conditions under which this design breaks down (abnormal cases, high load, invalid input)" (confirmation-bias mitigation). When no breakdown condition is found, treat it as "the breakdown-condition documentation is missing" rather than "unexplored".
This per-perspective confirmation is a static check item; introduce no new interaction loop, state machine, or interactive-confirmation tool. If an interactive per-perspective audit is needed, note in Open Questions / fix proposals that it is delegated to the existing reviewer subagent practice (kiro-review, etc.).
Step 3.6: Name suspected coinages (coinage-suspect; read-only; semantic)
First look only for project-local term-drift artifacts: check for .term-drift/ at the target repository root or skills/term-drift/SKILL.md under the selected agent's configuration directory inside that repository. If either exists, do not run the rest of Step 3.6. Presence is not evidence that term-drift is usable: do not read detect.md, run term-drift rules, judge term meaning, or determine version / hash / required-file-set / agent-skill compatibility. Continue the other intent checks and give one short instruction: “In the repository, run npx intent-planner . --agent <selected-agent> --with-term-drift --dry-run to inspect health through the existing installer; proceed to the selected agent's dedicated term-drift skill only when it reports ready.” intent-validate never launches that command itself (allowed-tools stays Read, Glob, Grep).
Only when no placement exists, continue with the legacy detection below: emit no new term-drift guidance on this branch and preserve the existing scope and temperature of coinage-suspect.
Read the mother-set: read .intent/glossary.md (the lightweight canonical-vocabulary ledger) read-only, taking the "Canonical term" column and its "Aliases & synonyms" as the mother-set. When rows carry a term status (approved/provisional/rejected) and a rewording example, read them too (optional fields; the ledger owns the schema). If .intent/glossary.md is absent, treat the mother-set as empty and do not fire this detection at all (the existing validate output is unchanged; backward-compatible).
Judge (semantic, not a mechanical check): among the terms appearing in the intent artifacts (intent-tree / intent-compass / packets, plus the export drafts if present), name as a "suspected coinage" any term found nowhere in the mother-set (canonical terms, aliases, synonyms). Judge by a semantic reading (including grasping synonyms / spelling variants in the LLM context), not by a mechanical regex match. Do not push this onto scripts/intent-check.mjs in any way.
Reflect the ledger status in how the mother-set is read (when present): a term whose status is approved stays silent as a team common term. A term whose status is explicitly provisional may, even though registered, be raised as a candidate "term still being used without team agreement yet" (word it differently from the suspected coinage of an unregistered term, and attach a suggestion to consider promotion to approved or a rewording). A term whose status is is excluded from the silence set; when it appears in artifacts, name it as "a suspected reinvention of a once-rejected term", attaching the rejecting ledger row (and its rewording example if present). (backward-compatible; an implementation whose behavior changes on an older 3-column ledger is wrong). Registering a term or promoting its status is a separate action approved by a human one term at a time; this skill never rewrites the ledger.
Step 3.7: Name groundless conclusions (groundless-conclusion; read-only; semantic)
Background: an Intent is often given as only "a conclusion a human summarized from events", with the rationale that produced it (reasons, constraints, premises, trade-offs) dropped. A conclusion can be re-derived from its rationale but a rationale cannot be re-derived from its conclusion (the asymmetry). A conclusion whose rationale is gone cannot be corrected even when a contradicting fact arrives (loss of correctability). This detection names, read-only, the places where a conclusion stands alone without traceable rationale, and checks correctability (A29; C18; INV36).
Judge (semantic, not a mechanical check): among the conclusions appearing in the intent artifacts (intents in intent-tree, Invariants/Decision Rules in intent-compass, decisions in packets, plus the export drafts if present), name as a "suspected groundless conclusion" any conclusion whose rationale (reasons, constraints, premises, trade-offs) is not traceable in the artifacts. Judge by a semantic reading (including grasping whether the rationale lives elsewhere — a parent intent, or the Why/Consequences of a related Decision Rule), not by scripts/intent-check.mjs, the presence/absence of a required field, or a mechanical regex match (INV2/A1).
Exclusions: do not name the following as groundless conclusions.
Self-evident intents (judgments so obvious that stating a rationale is unnecessary).
Conclusions that reference a rationale already stated (the rationale lives in the same artifact, a parent intent, or a related Decision Rule).
Cases where another intent / Invariant / Decision Rule supplies the rationale.
Attach the correctability lens: for each named conclusion, attach the observation "when a fact contradicting this conclusion arrives, can it be re-evaluated from the rationale?" (rationale not traceable = cannot re-evaluate = risk of being uncorrectable).
Tone: stay a candidate suggestion and never assert (false-positive-tolerant). Raise it as a "the rationale might not be traceable" candidate; do not take the judgment away from the user. When in doubt, do not raise it.
Silence: when there is not a single suspected groundless conclusion, do not fire any output for this detection at all.
Scope: keep the target of this detection to the artifacts in scope for this validate run (new / changed artifacts); do not indiscriminately re-scan the whole tree/compass retroactively (to avoid noise; a retroactive sweep is an opt-in separate path).
Supplying rationale is presentation only and rewrites nothing: keep any update proposal that attaches rationale to a read-only report, and do not automatically rewrite the canonical artifacts (intent-tree / intent-compass / packets). If the AI fabricates a rationale to retro-justify a conclusion, it worsens the very uncorrectability (brittle memory) we want to prevent. This skill only names "the rationale is not traceable"; writing the rationale is a taken only after a human approves it (A7/INV5; INV36).
Step 3.8: Name unverified hypotheses (unverified-hypothesis; read-only; semantic)
Background: at the decision points of cutting a packet / filling a decision slot, the agent tries to pass through on a tentative conviction (a hypothesis) such as "just copy the map and change the output destination". intent-packets' before-the-fact probe (decision-probe.md) adjudicates this at the moment of decision, but in the spots where the probe does not fire (an existing packet with no probe introduced, a missed load-bearing judgement, a conviction that slipped past the probe), a "hypothesis finalized without evidence backing it" remains in the artifacts. This detection picks it up after the fact before export. Keep its detection axis separate from the preceding groundless-conclusion (a conclusion with no traceable rationale = the preservation axis); this axis = a hypothesis with no evidence backing it = the verification axis (A30; C19; INV37; DR61).
Judge (semantic, not a mechanical check): among the hypotheses (tentative convictions) left in the artifacts (a packet's confident claims, the Human-fixed values in Decisions, a compass Decision/Invariant, plus the export drafts if present), name as an "unverified hypothesis" any whose verifying/refuting evidence is not traceable from .intent/ (the compass Invariants/Decision Rules, glossary, past deltas, related packets = the evidence pool). Judge by a semantic reading, not by scripts/intent-check.mjs, the presence/absence of a required field, or a mechanical regex match (INV2/A1).
Refutation first: so as not to corroborate the hypothesis with cherry-picked evidence and lend authority to a wrong decision, put the evidence contradicting the conviction (refutation/unverified) first in the findings. By semantic judgement, exclude convictions whose evidence actually exists and is backed in .intent/, so as not to false-flag them (being warn-only, the harm of a false positive is small, but keep the volume down).
Keep the axis separate from A29: report groundless-conclusion (a conclusion's missing rationale) and this detection (a hypothesis's missing evidence) as separate checks and do not mix the findings (even when both axes apply to the same spot it is not a duplicate detection — the matching surface differs: "preserving rationale" vs. "searching for evidence"). The evidence source may be shared (the rationale A29 preserves feeds into this check's evidence pool — a synergy).
Tone: stay a candidate suggestion and never assert (false-positive-tolerant). Raise it as an "the evidence backing might not be traceable" candidate; do not take the judgment away from the user. When in doubt, do not raise it.
Step 3.9: Name dangling references (dangling-reference; read-only; LLM-context)
Background: the canonical (compass) is bound by numbered cross-references (Anti-direction N / INV N / DR N), and the pull discipline (do not read the full text, pull only the relevant Invariant/Anti-direction) tacitly assumes the target exists. When retirement/merge/deletion moves a block, the target can disappear and become a dangling reference, yet validate had no axis to catch it (in canonical-slimming a compass block move caused a dangling reference and passed undetected = INV42, C20, A35, DR67). This check catches it before export. Equivalent to Doorstop's suspect-link.
Judge (LLM-context reading; not a mechanical check): among the numbered cross-references appearing in the target artifacts (compass's Anti-direction N / INV N / DR N), name any whose target does not exist within the artifacts as a "suspected dangling reference". The judgement is a semantic reading in which the LLM reads the artifacts and confirms the target's existence; it is never pushed onto scripts/intent-check.mjs, grep, or a mechanical regex match (INV2/A1; matching the existing three axes in kind).
Narrow the target: keep to compass's numbered references (Anti-direction N / INV N / DR N). [[memory-slug]] (memory is in a separate repo, so its existence cannot be checked from validate, and false positives are many) and a packet's parent_intents references are out of scope. ID re-numbering (an ID swapped for a different item) is out of scope; keep to a pure missing-existence check.
Keep the axis separate from the existing three: report coinage-suspect (a coinage not in the ledger) / groundless-conclusion (a conclusion's missing rationale) / unverified-hypothesis (a hypothesis's missing evidence) and this check (a missing referent's existence) as separate checks and do not mix the findings (even when several axes apply to the same spot it is not a duplicate detection — the matching surface differs).
Temperature: it stays a candidate suggestion that never asserts (false-positive-tolerant). Raise it as a candidate "the target may be dangling" and do not usurp the user's judgement. When in doubt, do not raise it.
Silence: when there is not a single suspected dangling reference, fire no output about this check. When there is not a single numbered reference in the target, or compass is absent, skip this check and continue the others (do not error).
: keep the target to this validate run's check scope (new/changed artifacts); do not indiscriminately re-scan the whole compass retroactively (a full check right after retirement is an opt-in separate path).
Step 3.10: Name Invariants with no verification oracle attached (invariant-oracle-missing; read-only; LLM-context)
Background: an Invariant (a promise that must not be broken) in the compass is written as prose only, with no "yardstick by which a breach is detectable" (verification oracle) beside it, so there is no means to check whether it is upheld right next to the promise. Attaching to each Invariant a one-line means of confirmation ("if you break this, this is how you find out" — not limited to a machine test; a procedure is fine), and explicitly marking "no yardstick" for an unattachable principled Invariant, turns the promise from "aspirational prose" into "something checkable, whose breach is detectable". This check catches, before export, Invariants where that attachment is missing (neither an oracle nor a "no yardstick" note) (the intent-planner version of Tessl's "tests are intent too"; C2/C8; A38; DR72; INV48).
Judge (LLM-context reading; not a mechanical check): read each Invariant in the compass (the INV N items under ### <feature>), confirm semantically whether a verification oracle is attached or "no yardstick (principled constraint)" is explicitly noted, and name any that has neither as a "suspected missing oracle". The judgement is the LLM reading the compass; it is never pushed onto scripts/intent-check.mjs, grep, or a mechanical regex match (INV2/A1; matching the existing axes in kind = INV48 "the judgement of an oracle's presence/soundness is LLM-semantic").
Also weigh the oracle's soundness (without asserting): even when an oracle is present, one that reads as not making a breach detectable (tautological, unobservable, aimed at the wrong target) may be surfaced as a candidate. But the soundness judgement is false-positive-tolerant; when in doubt, do not raise it.
Keep the axis separate from the existing axes: report coinage-suspect (a coinage not in the ledger) / groundless-conclusion (a conclusion's missing rationale) / unverified-hypothesis (a hypothesis's missing evidence) / dangling-reference (a missing referent's existence) and this check (an Invariant's missing verification oracle) as separate checks and do not mix the findings (the matching surface differs).
Temperature: it stays a candidate suggestion that never asserts (false-positive-tolerant). Raise it as a candidate "a yardstick may not be attached" and do not usurp the user's judgement.
Silence: when there is not a single suspected missing oracle (every Invariant has an oracle or a "no yardstick" note), fire no output about this check. When compass is absent or there is not a single Invariant, skip this check and continue the others (do not error).
Scope: keep the target to this validate run's check scope (new/changed Invariants); do not indiscriminately re-scan the whole compass retroactively (a full check is an opt-in separate path).
Step 3.11: Reverse-look-up the Invariants a changed file affects and name them (invariant-impact-reverse; read-only; grep plain match; warn-only)
Background: there are dozens of Invariants, and under the pull discipline (read only the relevant ones, not the full text) the implementer (human/AI) can only recall by hand which Invariants relate to the files they touched, and misses some. Plain-string-match the changed file paths against the impact-path attached to each Invariant (the marker for which files/paths it governs; the counterpart to the previous step's oracle), surface only the related Invariants, and show "this change touches this Invariant (verify it with this yardstick)". The window is aggregated into /intent-validate, so the previous step (oracle presence) and this step (change → affected) come out together in one run (C2/C10; A38; DR72; INV49).
Fire condition (only when changed file paths are available): fire when the user passed changed file paths, or the inherited context names the change target. When no changed paths are available, skip this step and continue the other checks (do not error; like the default-off drift-watch, do nothing with no input).
Match (grep plain string match; not a semantic judgement): plain-match the impact-path attached to each Invariant in the compass (- impact-path: <glob/path fragment>, etc.) against the changed file paths, and surface the matched Invariants. Since this is a plain match needing no semantic judgement, grep may be used as an aid (the convenience exception of INV48 = "string-matching changed paths against the marker"). An Invariant with no impact-path falls out of the matching (backward-compatible; not broken).
Present paired with the oracle (window aggregation; DR72): if a surfaced Invariant has an oracle (a yardstick by which a breach is detectable) attached, show "this change touches this Invariant → verify it with this yardstick" in one breath (working paired with the previous Step 3.10's output).
Temperature: it stays a candidate suggestion that never asserts (false-positive-tolerant). When impact-paths are coarse, unrelated Invariants are also caught, so raise it as a candidate "this change may touch this Invariant" and do not usurp the user's judgement.
Silence: when no Invariant matches, or the changed paths hit no Invariant with an impact-path, fire no output about this check. When compass is absent or there is not a single Invariant with an impact-path, skip this step and continue the other checks.
No gate (most important; INV49): the reverse look-up is an info-severity one-way report; it never stops export or implementation. Do not confuse it with the enforcement gate (writeback-omission only) or the INV43 external-write gate and add a stop mechanism. Making a false-positive-tolerant feature a gate loses trust and gets it disabled.
Step 3.12: Name stale compass rules (compass-rule-decay; read-only; LLM context; semantic)
Background: the compass's Invariants (promises that must not be broken) and Decision Rules (judgement criteria) grow to dozens, and under the pull discipline (read only the relevant ones, not the full text) "a rule whose referent still exists but whose content has drifted from reality" and "a dormant rule that has long gone uncited" go structurally undiscovered. Existing checks catch "the referent disappeared" (dangling-reference), "the packet side lags" (invariant-stale-vs-compass), and "unresolved items lingering" (stale-questions / stale-assumptions), but there was no axis to catch the decay of the canonical rule itself. This check catches it before export (C2/C8/C10; A41; INV54; DR75).
Judge (read in LLM context; not a mechanical check): read each Invariant (INV N) / Decision Rule (DR N) in the compass and name stale rules by three types:
(a) Broken premise: the rule body or its impact-path (the - impact-path: line of the A38 notation) names a file/skill/value that no longer exists or has changed in reality. Checking whether the impact-path's target exists is a plain path match needing no semantic judgement, so Glob/Grep may be used as an aid (the convenience exception of INV48). Whether the body's named target has drifted from reality is an LLM semantic reading.
(b) Dormant: it cannot be traced from any active packet's parent_intents or recent mentions in deltas / drift-log (no trace of being cited). Evidence is limited to in-file timestamps and reference relations (git history is not read).
(c) Relic reference: every packet referencing it is closed/archived, so no live owner remains.
Judgement-material constraint (no numeric threshold; no git history): do not mechanically judge by a numeric threshold of period/count ("stale if uncited for N days" violates INV2). Time-axis evidence is limited to in-file timestamps (the compass Updated (...) tags, packet frontmatter dates, deltas / drift-log date lines); keep Read, Glob, Grep and do not use Bash / git log (the tool contract of the read-only check layer). Even if A40-(2)'s firing trace gets implemented, this axis does not depend on it (a judgement that holds without it).
Narrow the target: scope to the compass's Invariants + Decision Rules. Anti-direction is out of scope (the largest population, highest flood risk; revisit later via DR75's Revisit).
Step 3.13: Name weak acceptance criteria in export drafts (requirement-oracle-check; read-only; LLM-context; semantic)
Background: at the packet-slicing stage (the /intent-packets termination test), acceptance criteria are tightened into "observable criteria that can catch a wrong implementation". But there was no check for criteria that blur when a packet is converted into an export draft (requirements). This catches, just before export, the quality that a packet had tightened being lost in the draft (C10/C27; A42; INV55; DR76).
How to judge (read in LLM context; not a mechanical check): read each acceptance criterion in an export draft (.intent/cc-sdd/<slug>/*.md / .intent/openspec/<slug>/*.md) and check semantically "can this criterion reject a wrong implementation that claims to satisfy it?". Name the weak criteria that cannot (no observable input, condition, or expected result; only subjective words or wishes — e.g., "make it easy to use", "works appropriately", "fast"), and attach a rework suggestion toward an observable form (input → condition → expected result). Do not flag observable criteria (those that state what goes in and what comes out). The judgement is a semantic LLM reading; it never leans on scripts/intent-check.mjs, regexes, or keyword lists (INV2/A1; on par with the existing semantic axes).
Keep the axis separate from the existing quality/boundary axes: report requirements-smell (the surface wording of a requirement = vague words, subjective words, comparatives, weak words, undefined pronouns) / export-draft-mismatch (the consistency between the draft and the packet definition) and this check (whether the acceptance criterion can discriminate a wrong implementation) as separate checks; do not mix findings (different comparison surfaces). The same weak criterion surfacing on both requirements-smell (surface is vague) and this axis (cannot catch a wrong implementation) is normal, and neither silences the other.
Do not hold the definition twice: the source of truth for "an observable criterion that can catch a wrong implementation" lives in the /intent-packets termination test (the oracle in what + constraints + oracle). This axis applies that to export drafts; it does not restate the definition separately on the validate side (it refers to it; Anti-direction 250).
Temperature: stay at candidate suggestions and do not assert (false-positive-tolerant). Raise them as candidates — "this criterion may not catch a wrong implementation" — and do not take the judgment away from the user. When in doubt, do not raise it.
Scope (export drafts only for now): the target is drafts under and . Acceptance criteria in the readable artifacts that emits () are out of scope for now (widen later as operation shows the need; DR76 Revisit).
Step 3.14: Name related-convention coverage gaps (starter-coverage-gap; read-only; LLM-context; semantic)
Background: candidate surfacing (firing) of the starters catalog / personal ledger happens at several hosts (discover / compass / packets / db-design / at implementation start), but every host stays silent when the fit is weak, so an omission can pass quietly. This aligns the supply side (firing) with the inspection side (validate) and, just before export, makes a packet with "a clearly related convention domain never considered" noticeable (A40; DR83 host ⑤; INV57).
Judge (an LLM reading; only clear combinations): read the active packet's Scope / Expected Behavior and, against the domain index of .intent/constraint-starters.md, name only those where "a convention domain corresponding to a technical surface this packet clearly touches (e.g., an externally-exposed API boundary / a persistent data model / concurrent updates) is never considered", attaching which domain file (.intent/constraint-starters/<domain>.md) to look at. To avoid false positives, narrow to clear combinations; do not surface marginal fits. The judgement is an LLM semantic reading; it does not lean on scripts/intent-check.mjs, regexes, or keyword lists (INV2/A1).
Read the decision ledger and stay silent on considered ones (INV57, DR84): read the issue directory's constraint-ledger.md; if a related convention is already considered (adopted/declined/deferred) for that packet, do not name it (not a coverage gap). Where the ledger is unreadable, lower confidence or lean toward silence. A declined one whose purpose/context has changed from decline time may be named as room for reconsideration (no numeric condition; INV2).
Separate the axis from existing quality axes: requirements-smell (surface vague wording) / requirement-oracle-check (whether the acceptance criterion catches a wrong implementation) / invariant-uninherited (a compass universal invariant not inherited) vs. this detection (a clearly-related convention domain left unconsidered). Report as separate checks; do not mix findings.
Match only related domains (INV57): do not brute-force against all conventions. From the parent catalog's domain index, look only at the domains matching the packet's technical surface (minimal-cost pull).
Temperature: stays a candidate suggestion, never asserts (false-positive-tolerant). When in doubt, do not raise it.
Silence; backward compatibility: when there is no clear omission, do not fire this detection at all. When there is no .intent/constraint-starters.md, skip this detection and continue the other checks (treat it as "nothing to check", not mislabel it "no coverage gap").
Step 3.15: Name broken or missing links between acceptance oracles and real tests (oracle-test-link-missing; read-only; LLM-context)
Background: A38 (invariant-oracle-missing) made "does an Invariant have a test oracle" visible. This detection is its post-implementation counterpart: it looks at which test actually guards a packet's acceptance oracle (the "yardstick that rejects a wrong implementation" in ## Validation) after implementation. packet-format's optional section ## Verification protocol is already defined to hold verified-by links (oracle→real-test correspondence), but no one notices when a test is deleted after being written, or the correspondence stays unfilled while implementation reaches done — this read-only detection picks up that round-trip gap (C38/C8/C10; A49; INV63).
Judge (an LLM reading; not a mechanical check): cross-check each packet's acceptance oracles in ## Validation against the verified-by in ## Verification protocol (the corresponding real test's file path + test name) and name two things — (1) a broken link = the test the verified-by points to does not exist (deleted/renamed; existence checking is a simple path match needing no semantic judgement, so Glob/Grep may assist = INV48 exception; matching down to the test name is a semantic read) / (2) unlinked = the oracle has no verified-by, or sits empty without being marked "unlinked". The judgement is an LLM reading of the packet; it does not lean on scripts/intent-check.mjs or regex matching (INV2/A1).
Separate the axis from existing axes: invariant-oracle-missing (whether a compass Invariant has an oracle) / trace-downstream-missing (whether a packet that corresponds to an intent has a downstream link) vs. this detection (the real existence of the correspondence between a packet's acceptance oracle and a real test). Report as separate checks; do not mix findings (the matching surface differs). It works as a pre/post-implementation pair with A38's oracle axis.
Temperature: stays a candidate suggestion, never asserts (false-positive-tolerant). When in doubt, do not raise it.
Silence; backward compatibility: when all of a packet's oracle correspondences are healthy, do not fire this detection at all. An old packet without a ## Verification protocol section is treated as "unfilled" and is not a warning target (absence = unobserved; the backward-compatible reading discipline; do not force retroactive filling; Anti-direction 304). An oracle already marked "unlinked" is treated as a filled, honest state and is not blamed again.
Filling stays a proposal, no auto-modification: naming the broken/unlinked correspondences stays a read-only report; filling in verified-by is a that writeback (the writer phase) performs based on measurement (the inspection layer does not write; A7/INV5). A one-way report at severity "recommended" that does not stop export/implementation (false-positive-tolerant; INV49 warn-only). It does not blame a missing trailer / missing verified-by but shows "where the correspondence cannot be traced" as information (recording is optional; INV63).
Step 3.16: Name provisional/undecided decisions carried over in conflict with the progress stage (provisional-carryover; read-only; LLM-context)
Background: intent-planner's core discipline is "an inference stays provisional until a human reviews it" and "an undecided slot is kept with a revisit condition." But there is no eye watching a packet's progress stage (state) advance to verifying/done while such items are still held — a decision left provisional at done becomes a de-facto settlement (an accomplished fact) even though no one reviewed it. This read-only detection picks up that gap, which is hardest to see precisely when the vessel (holding undecided items) is working correctly (C38/C3/C9/C10; A49; INV63). It is a sibling of compass-rule-decay (decay of the compass rules), but its target differs (this axis = a packet's unrecovered decisions).
Judge (an LLM reading; not a mechanical check): read each active packet's frontmatter (state) and ## Decisions, and name — (1) conflict with progress = the state is verifying/done yet 未定/暫定 (undecided/provisional) slots remain (especially those meeting the five front-load criteria = irreversible / cross-module impact / acceptance oracle / security floor / binding multiple packets) / (2) a satisfied revisit condition = an undecided slot's Revisit when reads as already satisfied given the current state of the deliverables / (3) an unconfirmed provisional marker = an inferred/provisional-marked statement remains without a record of human confirmation (user finalization; a human check in Evidence). The judgement is an LLM reading of the packet; it does not lean on numeric thresholds like elapsed days, scripts/intent-check.mjs, or regex matching (INV2/A1).
Do not penalize the holding of undecided items itself (most important; Anti-direction 300): the warning target is limited to the conflict with the progress stage (done yet unrecovered). Do not dock points for the mere existence of an undecided slot — holding undecided items (with a reason and a revisit condition) is correct design, and shrinking the vessel produces the opposite effect of filling by guessing (the very accomplished-fact problem this axis prevents). Stay silent for packets that hold 未定 while the state is still draft/ready.
Separate the axis from existing axes: ambiguous-deferred-phrasing (the surface of an unsettled verb slipped into settled prose) / decision-slot-empty (an empty slot) / compass-rule-decay (the life or death of the compass rules themselves) / stale-questions / stale-assumptions (stagnation of open items) vs. this detection (). Report as separate checks; do not mix findings (the matching surface differs).
Step 3.17: Name what the downstream deliverables dropped from the draft (draft-content-dropped; read-only; LLM context)
Background: merely "handing over" a draft lets it quietly get dropped during downstream generation. Measured (36 exports cross-checked against downstream specs, 2026-07), only about two-thirds of the intent references the drafts cited survived into the downstream requirements, and barely one in ten generated tasks carried an invariant reference. Even after fixing the mapping (the draft carries the acceptance material; INV75) and the guidance (hand the hints over per phase), the downstream generation can drop things again, so keep watch on the decay (C54 / A60 / DR122).
Judge it (read in LLM context; not a mechanical check): read the export draft (.intent/cc-sdd/<slug>/*.md) side by side with the deliverables the downstream generated from it (.kiro/specs/<feature>/*.md), and name: (1) dropped intent references = the Invariant / Decision Rule / parent-intent references the draft cited survive nowhere in the downstream requirements / design / tasks; (2) dropped acceptance material = the criteria derived from the draft's ## Acceptance Material do not appear in the downstream requirements' acceptance criteria. The judgment is the LLM reading both semantically; never lean on survival-rate scoring, threshold decisions, scripts/intent-check.mjs, or regex matching (INV2/A1).
Judge the two JIT input layers on the same axis (INV95 / DR175): name a missing confirmed material, a binding-strength promotion of a revalidation candidate copied from the target packet's Agent-discretion together with the same item's Revisit when, or a bulk injection of Tree / Compass / archive unrelated to the task as a draft-to-downstream survival or strength problem. Keep it separate from export-draft-mismatch's packet-to-draft responsibility. Detect bulk injection only when structural and semantic evidence clearly shows the full content; a single necessary reference must not become a false positive. When no candidate exists, silently skip candidate survival and promotion checks.
Identify the counterpart, preferring the real link: take the - feature: appended line in the export-record split file (.intent/export-log/<packet-slug>.md) as the primary clue; when absent, fall back to checking whether the packet name in the draft's ## Source Packet appears in the downstream deliverables. When neither identifies the counterpart, say "cannot be matched" in one line and never pair them by guesswork (prefer silence over a false positive).
Keep the axis separate: report this detection (what the downstream deliverables dropped from the draft) apart from (draft vs. packet definition consistency) and (whether the draft's acceptance criteria can reject a wrong implementation); do not mix the findings (the surfaces being cross-checked differ).
Step 3.18: Cross-check shared-contract coverage across packets (cross-packet-contract-coverage; read-only; warn-only)
Only for new or updated items explicitly marked as shared contracts in Impact Analysis, follow identical source references into active packets' ## Safety / Invariants and the existing plan.md. Check semantically that at least one packet names its protection and verification, that multiple protections can coexist, and that an integration oracle exists.
Name only unassigned contracts, incompatible protections, and missing integration oracles, attaching the shared-contract source and related packet names. Multiple protectors alone are not a conflict. Keep findings separate from invariant-uninherited, packet-scope-overlap, and oracle-test-link-missing.
When there is no shared contract, keep the entire check silent. Do not read archive, apply retroactively to existing packets, or modify canonical automatically. Keep severity at recommendation-level as a one-way warning that never stops export or implementation.
Step 3.19: Name an unstarted capability tied to the center of value (capability-starvation; read-only; warn-only)
Cross-check only the intent-tree's North Star / Current Drift, the work plan / Walking Skeleton in packets/plan.md, active packet frontmatter state, and export-log. Through LLM semantic judgment, present as a suspected candidate a value-center packet that remains unexported and unstarted while only supporting packets are completed first. Packet existence or a parent reference alone is not evidence that value was reached.
Use no fixed threshold for days or completed-packet counts, no score, and no grep / scripts/intent-check.mjs decision. Following Step 4, anchor the canonical capability, the unexported packet name, and the completed supporting packet names with verbatim quotes.
Stay silent when the value-center packet is exported or underway, or is progressing as the Walking Skeleton / highest priority. If there is no North Star / Current Drift equivalent, there are not multiple active packets, or the relationship cannot be read, skip the axis as unverified; do not claim there is no gap.
goal-without-packet covers a missing packet; this axis covers the suspected failure to reach value despite an existing packet. Keep success metrics in the existing l1-metric-missing / decision-success-signal surfaces rather than adding another axis. Keep severity at info, never change priority, state, or canonical automatically, and never gate.
Step 3.20: Name missing outcome provenance (outcome-provenance-missing; read-only; warn-only)
Where to read: prefer packet-scoped split deltas at .intent/deltas/**/*.md, then read legacy records absent from the split store from .intent/deltas.md. Do not report the same observation twice when it appears in both. Preserve backward compatibility for existing deltas with no outcome record.
Judge it (read in LLM context; not a mechanical check): for each observation under "Outcome learning", read whether Who measured, When measured, and Where measured contain meaningful values. Treat a blank or an unfilled placeholder as missing, and combine the missing field names into one finding in that fixed order. For the same input, return the same finding; do not shift the judgment into grep, regular expressions, or scripts/intent-check.mjs.
Silence conditions: emit no finding for an observation with all three fields or for an ordinary case with no outcome record.
Independent warning: do not mix this finding with any other check. Present it at recommended severity while remaining read-only and never changing canonical data. Validation and subsequent work (recording, export, and implementation) continue despite the warning; it is neither a stop condition nor an aggregate gate.
Step 4: Report (one-way; fixes are proposals only)
Evidence-anchoring discipline (evidence-anchored finding; applies to every finding; NOT a new detection axis): The following is a cross-cutting discipline that governs how every finding is produced by all existing detection axes (coinage-suspect / groundless-conclusion / unverified-hypothesis / dangling-reference / invariant-oracle-missing / invariant-impact-reverse, and every other axis in the catalog) plus the PBR four perspectives plus the boundary checks. It is not placed as a seventh detection axis (do not add a Step 3.x for it — INV50 / DR73 / Anti-direction 230).
Anchor the evidence with a verbatim quote: Every finding attaches a verbatim quote from canonical (intent-tree.md / intent-compass.md / packets) together with the file name. The quoted string is not altered (no summarizing or paraphrasing — verbatim).
A finding that cannot be backed is not raised to must-fix: If a finding cannot be backed by a verbatim quote from canonical, its severity is not raised to "must-fix" (cap it one level down at "recommended" — OQ-eae-2; this keeps a finding whose evidence is weak but whose concern is real from being buried in "info"). A finding backed by a verbatim quote is emitted as must-fix as usual, per the severity guidance in the catalog.
The judgment is LLM semantic reading, not machine matching: Whether the verbatim quote actually supports the finding is judged by the agent's semantic reading — it does not rely on grep / scripts/intent-check.mjs / regex string-match (this avoids the surface-match trap where a semantically irrelevant quote passes on a mere string match — Anti-direction 232 / INV2 / A1). The severity cap is likewise held as a reporting discipline ("a finding that cannot be backed is not raised to must-fix"), not a machine auto-deduction.
Do not double up with each axis's annotations: Each axis's own annotation (coinage-suspect's rephrasing candidate, groundless-conclusion's correctability lens, invariant-oracle-missing's attachment lens, etc.) stays scoped to that axis's specific concern, and the verbatim evidence is centralized here in Step 4 (OQ-eae-3; do not make it redundant/verbose).
[Abnormal] When canonical is empty/uncreated: There is nothing to quote as backing, so state "cannot be backed by a quote" explicitly and do not raise the finding to must-fix (do not wrongly suppress all findings on a cold start — same shape as unverified-hypothesis's Fail-Safe).
The temperature of existing axes is preserved: Adding this discipline preserves the existing axes' temperature — silence, candidate suggestion, no automatic modification, and not gating (behavior-preserving; warn-only; inheriting the spirit of INV49; does not stop export/implementation).
Present the findings as a list grouped by severity (must-fix / recommended / info), citing for every finding its check ID (the ID column of the table in rules/validate-checks.md) together with the severity (e.g., ).
Output Description
The output target is the terminal. Use no raw HTML (<details> / <summary>, etc., collapsible UI) in the output; separate details with plain Markdown headings instead (in a terminal the raw tags are shown literally and become unreadable). Internal notations such as [[...]] (wikilinks for memory / delta) are legitimate in records written to delta / memory files, but in human-facing terminal output do not emit them raw — open them into ordinary words (spell the linked name out in plain prose).
Reader: a human developer who looks at the findings and decides whether to remediate.
What this output makes them grasp first: "N must-fix / M recommended. These are what to fix, and this is the command to re-run." The enumeration of check IDs / unverified targets is detail for the decision, so place it after the counts and the must-fix items.
Lead the output with the conclusion (the counts and the must-fix items).
Verdict summary (top, one line): N must-fix / M recommended / K info. If there are 0 must-fix, state explicitly "no critical issues".
Domain-scope target count (only on runs under domain scope; right after the verdict summary): on runs that partially loaded via rules/domain-scope.md, state the number of loaded symbols and the total (e.g. "並行 + always = 41 symbols / of 829 total; compass-rule-decay scanned all symbols = inherent exception"). Omit it on full-read runs (as before).
Must-fix list (next): each item with its check ID + evidence (file and the relevant statement) + fix proposal (the skill to re-run or the fix direction). The chunk the reader should tackle first.
Details: the recommended / info-level findings (same format), the unverified targets (including the IDs of skipped checks) and their reasons, and the Open Questions that the human should review.
Include the command to run next in the fix proposals (e.g., re-running /intent-compass).
For a coinage-suspect finding, attach in the fix-proposal area a rewrite / consolidation suggestion toward the canonical term (the target canonical term, or — when no fitting target exists — a note to consider adding a canonical term to the ledger). Presentation only, with no automatic rewrite; adoption is a separate action taken only after a human approves it.
For a groundless-conclusion finding, attach in the fix-proposal area which rationale (reasons, constraints, premises, trade-offs) is not traceable and the correctability lens (whether it can be re-evaluated when a contradicting fact arrives). Supplying the rationale is an update proposal only, with no automatic rewrite; writing it is a separate action taken only after a human approves it.
For an unverified-hypothesis finding, attach in the fix-proposal area which hypothesis (tentative conviction) has no evidence backing it and the refutation/unverified lens (whether it contradicts the evidence in .intent/, and what evidence is needed to back it). Keep it in a separate area from groundless-conclusion (a conclusion's missing rationale) and do not mix the findings. Supplying the evidence is an update proposal only, with no automatic rewrite; writing it is a separate action taken only after a human approves it.
For a dangling-reference finding, attach in the fix-proposal area which numbered reference (Anti-direction N / / ) has a dangling target and the (whether the target disappeared through retirement, merge, or deletion; whether to restore the original item into the body or re-point the reference to a live number). Keep it in a separate area from the existing three axes and do not mix the findings. Re-pointing the reference is an update proposal only, with no automatic rewrite; writing it is a separate action taken only after a human approves it.
Plainness check for reports (user-facing reports; right before output; shared)
Right before emitting a user-facing report (progress, completion, items needing confirmation — including the end-of-turn summary), run this check (INV105, DR208). It applies only to user-facing report text, not to how internal records (canonical files and logs under .intent/) are written.
Do not transcribe internal documents verbatim: text you just read or wrote in internal artifacts (tree, compass, packets, Open Questions) is written in internal vocabulary. In the report, restate that content in words a first-time reader understands (without changing facts or meaning).
Identifiers must not be the subject of the sentence: when presenting an item to confirm or a unit of work, first write one sentence that stands on its own ("what and why"), then append identifiers (Open Question numbers, packet names, symbols, stage names) after it as references (e.g. "... please verify this before starting (ref: OQ-xxx-1)"). Do not delete identifiers or references to records for the sake of plainness (the trail back to the record is lost).
Signal for overload: three or more unexplained internal terms in one sentence signal overload (read by meaning, not by mechanical count). If a sentence does not stand on its own, rewrite it in plain words before sending (without changing facts or meaning).
Do not convey meaning only through a metaphor or a vague qualifier: the foundation of a report is precision — write so the meaning reads unambiguously (plain language is a means of staying easy to read while preserving it). Do not report results only with ungrounded qualifiers (e.g. "significantly", "nicely"); state observable facts. If you use a metaphor, pair it immediately with a precise restatement (do not force established technical terms, or ordinary words in their everyday sense, into strained paraphrases).
This check works as a pair with the after-the-fact record (prevention alone is never enough): when a report failed to get through, log the case to the drift log while drift-watch is on, and feed the next prevention.
Safety & Fallback
Read-only: create, change, or delete no file whatsoever. Keep fixes as proposals, always attaching the skill to re-run or the fix direction.
Only the absence of .intent/ is a stop condition: guide the user through the setup steps and finish.
Partial absence of deliverables is non-blocking: check only the verifiable scope and state the unverified targets and reasons explicitly.
The absence of mode.md does not stop; continue with the standard default and announce it.
Do not change application code (INV6. Being read-only, the skill has no write path in the first place).
Observe the downstream spec deliverables (.kiro/specs/) read-only; never rewrite them (INV1). In cases without .kiro/specs/, skip draft-content-dropped and continue with the other checks.
rejected
Rows without a status — and older 3-column ledgers with no status column — are read as provisional, but the detection output stays as before (registered = silent)
Exclusions: do not name the following as suspected coinages.
Proper nouns (product names, personal names, organization names, etc.).
Established English technical terms.
Legitimate new terms that already carry a first-mention one-line explanation per the terminology convention (do not double-flag what the terminology convention already governs).
Tone: stay a candidate suggestion and never assert (false-positive-tolerant). Raise it as a "this might be a coinage" candidate and do not take the judgment away from the user. When in doubt, do not raise it.
Silence: when there is not a single suspected coinage, do not fire any coinage-detection output at all (do not force anything to be treated as a coinage).
Attach a rewrite suggestion (presentation only): when naming a suspected coinage, attach the glossary ledger's canonical term(s) the coinage could fold into as a "fix proposal" (enumerate candidates if several; e.g., "<coinage> is a candidate to fold into the glossary's <canonical term>"). Like the naming itself, stay a candidate and never assert. When no fitting target is found in the glossary, do not assert a target; instead suggest "consider adding a canonical term to the ledger". The rewrite suggestion rides on the coinage naming: when detection is silent, emit no rewrite suggestion either.
The rewrite is presentation only and rewrites nothing: keep the rewrite / consolidation suggestion to a read-only report, and do not automatically rewrite the canonical artifacts (intent-tree / intent-compass / packets) or the glossary ledger. This skill only names the suggestion; it does not edit files. Any actual adoption is a separate action taken only after a human approves it (this feature stops at presentation; INV20).
No gate: a suspected coinage is an info-severity one-way report; it never stops export or implementation.
separate action
No gate: a suspected groundless conclusion is an info-severity one-way report; it never stops export or implementation (it does not impede legitimate omission — self-evident intents, references to rationale already stated).
Silence: when there is not a single suspected unverified hypothesis, do not fire any output for this detection at all.
Scope: keep the target of this detection to the artifacts in scope for this validate run (new / changed artifacts); do not indiscriminately re-scan the whole tree/compass retroactively (a retroactive inventory is an opt-in separate path).
Cold-start avoidance (Fail-Safe): when the evidence pool (.intent/) is empty, do not treat "no evidence backing = warn outright" but state "cannot be judged because there is no evidence pool" (do not mislabel it as a pass either).
Supplying evidence is presentation only and rewrites nothing: keep any update proposal that attaches evidence to a read-only report, and do not automatically rewrite the canonical artifacts (intent-tree / intent-compass / packets). If the AI fabricates evidence to retro-justify a conviction, it worsens the very drift we want to prevent (A30's premortem). This skill only names "the evidence backing is not traceable"; writing the evidence is a separate action taken only after a human approves it (A7/INV5; INV37).
No gate: a suspected unverified hypothesis is an info-severity one-way report; it never stops export or implementation (it does not impede legitimate omission).
Scope
No auto-edit; re-pointing stays presentation only: naming a dangling reference stays a read-only report; do not auto-re-point references and rewrite the canonical (intent-tree / intent-compass / packets). This skill only names "the target is dangling"; fixing the reference is a separate action taken only after a human approves it (A7/INV5; INV42).
No gate: a suspected dangling reference is an info-severity one-way report; it never stops export or implementation (false-positive-tolerant; Anti-direction 218).
No auto-attach: naming a missing oracle stays a read-only report; do not auto-write the oracle and rewrite the canonical (intent-compass). This skill only names "no yardstick is attached"; attaching it is a separate action taken only after a human approves it (A7/INV5; INV48).
No gate: a suspected missing oracle is an info-severity one-way report; it never stops export or implementation (false-positive-tolerant; inheriting INV49's warn-only spirit).
No indexing (the boundary of A38's future candidate; Anti-direction 229): keep the matching to grep's plain string match; do not pre-emptively add an index such as a vector DB, dependency graph, or GraphRAG. Indexing for when grep becomes a bottleneck at scale violates INV2, so keep it to an L4 future candidate opened only when the user explicitly opts in for reasons of scale.
Separate the axis from existing ones: report dangling-reference (referent disappeared) / invariant-stale-vs-compass (packet-side lag; time axis) / stale-questions / stale-assumptions (unresolved items lingering) / invariant-oracle-missing (missing oracle) and this check (the life-or-death of the canonical rule itself) as separate checks; do not mix findings (the matching surfaces differ). Do not confuse the name with invariant-stale-vs-compass (the packet-side lag) either.
Temperature: it stays a candidate suggestion that never asserts (false-positive-tolerant; especially (b)(c) stay a circumstantial "suspicion"). When in doubt, do not raise it.
Output granularity (split by confidence): (a) broken premise = individual findings (with the INV/DR number and a verbatim quote of the evidence). (b) dormant / (c) relic reference = a one-line count summary by default (same as invariant-stale-vs-compass; expand the individual list only on user request = do not flood with a full individual enumeration).
Reach: decay is an axis that watches "what does not move / what does not appear in the diff", so a full scan is inherently required (an exception to limiting to the run's new/diff), but the output is narrowed by the granularity rule above.
Silence: when there is not a single stale-rule suspicion, fire no output about this check. For a target whose judgement material is unreadable (no Updated (...) tag, no packets, empty active/, etc.), do not mislabel it as "no gap" but name it explicitly as unverified with its ID and skip (Fail-Safe). When compass is absent or there is not a single Invariant/DR, skip this check and continue the others (do not error).
Do not perform the cleanup (no auto-modification): naming a stale rule stays a read-only report; do not auto-tidy, delete, or archive the canonical (intent-compass). The cleanup is executed by a human or by /intent-improve's re-alignment; do not mix detection (this axis) with the fix. Writing is a separate action taken after a human approves (A7/INV5; INV54).
No gate: the stale-rule suspicion is an info-severity one-way report; it never stops export or implementation (false-positive-tolerant; inheriting the warn-only thinking of INV49).
.intent/cc-sdd/
.intent/openspec/
/intent-to-spec
.intent/nl-spec/
Silence / backward compatibility: when there is not a single weak acceptance criterion, fire no output about this check. When there is no export draft at all, skip this check and continue the others (do not mislabel it "criteria are fine"; treat it as nothing to check; do not error).
Rework stays a proposal, no auto-modification: naming the weak criteria and the rework suggestions stay read-only reports; do not auto-rewrite the draft or the canonical (intent-tree / intent-compass / packets). This skill only names "this criterion cannot catch a wrong implementation"; writing the rework is a separate action taken after a human approves (A7/INV5; INV55).
No gate: a weak-criterion finding is a recommended-severity one-way report; it never stops export or the downstream spec flow (false-positive-tolerant; inheriting the warn-only thinking of INV49). The severity is "recommended" because a weak criterion is not a contradiction that halts work but a quality risk whose fix raises the reliability of the draft handed downstream (on par with requirements-smell).
Adoption stays a proposal, no auto-modification: naming and the rework suggestions stay read-only reports; do not auto-adopt into the packet's ## Safety / Invariants (adoption is a human's call; A7/INV5). A one-way report at severity "recommended" that does not stop export/implementation (false-positive-tolerant; INV49 warn-only).
separate action
the conflict between a packet's decision certainty and its progress stage
Temperature: stays a candidate suggestion, never asserts (false-positive-tolerant). When in doubt, do not raise it.
Silence; backward compatibility: when there is no conflict with progress, do not fire this detection at all. An old packet without a ## Decisions section is treated as "unfilled" and is not a warning target (absence = unobserved; the backward-compatible reading discipline).
Finalization stays a proposal, no auto-modification: naming and the suggestion to finalize stay a read-only report; finalizing a provisional value or changing the state is a separate action by a human's declaration (do not auto-finalize, auto-promote, or auto-change state; Anti-direction 303; A7/INV5). A one-way report at severity "recommended" that does not stop export/implementation (false-positive-tolerant; INV49 warn-only).
export-draft-mismatch
requirement-oracle-check
Do not score it: hold no numeric survival rate and no threshold decision (INV2). Go only as far as naming which reference was dropped in which file; never attach a quality score.
Silence and backward compatibility: when .kiro/specs/ is absent (cases that do not use cc-sdd, or implement directly), skip this detection and continue with the other checks (do not mislabel it "nothing dropped"; treat it as having no target, and do not error). When nothing was dropped, emit no output at all for this detection.
No automatic rewriting, no gating: keep the naming and the make-up suggestions to a read-only report; never rewrite the downstream deliverables or the canonical (intent-tree / intent-compass / packets) automatically. It is a one-way report at severity "Recommended" and never stops the export or the downstream spec flow (false positives assumed; INV49's warn-only).
must-fix invariant-conflict: …
Always attach to every item its "evidence (file and the relevant statement)" and a "fix proposal (the skill to re-run or the fix direction)".
State the unverified targets and their reasons explicitly, identifying skipped checks by their IDs.
Present the remaining Open Questions.
Perform no automatic fixes at all.
INV N
DR N
recovery lens
For an invariant-oracle-missing finding, attach in the fix-proposal area which Invariant has no verification oracle (a yardstick by which a breach is detectable) attached and the attachment lens (whether a machine test or a procedure makes the breach detectable; or, for an unattachable principled constraint, marking it "no yardstick"). Keep it in a separate area from the existing four axes and do not mix the findings. Attaching the oracle is an update proposal only, with no automatic rewrite; writing it is a separate action taken only after a human approves it.
For invariant-impact-reverse output, attach to each item which changed file matched which Invariant's impact-path and, if that Invariant has an oracle attached, the means of confirmation (the yardstick) ("this change touches INV N → verify it with this yardstick"). Since this is reverse-look-up guidance rather than a finding, present it as guidance that "prompts verifying the touched Invariants", not as a fix proposal. It is false-positive-tolerant (coarse impact-paths also catch unrelated ones), never asserts, and never stops export/implementation (warn-only).
For compass-rule-decay findings, attach to the fix-proposal column which Invariant/DR is stale under which type (broken premise / dormant / relic reference) and the cleanup perspective (for a broken premise, update the body/impact-path to match reality or archive the whole rule; for dormant/relic reference, re-confirm whether a live owner truly no longer exists and re-align via /intent-improve). (a) broken premise is individual with a verbatim quote of the evidence; (b) dormant / (c) relic reference is a one-line count summary by default (expand the individual list only on user request). Keep it as a separate column from the existing staleness / consistency axes; do not mix findings. The cleanup stays a proposal only with no auto-modification; writing/archiving is a separate action taken after a human approves.
For requirement-oracle-check findings, attach to the fix-proposal column which acceptance criterion is a weak one that cannot catch a wrong implementation (with a verbatim quote of the evidence) and the rework perspective (how to rewrite it into observable input / condition / expected result). Keep it as a separate column from requirements-smell (surface vague wording) / export-draft-mismatch (draft-vs-packet consistency); do not mix findings. The rework stays a proposal only with no auto-modification; writing into the draft is a separate action taken after a human approves.
For oracle-test-link-missing findings, attach to the fix-proposal column which acceptance oracle has a broken link (the test its verified-by points to does not exist) or is unlinked (no verified-by) (with a verbatim quote of the evidence) and the remedy perspective (for a broken link, re-point verified-by to the deleted/renamed test or re-confirm the correspondence; for unlinked, have writeback fill in verified-by based on measurement, or mark it "unlinked"). Keep it as a separate column from invariant-oracle-missing (whether an Invariant has an oracle) / trace-downstream-missing (whether a downstream link exists); do not mix findings. Filling in verified-by stays a proposal only with no auto-modification; the filling is a separate action that writeback performs based on measurement.
For provisional-carryover findings, attach to the fix-proposal column which packet's slot/marker is carried over in conflict with the progress stage (with a verbatim quote of the state and the relevant slot) and the remedy perspective (have a human review and promote it to a finalized value; if a revisit condition is satisfied, revisit and finalize; attach a human check to a provisional marker). Do not blame the holding of undecided items itself; show only the conflict with progress (Anti-direction 300). Keep it as a separate column from ambiguous-deferred-phrasing (the surface of an unsettled verb) / decision-slot-empty (an empty slot) / compass-rule-decay (the life or death of a compass rule); do not mix findings. Finalization / state change stays a proposal only with no auto-modification; it is a separate action taken after a human approves.