--- name: check-scenario-overlap description: "Partial Skill: invoke by name only — project-spec/scenario-overlap's engine that detects the same behavior specified in two nodes' suites across one project spec — feeds the formation Warden, not triggered by users directly." user-invocable: false metadata: internal: true --- # Check Scenario Overlap The concrete engine for **cross-node scenario-overlap detection** — the intra-project **spec-level SSA** partner of the code-side collision ladder. It audits **across the nodes** of one project spec and returns the candidates where the **same behavior lives in more than one node's `.feature`**, for the formation **Warden**. It is the **cross-node** sibling of `check-spec-structure` (intra-node node-shape), whose per-node scope leaves this cross-node axis uncovered. Self-contained `.mjs` (compiled from `src/`; no runtime deps, Node ≥22.11). ## Why this rung exists The scheduler treats two missions touching different `.feature` files as **file-disjoint** — but if the **same behavior** is specified in **two** files, a change to that behavior must touch both, so what looked disjoint is a **hard collision the scenario rung cannot see** (it diffs changed scenarios per file, never across files). One behavior = **one scenario in one owning node** keeps the scenario rung honest. This detector covers cross-*node* overlap **inside** a project. ## The two deterministic candidate kinds (and one judgment arm) - **exact-duplicate** (blocking) — two **distinct** nodes whose suites each carry a scenario with an **identical normalized fingerprint** (the ordered Given/When/Then step bodies, whitespace- and case-normalized; for a `Scenario Outline`, its normalized `Examples` table too). A near-certain one-behavior-two-nodes violation; `--check` fails on it. - **title-overlap** (advisory) — two distinct nodes sharing a **normalized scenario title** but with **differing** fingerprints. A weaker hint (same words may name different behavior); **never** fails `--check`. - **real-overlap + owning-node** is a **Warden judgment** (the spec's `@rubric` scenario) — the engine ships no verdict. The Warden confirms the candidate is the same behavior (not a coincidental text match) and **assigns a single owning node** for the dedup. The fingerprint is computed from **step bodies only** for a plain `Scenario` — its title, tags, comments, and the `.feature` prose never reach it, so the signal is behavior-shaped, not cosmetic. A `Scenario Outline`'s steps are a **template**, not its content: every canonical `@trigger` outline shares byte-identical steps by construction, so its fingerprint also folds in its **`Examples` table** (header + rows, normalized cell-by-cell) — two outlines are an exact-duplicate only when their steps **and** their rows match. Title and tags stay excluded from both shapes. Detection is **cross-node only**: a scenario duplicated **within one node** raises no candidate, and a scenario appearing **once** corpus-wide raises nothing. ## Run the scan ```bash node "/scripts/check-scenario-overlap.mjs" [--spec-dir ] [--check] [--format toon|json] ``` - Default `--spec-dir` is the current directory; default `--format` is **TOON** (the token-efficient form the Warden scans). - **Audit mode** (default) emits the candidate set — a `blocking[]` group then an `advisory[]` group, each candidate naming **both** nodes and the overlapping scenario. `--format json` emits the same candidates as a flat JSON array. - **`--check`** (CI guard) exits **non-zero** iff an **exact-duplicate** candidate exists and **writes nothing**; title-overlap advisories still exit zero. Wire it after `check-spec-structure --check` in `verify:specs` so the project-spec holds the one-behavior-one-node partition. When `node` is absent, an agent performs the same derivation by hand: parse each node's `.feature` into scenarios, normalize each scenario's ordered steps into a fingerprint, and flag any fingerprint (or, more weakly, any title) shared across two distinct node folders. ## Boundaries Steps + titles only — it never reads a scenario's prose into the fingerprint, owns no lifecycle state, and writes nothing. It **never acts** on a candidate (a dedup, a relocation) — that is the Warden's (`sdd:formation-loop`) under its own self-clear-vs-escalate verdict. It does **not** audit node-shape (`check-spec-structure`), flag within-node duplicate scenarios, render the by-concept view (`concept-index`), or advise a new node's home (`place-node`).