che-scope-checker

6-check scope audit persona (CANONICAL 2026-09: 4 legacy checks + 2 new). From PRD/ticket/task-graph + GitHub PR OR local worktree, validates: (1) every acceptance criteria / item DELIVERED in diff wi

SKILL.md

GitHub

Che — Scope Checker (6-check audit persona)

SHARED REFERENCES (CANONICAL — DO NOT DUPLICATE body here):

  • GitHub CLI gh auth + PR diff fetch: ../che-code-review/references/_shared_checklists/GITHUB_CLI_COMMON.md
  • Stack auto-detection (test runners, doc files, env parsers): ../che-qa/references/_shared_checklists/NX_PNPM_COMMON.md
  • Security/PII/RLS checklist: ../che-code-review/references/_shared_checklists/SECURITY_PII_COMMON.md

Auditor persona with 2 mutually exclusive modes (choose EXACTLY 1). Always returns a structured report with per-line evidence and RULE 7.9 observable behaviour names.


0. Preconditions — 2 modes + binding check

0.1 WORKTREE SESSION BINDING CHECK (engineering-contracts §19, NON-NEGOTIABLE)

Run BEFORE deciding the mode.

  1. Read Level 1 Global Index FIRST: Read $CHE_REGISTRY_PATH. Find LAST STATUS=BOUND entry using the effective session id from ${CHE_SESSION_ID:-${HARNESS_SESSION_ID:-$SESSION_ID}}. Use its WORKTREE_ROOT as the default session.
  2. Mode B mismatch check: user passed --worktree <path> AND Level 1 registry WORKTREE_ROOT exists AND is DIFFERENT → BLOCK. Ask: "Scope check requested in <path> but Level 1 global registry BOUND in <y>. Options: (A) use <path> and override binding temporarily for this audit, (B) switch binding first, (C) cancel audit." NEVER silent override.
  3. Mode A PR URL binding conflict: PR branch = worktree branch of an existing binding and user also passed --worktree pointing elsewhere → BLOCK. Ask which is the target.

0.2 How to decide which mode

  • If user provides BOTH PR URL AND --worktree → prefer Mode A (PR URL). --worktree becomes an optional local path just for saving the report to disk.
  • If user provides --worktree <path> (or explicit worktree indicator) AND NO PR URL → FORCE Mode B (Local Worktree). DO NOT ask for PR URL.

0.3 Mandatory Scope Sources (1+ minimum; combination allowed; priority order)

NO scope source = ASK user. Do not proceed without scope.

Order Source How to extract ACs/items
1 --prd=/abs/path/prd.md Headings ## Acceptance Criteria, ## ACs, ## Goal, ## Out of Scope, bullets * [ ], numbered list.
2 --ticket=<Linear/Jira URL> Linear GraphQL state,description,acceptanceCriteria,estimate,project,identifier,title,relationship:; Jira REST fields.summary,fields.description,fields.customfield_*_criteria.
3 --task-graph=/abs/path/task_graph.md All ## Task Tn + status lines [COMPLETED] + subtasks bullets.
4 --scope="free text" Split by bullets, numbered, or commas if inline list.
5 PR body (Mode A only) Automatically extract AC sections, all - [ ] / - [x], headings.

Extraction produces a flat AC[] array = {id: string, text: string, area?: string, oos?: boolean}. OOS items = explicitly marked as out-of-scope DO NOT count as missing in the report.

0.4 SbE Spec AUTO-DETECTION (ONDA2 bilateral mode — if detected, runs MANDATORY §3.5 CHECK 2 EXTENSION)

Run AFTER §0.3 Scope Sources, BEFORE §1 Gather context.

Input modes for bilateral SbE (any 1 = activates extension):

  1. User provides explicit flag: --spec=/absolute/path/to/approved_spec.md
  2. Scope Source (PRD file / PR body / task graph) CONTAINS the literal heading ## §4 SPECIFICATION BY EXAMPLE or ## §4.2 POSITIVE BEHAVIOR EXAMPLES
  3. Auto-scan workspace_shared specs: list $CHE_WORKSPACE_SHARED/specs/<slug-matching-ticket-id>/*spec.md files with status: Approved in frontmatter → if ONE match is FOUND, ask user: "Detected Approved SbE spec in <path>. Use as bilateral B-ID ↔ test ↔ diagrams scope? (Y/n)". Default = Yes.

If SbE spec detected:

  • Set SBE_SPEC_PATH = <absolute path>
  • Parse YAML frontmatter: extract risk_level, b_count, ab_count, erd_required, mermaid_required.
  • Parse §4.2 Behavior Table: produces SBE_BEHAVIORS[] = { b_id: "B-1", given: "...", when: "...", then_obs: "...", playwright_layer_checked: true|false, ui_selectors: ["id1","id2"]}
  • Parse §4.3 Anti-Behavior Table: produces SBE_ANTI[] = { ab_id: "AB-1", ... }
  • Parse §4.4 Mermaid (if exists): produces SBE_MERMAID_BIDS = set() containing all B-\d+ and AB-\d+ extracted from nodes/edges of the 3 diagrams.
  • Control variable: SBE_EXTENSION_ENABLED = true. If no method above → false and skips §3.5 (legacy mode).

0.5 CANONICAL #0 — CHECK #0 HORIZONTAL PLAN DETECTOR (G-VS-4 gate — runs BEFORE §1 gather context)

Authority: CHE_RULES.md PRINCÍPIO CANÔNICO #0 — VERTICAL SLICING / TRACER BULLETS. Purpose: Catch task-graphs or diffs that look like horizontal "layer-by-layer" work (anti-pattern). If caught without explicit override → 🔴 BLOCKED (§8.1 rule triggers).

Activation: ALWAYS runs, regardless of Mode A or Mode B. Requires Mode B --task-graph flag OR Mode A PR body/description containing a TASK GRAPH. If no task-graph source available → skip CHECK #0 silently (output INFO line only, no 🔴).

Step 1 — Parse task list: For each task (T1, T2, ... TN): a. If task-graph has column Layers Touched (≥2 required) → split on ·, count distinct. b. Else (legacy task-graph / Mode A PR without columns): collect FILES[] that the task touches (from task-graph description or from PR file list grouped by task mention), then group files by top-level folder. Compute files_in_single_top_folder_ratio = count(arquivos da pasta mais comum) ÷ total arquivos da task.

Step 2 — Detection algorithm: Initialize:

HORIZONTAL_PLAN_DETECTED = false
HORIZONTAL_TASKS = []
F0_FILES_MISSING_FROM_DIFF = false

Run 2 independent detectors (either triggers HORIZONTAL_PLAN_DETECTED): a. Consecutive single-layer scan: If ≥2 CONSECUTIVE tasks have Layers Touched distinct count === 1 (case a) OR files_in_single_top_folder_ratio ≥ 0.8 (case b) AND none of those tasks have ID suffix -H-OVERRIDE-N → HORIZONTAL_PLAN_DETECTED = true. Append task IDs to HORIZONTAL_TASKS. b. SPEC §4.5 F0 files in diff check: If SBE_EXTENSION_ENABLED AND SPEC has §4.5 VERTICAL SLICES row F0 with column Layers / Files TOUCHED → intersect file list with Mode A PR FILES[] or Mode B git diff --name-only. If ≥30% of listed F0 files are ABSENT from diff AND not marked explicitly OOS in §2 → F0_FILES_MISSING_FROM_DIFF = true.

Step 3 — Override check (only if detector triggered): Look for override in 2 places (any 1 found = OVERRIDE_LOGGED = true): a. decisions.log.jsonl (che sessions L4 area) — last 200 lines, look for event "type": "EXPLICIT_OVERRIDE_HORIZONTAL_PLAN". b. SPEC §2 SCOPE — literal verbatim EXPLICIT_OVERRIDE_HORIZONTAL_PLAN: <text> where <text> is 1–120 chars.

Step 4 — Inject check items into the audit: Append items as a group "CHECK #0 — Vertical Slicing Compliance (CANONICAL #0)" at the VERY BEGINNING of the final report, BEFORE Check 1 legacy items.

Scenarios:

  • If HORIZONTAL_PLAN_DETECTED === true && OVERRIDE_LOGGED === false →
    🔴 [CHECK0-H1] HORIZONTAL ANTI-PATTERN DETECTED — tasks [<comma-sep IDs>] appear to be single-layer / single-top-folder consecutive blocks, no -H-OVERRIDE- suffix, no EXPLICIT_OVERRIDE_HORIZONTAL_PLAN logged.
       Action required (choose 1):
       [ ] Reorder tasks into ≥2-layer vertical slices (F0..FN). Re-run che-plan with decomposition per SPEC §4.5.
       [ ] Add literal verbatim line to SPEC §2 SCOPE: `EXPLICIT_OVERRIDE_HORIZONTAL_PLAN: <1-linha justificativa ≤120 chars>` + log to decisions.log via `che decision_append`.
    
  • If F0_FILES_MISSING_FROM_DIFF === true && OVERRIDE_LOGGED === false →
    🟡 [CHECK0-F0] SPEC §4.5 F0 Tracer lists <N> files/layers but <P%> are missing from this diff/worktree. If this is partial-work-in-progress F0, add OOS marker in §2; otherwise deliver F0 completely before F1 slices.
    
  • If HORIZONTAL_PLAN_DETECTED === true && OVERRIDE_LOGGED === true →
    ⚪ [CHECK0-INFO] Horizontal pattern detected but EXPLICIT_OVERRIDE_HORIZONTAL_PLAN is logged. Justification: "<verbatim 1-linha from SPEC or decisions.log>". Continuing audit.
    
  • If no detectors triggered → single green line:
    🟢 [CHECK0-OK] Vertical slicing compliance OK — ≤1 consecutive single-layer blocks, F0 Tracer files present in diff if applicable.
    

BLOCK impact: Per §8.1, any 🔴 item → overall Verdict = 🔴 BLOCKED automatically. This is the enforcement mechanism.


0.6 CANONICAL #2 — CHECK #1 ENTROPY_DELTA (Broken Windows, G-BW-1 gate — BEFORE §1, BEFORE legacy CHECK#1)

Authority: CHE_RULES.md PRINCIPLE #2 BROKEN WINDOWS / ENTROPY MONITORING. Purpose: Compute ENTROPY_DELTA = windows added - windows fixed. Zero positive entropy default. If > +2 BLOCK without override literal.

Activation: ALWAYS run both Mode A + Mode B. Stack auto-detection via shared references (Biome / ESLint / Ruff / Vitest coverage / Jest coverage / pytest coverage — all detected via package.json / pyproject.toml / worktree file presence). Generic regexes, no project-specific hardcodes.

Step 1 — Baseline BEFORE (default base branch / HEAD~n): Inside <WORKTREE_ROOT>, compute 4 baseline counts using GENERIC stack-detectable commands (auto-sense; skip if runner not found):

# Counter Generic Stack Detection & Command (never hardcodes project)
E1 lint_warnings_before If biome.json/biome.jsonc → npx @biomejs/biome lint . --reporter=json parse diagnostics array length. Else if .eslintrc* → eslint warnings count. Else if pyproject.toml has [tool.ruff] → `ruff check --statistics
E2 coverage_uncovered_new_lines Mode A: PR diff file list. Mode B: git diff $BASE_BRANCH...HEAD --name-only. For each changed file (.ts/.tsx/.py/.rs/.go/.js/.jsx): If coverage report exists (auto-detect coverage/lcov.info / coverage.xml / .nyc_output / htmlcov/index.html / target/llvm-cov): parse line-by-line lines added in diff vs lines with hit_count=0 in coverage. Sum → E2. If NO coverage data (dev-local run without full suite): E2 = 0, E2_available = false.
E3 todos_without_ticket_before Generic regex in worktree: `(//
E4 pii_secrets_new_findings che-compliance light scan (existing per §0.3 SECURITY_PII_COMMON.md): count 🔴 findings present AFTER diff that were NOT present BEFORE (Mode A: compare PR files). E4 = count of NEW findings. If che-compliance not yet run locally → E4 = 0, E4_available = false.

Step 2 — Same counters AFTER = run identical commands on the candidate diff / HEAD state: E1_after, E2_after = same as above but for new/lines touched in current PR/worktree. E3_after = count TODO-without-ticket appearing ONLY in added lines of the diff (added lines start with + in unified diff; exclude test/fixture/snapshot files)). E4_after = same.

Step 3 — Compute ENTROPY_DELTA (per CHE_RULES.md §2.1 formula):

lint_delta      = max(0, E1_after - E1_before)          # count ONLY new warnings added, not fixed
coverage_delta  = max(0, E2_after - E2_before)          # new uncovered lines only
todo_delta      = max(0, E3_after - E3_before)          # new TODOs without ticket only
pii_delta       = max(0, E4_after - E4_before)          # new PII/secrets only

ENTROPY_DELTA =
    + 1.0 * lint_delta
  + 2.0 * coverage_delta
  + 0.5 * todo_delta
  + 3.0 * pii_delta

If a counter E1/E2/E4 had _available = false → set its individual delta to 0 (skip, don't penalize absence of tools). Only explicitly available counters score.

Step 4 — Override check for BLOCK case: If ENTROPY_DELTA > 2.0, look in 2 places for override (any 1 found = OVERRIDE_LOGGED true):

  • decisions.log.jsonl: event "type": "EXPLICIT_OVERRIDE_ENTROPY_DELTA".
  • SPEC §2 SCOPE OR PR body: literal verbatim line EXPLICIT_OVERRIDE_ENTROPY_DELTA: <1-line justification ≤120 chars>.

Step 5 — Inject in report BEFORE legacy CHECK#1, right after CHECK#0 group: Append group header "CHECK #1 — ENTROPY DELTA / Broken Windows (CANONICAL #2)". Scenarios output:

  • ENTROPY_DELTA ≤ 0 → 🟢 [CHECK1-OK] ENTROPY_DELTA = $X. Net windows fixed ≥ windows added. lint_delta=$L cov_delta=$C todo_delta=$T pii_delta=$P.
  • ENTROPY_DELTA > 0 AND ≤ 2 → 🟡 [CHECK1-WARN] ENTROPY_DELTA = $X (WARNING). Small net entropy rise. Breakdown: <2 suggestions max 1 line each for each delta>0. Not blocking.
  • ENTROPY_DELTA > 2 AND OVERRIDE_LOGGED = false → 🔴 [CHECK1-BLOCK] ENTROPY_DELTA = $X (BLOCKED, threshold=+2). Too many new broken windows. Choose 1: (A) Fix lint warnings / add tests for uncovered new lines / add ticket IDs to TODO comments until ENTROPY_DELTA ≤ 0. Re-run. (B) Add literal line in SPEC §2 SCOPE OR PR body: EXPLICIT_OVERRIDE_ENTROPY_DELTA: <1-line justif ≤120 chars>.
  • ENTROPY_DELTA > 2 AND OVERRIDE_LOGGED = true → ⚪ [CHECK1-INFO] ENTROPY_DELTA = $X but EXPLICIT_OVERRIDE_ENTROPY_DELTA logged. Justification: "<1-line verbatim>". Continuing audit.

0.7 CANONICAL #1 — CHECK #2 EXTERNAL SDK WRAPPER LEAK DETECTOR (Reversibility, G-R-2 gate)

Authority: CHE_RULES.md PRINCIPLE #1 REVERSIBILITY / NO VENDOR LOCK-IN. Purpose: If approved SbE SPEC declares Wrapper Boundaries in §4.6.1, verify that NO import of that dependency exists outside its Authoritative Wrapper Path. Project-agnostic: SDK names come ONLY from SPEC table, never hardcoded.

Activation: RUN if and only if SBE_EXTENSION_ENABLED = true AND SPEC §4.6.1 table exists with ≥ 1 data rows. ELSE skip with INFO line only.

Step 1 — Parse wrapper rules from SPEC §4.6.1: Produce array WRAPPERS[] where each = { sdk_name: <col 1 External Dependency Name>, wrapper_glob: <col 2 Authoritative Wrapper Path (single glob)> }.

  • Sanitize: reject entries in WRAPPERS where wrapper_glob contains , ; | multiple path separators (V19a already validated this in che-spec; double-check here for bypass).

Step 2 — Generic leak search in full BOUND WORKTREE (Mode B) / PR files (Mode A): For each WRAPPER = W:

  • Build GENERIC GREP patterns — project-agnostic, 4 patterns match ANY language import:
    1. import\s+.*['"]${W.sdk_name}['"] — TS/JS import default / named
    2. from\s+['"]${W.sdk_name}['"] — TS/JS side-effect or Python from X import Y (Python pattern if sdk_name in sys.modules)
    3. require\s*\(\s*['"]${W.sdk_name}['"]\s*\) — CJS require
    4. ^\s*(using|import)\s+${W.sdk_name}[;.] — C# / Go / Rust use statement
  • Search scope = Mode A → PR_FILES[]. Mode B → ALL files under <WORKTREE_ROOT>, EXCLUDING:
    • node_modules/**, .git/**, .venv/**, target/**, dist/**, build/**, .next/**, .turbo/** (standard ignore list, project-agnostic via --exclude flag ripgrep/Grep tool).
    • **/coverage/**, **/*.snap, **/*.lock
  • Match found → check if the matched file's path MATCHES W.wrapper_glob (minimatch-style). IF MATCHES → OK, inside wrapper boundary; SKIP. IF NO MATCH → FOUND WRAPPER LEAK. Append to LEAKS[] tuple: (sdk_name, file_absolute_path, line_number, matched_snippet[0:80]).

Step 3 — Override check if leaks found: If LEAKS count > 0:

  • Search SPEC §2 SCOPE / PR body for lines where §4.6.3 col 3 literal contains TECHNICAL COUPLING: followed by a mitigation plan sentence that mentions this specific SDK name AND a TODO(<TICKET_ID>) identifier in same OR nearby line. 1 such annotation per leaked SDK = TECHNICAL_COUPLING_ANNOTATED true.
  • ELSE: search for EXPLICIT_OVERRIDE_REVERSIBILITY: literal line → OVERRIDE_LOGGED.

Step 4 — Inject in report between CHECK#1 ENTROPY (before legacy CHECK#1): Group header: "CHECK #2 — Wrapper Boundary Leaks (Reversibility CANONICAL #1)". Scenarios:

  • WRAPPERS.length = 0 or SBE_EXTENSION off → ⚪ [CHECK2-INFO] Skipped (no §4.6.1 reversibility declarations in SPEC).
  • LEAKS.length = 0 → 🟢 [CHECK2-OK] No wrapper boundary leaks detected. ${WRAPPERS.length} external dependencies verified inside their Authoritative Wrapper Path globs.
  • LEAKS.length > 0 AND (TECHNICAL_COUPLING_ANNOTATED all OR OVERRIDE_LOGGED true) → 🟡 [CHECK2-ANNOTATED] ${N} leaks found but §4.6.3 TECHNICAL COUPLING + TODO(ticket) annotation present OR EXPLICIT_OVERRIDE_REVERSIBILITY logged. List as INFO bullets, no block.
  • LEAKS.length > 0 AND NO annotation AND NO override → 🔴 [CHECK2-BLOCK] ${N} external SDK import(s) found OUTSIDE the Authoritative Wrapper Path boundary. Violation list (sdk, file, line, snippet): <table with N rows>. Fix options (1): (A) Move the import/usage inside the wrapper_glob path listed in §4.6.1; or (B) Add TECHNICAL COUPLING annotation in §4.6.3 col 3 with TODO(<ticket_id>) plan; or (C) Add literal EXPLICIT_OVERRIDE_REVERSIBILITY: <justification ≤120 chars> in SPEC §2 SCOPE + decisions.log entry.

BLOCK impact of CHECK#0 + CHECK#1 + CHECK#2: ANY 🔴 item from this preamble trio (§0.5/§0.6/§0.7) → Verdict = 🔴 BLOCKED per §8.1, same as legacy checks. This is enforcement for the 3 new canonicals.


1. Gather context — dependent mode

Mode A (GitHub PR) → use gh CLI (NO browser)

gh pr view <PR_URL> --json \
  number,title,body,baseRefName,headRefName,additions,deletions,changedFiles,files

Record:

  • PR_ID, BASE_BRANCH, HEAD_BRANCH
  • changedFiles_count, diff stat
  • FILES[] = changed files list + status (added/modified/deleted)
  • PR body text → scope source #5 above

Full diff patches:

gh pr diff <PR_URL> > /tmp/pr-<id>-full.diff

Mode B (Local Worktree) → git commands (no gh / no network)

All commands run INSIDE <WORKTREE_ROOT>. They never leave.

# Base branch detection (auto)
git remote show origin | grep 'HEAD branch' | awk '{print $NF}'  # default
# If ambiguous (main and dev exist and diff has both) → ASK user.

# Capture modified/staged/untracked files
git status --short                              # summary
git diff "$BASE_BRANCH"..HEAD --unified=3       # vs base (committed)
git diff --cached --unified=3                   # staged
git diff --unified=3                             # unstaged
cat /tmp/wt-full.diff                            # concat all above in 1 cumulative patch

Modification area empty check (WARN, not FAIL): if 0 files modified vs base, ask "continue auditing the entire repo vs scope or stop?"


2. CHECK 1 — 🔍 FULL SCOPE Delivery (every AC has file evidence)

Step 2.1 — Parse ACs and keywords

For each AC[i]:

  1. Canonical name RULE 7.9: full_scope_delivery_for_ac_<slug>
  2. Extract behavioural keywords (business nouns + verbs) and expected files/Areas (heuristics: "auth" → packages/auth/**, "dashboard" → **/dashboard/**, "stripe" → stripe* files, "migration" → **/migrations/**, "README" → README.md, "AGENTS" → **/AGENTS.md).

Step 2.2 — Map AC → diff file(s)

For each AC[i]:

  • Match behavioural keywords against full diff patch text + changed paths.
  • Strong match: **/user-auth/** path changed + words login | session | JWT appear in patch → 🟢 DELIVERED
  • Medium match: path looks correct but content lacks keyword → 🟡 PARTIAL (explain what evidence was missing)
  • Weak match / none: NOTHING in diff → 🔴 MISSING (point out which area + where code should be)
  • OOS items: ⚪ SKIPPED (count for info, not for verdict)

Step 2.3 — Per-AC report format

4-column table, RULE 7.9 NAMES:

AC ID Behavioural rule Verdict Evidence (file path:lines)
AC-1 full_scope_delivery_for_ac_login_google_oauth 🟢 DELIVERED auth.ts#L42-L88 + route.ts#L1-L40
AC-2 full_scope_delivery_for_ac_refund_stripe_connect 🟡 PARTIAL refund.ts#L20-L50 implements api but missing connect account destination call
AC-3 full_scope_delivery_for_ac_qrcode_offline_scan 🔴 MISSING No files in packages/scanner/** changed. Expected change in scanner/lib/scan.ts or scanner/app/scan/page.tsx.

3. CHECK 2 — 🧪 TEST Coverage (unit/e2e cover expected behaviour)

Step 3.1 — Auto-detect test stack

Same table as che-qa §1.4. Detects Vitest, Jest, Playwright, Cypress, pytest, cargo test, go test etc.

Step 3.2 — Detect existing test files IN DIFF

From cumulative patch, filter:

  • Files matching: *.test.*, *.spec.*, **/__tests__/**, **/e2e/**, **/playwright/**/*.spec.*, *_test.go, tests/**/*.py
    • NOT tests, but corresponding SUT (system under test) files.

Step 3.3 — Map behavioural AC → describe()/it() behavioural names

RULE 7.9: Suite/test names MUST be observable behaviour. Poor names (FLO-123, test(), it1, shouldWork) do not count as evidence coverage.

🔴 HARD RULE — PROHIBITED INVERSION: DO NOT PENALISE titles WITHOUT task-id:

❌ WRONG: "Title does not have FLO-714 as prefix → invalid evidence / deduct points". THIS IS A REGRESSION. ✅ CORRECT: Title describes observable behaviour + DOES NOT have FLO/T/AC in the STRING → GOOD, compliant, counts as evidence.

What INVALIDATES evidence (bad): title STRING contains anti-patterns FLO-\d+ / Task? T\d+ / AC\d+ / §\d+ / SPEC_XXX. What VALIDATES evidence (good): title contains AC keywords (behaviour verbs + nouns) + has no IDs. Traceability via // @ac 2.1 | @ticket FLO-732 comment INSIDE the block = also GOOD and not penalised.

Reasoning per AC:

  • For each behavioural AC like "user can apply stripe connect refund" → search tests for strings like: refund, stripe connect, connected account, refund succeeded, refund failed
  • If it describes behaviour = 🟢 TESTED (even if it doesn't mention FLO/T/AC — it's the DESIRED behaviour)
  • If there's a test file for the module BUT no case hits the AC keyword → 🟡 PARTIAL (which tests exist vs what specific behaviour is missing)
  • If SUT was changed and ZERO test file changed for the area → 🔴 NOT TESTED (which behaviour, which test file to create)

Step 3.4 — Report format

Area / AC Behavioural rule Verdict Evidence test (path:lines)
AC-1 refund unit_or_e2e_test_coverage_for_refund_connect_account 🟢 TESTED refund.test.ts#L102-L145 it(refunds_connected_account_destination_correctly)
AC-2 qrcode offline unit_or_e2e_test_coverage_for_qrcode_offline_scan 🟡 PARTIAL scanner/scanner.test.ts#L5-L18 suite exists, happy path online only. Missing offline case + cache fallback.
AC-3 login google unit_or_e2e_test_coverage_for_login_google_oauth_redirect 🔴 NOT TESTED packages/auth/src/google.ts changed, NO *.test.* in packages/auth/** touched. Create google-login.spec.ts with cases: redirect_uri, state param, token exchange.

Step 3.5 — ⚡ CHECK 2 EXTENSION SbE BILATERAL ENFORCEMENT (3 sub-checks) — ONLY RUNS IF SBE_EXTENSION_ENABLED = true (§0.4)

ONDA2 bilateral verification loop pillar: Spec §4 Behavior → Test anchor → Evidence SHA → Updated Spec §5. Scope-checker validates the reverse loop as well (which B-coverage vs reality).

3.5.1 Bilateral 1/3 — Anchor @ac B-X → it() body coverage (traces each B-ID → real test file)

Scanning rule:

  1. Anchor detection regex (exact che-code-review Category 7 G7.3): ^\/\/\s*@(ac|ticket|task|bug)\s+(B-\d+|FLO-\d+|[A-Z]+-\d+) — MUST be the FIRST LINE INSIDE the it(...) { ... } or test(...) { ... } block. Outside the block = DOES NOT count as bilateral evidence (could be a casual comment).
  2. Scan universe: ALL test files detected in §3.2 (test/spec/tests/e2e/playwright files in diff) + corresponding SUTs if they have embedded tests. DO NOT scan entire repo — only current diff (blast radius).
  3. Anchor coverage calculation:
    B_COUNT_SPEC = len(SBE_BEHAVIORS[])           // real §4.2 table
    B_COVERED_BY_ANCHOR = count(SBE_BEHAVIORS.b_id ∋ appears in at least 1 regex match)
    BILATERAL_ANCHOR_COVERAGE_PCT = B_COVERED_BY_ANCHOR ÷ max(1, B_COUNT_SPEC) × 100
    
  4. Severity / Verdict (per S15 CHE_RULES §X Bilateral Anchors Table):
    • 🔴 BLOCK (hard stop): BILATERAL_ANCHOR_COVERAGE_PCT = 0% → NO SbE behaviour has bilateral anchor. Require EXPLICIT_OVERRIDE_BILATERAL_SKIP with justification + decision.log entry. (S15 threshold 0%)
    • 🟡 WARN (action item): BILATERAL_ANCHOR_COVERAGE_PCT < 70% OR ≥2 B-IDs missing individually even if global ≥70 → list missing B-IDs + expected test file(s) by keyword mapping. (S15 threshold <70%)
    • 🟢 FULLY LINKED: ≥70% AND <2 individual missing B-IDs → green. §7.1 bonus score applied if ≥90%. (S15 thresholds ≥70% + ≥90% bonus)

3.5.2 Bilateral 2/3 — SPEC §4.4 Mermaid B-ID refs set vs §4.2 real Behavior Table set (diagrams don't lie)

Common error in long specs: diagram gained B-11 but table only goes up to B-10 (orphan) OR table has B-3/B-4 but no diagram references even with mermaid_required=true.

Procedure:

  1. Build sets:
    • MERMAID_REF_SET = SBE_MERMAID_BIDS ∩ B (B- only, ignore AB- for this check)
    • TABLE_B_SET = { SBE_BEHAVIORS[].b_id }
  2. Bidirectional diff:
    • Orphan refs (diagram has, table DOES NOT): MERMAID_REF_SET \ TABLE_B_SET → WARN if non-empty. E.g.: "B-11, B-12 appear in sequenceDiagram but DO NOT exist in §4.2 Behavior Table — remove from diagram or add rows to table."
    • Missing diagram refs (table has ≥2, NO diagram references): (TABLE_B_SET \ MERMAID_REF_SET) ≥ 2 ENTRIES AND mermaid_required === true (frontmatter) → WARN. E.g.: "mermaid_required=true but table B-2, B-5, B-7 DO NOT appear in any of the 3 §4.4 diagrams — label nodes/edges with B-X to ensure bilaterality."
  3. AB-IDs anti-behaviour in diagram is OPTIONAL by default; if frontmatter ab_count ≥ 3 AND erd_required=true → soft WARN if 0 AB- in ERD (no block).

3.5.3 Bilateral 3/3 — SPEC §4.4.3 ERDiagram ↔ Migration SQL + real TypeORM @Entity

Hard gate only applies IF: erd_required === true (frontmatter §0.4) AND diff has NEW/MODIFIED migration files (**/migrations/*.sql, **/migrations/*.ts) OR entity files (*.entity.ts, @Entity()). Otherwise → ⚪ N/A marked in summary.

Cross-check procedure:

  1. Does entity declared in ERD exist in code? each entity name in erDiagram → grep @Entity.*<name> in diff. Missing → WARN "Entity <X> declared in ERD but TypeORM @Entity NOT found in diff."
  2. Do FK cardinality + ON DELETE rule match? each FK in ERD (Mermaid cardinality }|/o| etc.) → cross-check with migration SQL REFERENCES <target>(id) ON DELETE <CASCADE|SET NULL|RESTRICT|NO ACTION> and with TypeORM @ManyToOne({ onDelete: "CASCADE" }). Cardinality or onDelete mismatch → WARN "ERD cardinality Order }|--|{ OrderItem differs from migration ON DELETE RESTRICT (CASCADE expected due to strong 1:N cardinality)."
  3. Do ERD declared constraints ≥3 really exist? if ERD lists fields with UK/CHECK/UNIQUE explicitly → grep ADD CONSTRAINT <name> UNIQUE or UNIQUE(col, col2) or @Index({ unique: true }) in diff. ≥1 constraint declared in ERD but missing in schema → WARN "UK uk_order_stripe_pi_unique exists in ERD but migration has no ADD CONSTRAINT nor unique @Index."
  4. Severity: All ERD findings = 🟡 non-blocking WARN by default (ERD is often "target state" and migration incremental). If ≥3 mismatches + high risk (wrong FK ON DELETE in payments/tickets table) → mark as upgrade: 🔴 BLOCK if 1 of the mismatches is FK cardinality of strong referential integrity (e.g. Order 1:N Ticket but ON DELETE CASCADE in Ticket would delete tickets when deleting Order — GDPR violation).

§3.5 Report format — SbE Bilateral Extension (new, RULE 7.9)

Anchor ID Behavioural rule Verdict Evidence + Action item if 🟡/🔴
SBE-B1 bilateral_anchor_coverage_for_spec_Behaviors_test_files 🟢 FULLY LINKED 9/10 B-IDs have // @ac B-X on 1st line inside it() block. Coverage=90%. Files: refundFlow.api.test.ts + RefundService.unit.test.ts. +0.5 §7.1 SCOPE_score bonus applied.
SBE-B2 bilateral_mermaid_bid_refs_real_table_behaviour 🟡 WARN ORPHAN Orphan refs: B-11 exists in §4.4.1 sequenceDiagram but has NO corresponding entry in Behavior Table. Add B-11 to table OR remove from diagram. Missing refs count = 0 (ok, mermaid_required=true).
SBE-B3 bilateral_erd_cardinalities_constraints_migration_and_typeorm 🟡 WARN MISMATCH ERD `OrderItem → Order }
SBE-B2-alt (0% example — hard block) 🔴 BLOCK ZERO ANCHORS 0/7 B-IDs have bilateral anchor. NO test file modified in this diff contains // @ac B-X pattern. Resolve: add anchors OR justified EXPLICIT_OVERRIDE_BILATERAL_SKIP + decision.log entry.

4. CHECK 3 — 📘 UPDATED Documentation (AGENTS / README / runbooks / CLAUDE)

4.0 MANDATORY Pre-check (BEFORE using heuristics) — Relevance + Docstrings

This stage is NOT optional. Run on EVERY diff, even small ones.

Mandatory Item Question to answer (verification) Verdict Evidence
Relevance Check 1 (engineering-contracts §22.1) "Does this change alter public contract, new or changed commands, UX/UI, onboarding, architectural premises, deploy/runbook flows, or public APIs?" If YES → docs are mandatory. If NO → justify 1 line if diff >5 files or >150 lines. 🟢 Answered Justification line in decisions.log OR marked YES/NO in report
Relevance Check 2 (engineering-contracts §22.1) "Does a human or agent reading this code in 3 months benefit from an explanation?" If MAYBE or YES → docs are mandatory. 🟢 Answered Decision recorded in report
Public Docstrings (engineering-contracts §22.2) Did diff add or change public functions/methods/classes, modules, difficult custom types? Does each new item have docstring/JSDoc/TSDoc with PURPOSE + intricate observations (NOT inputs/outputs if typed)? Intricate private functions should also have them. 🟢 FULL / 🟡 PARTIAL / 🔴 ZERO List files/fns missing docstring

HARD FAIL: If the 3 items above are NOT explicitly verified, CHECK 3 cannot be marked 🟢 under any circumstances.

4.1 Trigger heuristics (WHEN to document — after 4.0 pre-check)

Change in diff MANDATORY document to update
New /commands/che-*.md or skills/*/SKILL.md README.md §5 commands table (count + new line) + top banner count. Optional: §6 cheatsheet if daily command.
New architecture premise, new hook, new contract App or repo-level AGENTS.md, CHE_RULES.md if cross-cutting, CLAUDE.md
New environment variable (see also CHECK 4) .env.example + README "env vars required" section + app-level config doc
New public endpoint / public API route / breaking change Package README, docs/api/, OpenAPI/Swagger if any
Runbook changed, deploy command changed, new CI step .github/workflows/*.yml comments, docs/runbook-*.md if any
Important architecture refactor App-level AGENTS.md + decision log docs/decisions.md if any

4.2 Step 4.2 — Cross-check .md/.yml diff against triggers

From cumulative patch:

  1. List all changed .md, .yml, .yaml, .json schema, .toml config
  2. For EACH applicable trigger, mark:
    • 🟢 DOCUMENTED if corresponding file appeared in diff and changed content matches trigger keyword
    • 🟡 PARTIAL if documented in only one place but missing another (e.g.: new skill was README §5 but missing top banner count)
    • 🔴 NOT DOCUMENTED if trigger applied and no doc was touched

Step 4.3 — Report format, RULE 7.9 names

Trigger / Item Behavioural rule Verdict Evidence doc path
New /che-scope-check command added documentation_update_for_che_scope_check_command_in_readme_and_count 🟢 DOCUMENTED README.md#L244-L267 §5 table line 18 + top banner updated count 17→18
New STRIPE_CONNECT_SECRET env var (CHECK 4) documentation_update_for_stripe_connect_secret_env_var_in_dotenv_example_and_parser 🟡 PARTIAL .env.example has the var but packages/config/src/env.ts zod schema DID NOT validate type (string required)
New offline scanner architecture documentation_update_for_offline_architecture_in_agents_md_and_claude_md 🔴 NOT DOCUMENTED Diff changes 12 offline scanner files. packages/scanner/AGENTS.md + CLAUDE.md WITHOUT changes. Add §scanner offline architecture.

5. CHECK 4 — 🔐 New environment variables = DECLARED in INFRA/ENV parser

5.1 Detect NEW env var usage in diff

Regex patterns (all languages, case-insensitive match whole words):

process\.env\.[A-Z0-9_]+
Deno\.env\.get\(["']([A-Z0-9_]+)
os\.environ\[["']([A-Z0-9_]+)
os\.getenv\(["']?([A-Z0-9_]+)
ENV\["?([A-Z0-9_]+)"?\]
env\(["']([A-Z0-9_]+)
z\.object\(\{\s*([A-Z0-9_]+)

Produces ENV_USAGE[] = {var: string, file: path, line: n, lang: ts|py|rs|go|sh}.

5.2 Cross-check with DECLARATIONS

Search in ENTIRE REPO (not just diff) for declarations of each ENV_USAGE[i]:

Declaration type Where to search
Zod schema env parser packages/config/src/env.ts, env.ts, config/env.ts, src/env/index.ts, app/env.ts, next.config env
.env.example, .env.local.sample, .env.dist repo root, apps/, packages/
Vercel (if project uses) vercel.json env keys, OR Railway/Railway.tf
Terraform / AWS env *.tf environment blocks, SSM parameter store names
Docker / K8s Dockerfile ENV, k8s ConfigMap, helm values.yaml
CI GitHub Actions .github/workflows/*.yml env blocks if CI only var

Each NEW env var vs diff marked:

  • 🟢 DECLARED: appears in ≥1 declaration AND (if zod schema) has validated type (z.string().min(1), z.number(), etc.)
  • 🟡 WEAK DECLARATION: appears in .env.example BUT NOT in zod schema parser (no runtime validation). Or zod optional without default.
  • 🔴 NOT DECLARED: No declaration found in repo. Point out: which var, what expected type, where to add (both packages/config/src/env.ts + .env.example)

Step 5.3 — RULE 7.9 report format

Variable Behavioural rule Verdict Where to declare (if 🔴/🟡)
ANALYTICS_S3_BUCKET env_var_declaration_in_parser_for_analytics_s3_bucket 🟢 DECLARED packages/config/src/env.ts z.string() + .env.example line 42
STRIPE_CONNECT_SECRET env_var_declaration_in_parser_for_stripe_connect_secret 🟡 MISSING RUNTIME VALIDATION .env.example line 37 OK. Missing packages/config/src/env.ts zod entry + default throw if absent in prod
ETL_SENTRY_DSN env_var_declaration_in_parser_for_etl_sentry_dsn 🔴 NOT DECLARED Used in etl/ingest.ts#L18 without declaration. Add in packages/config env schema zod.string().url() + .env.example.

6. CHECK 5 — 🧩 LEAN / KISS / YAGNI — Overengineering Scanner (13 generic categories L1-L13 + 13 Ousterhout RED FLAGS Appendix D)

New pillar introduced 2026-09. Combats LLM overengineering by default. Every line of new code must justify its existence against the explicit scope of the diff. NOT "clean code personal taste"; it is YAGNI + blast-radius reduction + reuse-before-create from engineering-contracts §1 §4.

Simplicity Bias (engineering-contracts §1 — the touchstone): also weigh reversibility (cheap to change or undo) and observability (cheap to see in production). A solution that is complete but hard to reverse, or blind at the seam it touches, is a finding even when it has no unused abstraction (L13).

Ousterhout integration (APoSD canonical Appendix D): After running the 13 categories L1-L13, also apply the 13 RED FLAGS from Appendix D (D.1). Same finding format with same AC scope justifier downgrade. Default severity in scope-checker: HIGH (RF01-RF04), MEDIUM (RF05-RF13). Cross-reference with che-code-review findings in ship gate.

6.0 Pre-step — Automatic scope justifier (downgrade severity when abstraction is requested in scope)

Before applying the 13 categories, build:

  • SET_AC_SCOPED_KEYWORDS: all behavioural keywords from CHECK 1 ACs that mention "extensibility / multiple backends / strategy / abstract X / replace Y with Z in the future" / items that EXPLICITLY request flexibility.
  • For each L1-L13 finding:
    • IF finding matches ANY keyword in SET_AC_SCOPED_KEYWORDS → DOWNGRADE 1 level of severity AUTOMATICALLY (HIGH→MEDIUM, MEDIUM→LOW, LOW→INFO allowlisted in report). The abstraction was requested in the scope; it is not overengineering.
    • IF NOT matched → original severity.

6.1 Procedure per category — 13 mandatory checks

For EACH category below, apply steps on the cumulative diff (NEW + MODIFIED files, NOT entire repo).

ID Trigger (regex / heuristic) Default severity Detection procedure
L1 Premature abstraction: Interface / abstract class with only 1 implementation MEDIUM (HIGH if > 5 total indirections in same flow) 1. List all new/modified interfaces: interface\s+\w+ / abstract class\s+\w+. 2. For each, grep implementations: implements\s+<InterfaceName> / extends\s+<AbstractName>. 3. IF implementation count = 1 AND NOT an existing interface in repo history → flag L1. 4. Record: indirection path in call chain; if > 5 total interface/abstract hops → upgrade severity to HIGH.
L2 Strategy / Factory / Dispatcher pattern with only 1 entry in switch/map MEDIUM 1. Find NEW switch/case, Record<Enum, Handler>, Map<string, () => R>. 2. Count effective entries (non-default cases / non-empty keys). 3. IF count = 1 AND NO TODO/FIXME attaching "next we add second strategy" → flag L2.
L3 Wrapper/builder around lib with 1 method and zero extra logic LOW (MEDIUM if > 3 wrapper files in same diff) 1. Find NEW classes/funcs that only call lib deps directly: method body = just return lib.f(args) without validation/cache/retry/error mapping. 2. Name contains "Factory", "Wrapper", "Client", "Provider" but without extra implementation. 3. IF ≥ 3 of these in same diff → upgrade to MEDIUM.
L4 createX() factory with body = 1 line return new ConcreteX() without any if/switch LOW 1. Grep function\s+create\w+\s*\([^)]*\)\s*\{ / static\s+create\w+\s*\(. 2. Body AST/syntax = return new <ConcreteClass>(same params without change). 3. Zero conditionals, zero fallback, zero cache. → flag L4.
L5 Helper/utility with 1 SINGLE use in entire codebase LOW (MEDIUM if > 20 helper lines) 1. For each new EXPORTED function/const in utils.*, helpers.*, *util*: grep name. 2. Occurrence count = 2 (declaration + 1 use) OR 1 if default export. 3. IF body > 20 lines → upgrade to MEDIUM.
L6 Env VAR declared in .env.example BUT NEVER read in code with process.env etc HIGH if credential/secret; MEDIUM if feature flag/toggle 1. List new vars in .env.example in diff. 2. For each VAR: grep process.env.<VAR> / Deno.env.get / os.environ / ENV[var] in ENTIRE REPO (not just diff, as it might be used in unchanged file). 3. ZERO matches → flag L6. Credentials = name contains (KEY/SECRET/TOKEN/DSN/PASSWORD/AUTH) → HIGH; rest MEDIUM.
L7 React useHook/custom component ≤ 2 lines, called once LOW 1. NEW React hooks: function use\w+ → body lines ≤ 2. 2. NEW Component: export default function \w+ with JSX ≤ 2 lines and no children/props beyond hardcode. 3. Grep name finds exactly 1 call site (outside declaration file). → flag L7.
L8 Generic <T> / Type parameter used for only 1 concrete type in all call sites LOW 1. Find NEW function\s+\w+\s*<T[^>]*> / class\s+\w+\s*<T[^>]*>. 2. Grep all call sites in diff + entire repo. 3. All pass EXACT SAME type (e.g.: all invoke<Refund> without any other variation). → flag L8.
L9 Chain ≥ 3 indirection hops without real value (X → Y → Z → real db/network op) MEDIUM (HIGH if 1 hop has lock tx held over network — che-code-review Category 0.3 cross-ref) 1. For each public entrypoint (router handler / tRPC procedure / controller): trace call chain to real side effect (DB read/write / HTTP / FS). 2. ≥ 3 functions/class.methods in middle that ONLY pass args (zero validation/transform/branching). 3. If any stage has queryRunner START TRANSACTION FOR UPDATE not yet released and hop does await fetch/stripe → upgrade HIGH (same C0.3 code-review finding; linked). → flag L9.
L10 Dead code comment-out / // TODO without #ticket number / FIXME without reference MEDIUM if TODO/FIXME without ticket; LOW for commented dead code 1. Regex /\/\/\s*TODO(?!\s*[:(]?\s*[A-Z]{2,}-?\d+)/ (TODO without ticket). 2. Regex \/\*[\s\S]*?\*\/ commented blocks with syntactically valid code (not docstring). 3. Commented blocks + TODO without id → flag L10.
L11 Function parameter that ALL diff call sites pass the SAME hardcoded value MEDIUM 1. For each new/modified exported function: list params. 2. For each non-trivial param that is not last: grep all call sites in diff. 3. 100% of calls pass exact same literal value (e.g.: all fn(..., "gbp")). 4. No call site uses another value. → flag L11.
L12 Lookup table / Record / Config table with only 1 ENTRY LOW (except if 1 entry + >30 lines total block → MEDIUM) 1. Regex =\s*\{\s*\w+\s*:\s* + close brace in < 5 lines AFTER → only 1 key. 2. Record<K,V> + initialization only 1 key. 3. No other key added in other diff files. → flag L12.
L13 Premature completeness: edge-case / error-branch / validation / fallback handling with NO current AC demand (Simplicity Bias, engineering-contracts §1) MEDIUM (HIGH if it adds a new dependency or module to support a case no AC requires) 1. For each NEW branch / try-catch / validation / fallback / extra param in the diff, check if ANY CHECK 1 AC demands it. 2. If NONE demands it AND the SPEC §1 Non-goals lists it as deferred by design → flag L13. 3. Remedy = defer + record, do NOT implement.

6.2 CHECK 5 report format — 4-column RULE 7.9 table

ID Behavioural rule (verb_object_for_target) Severity (after scope downgrade) Evidence (path:lines) + Scope justifier if applied
L1 overengineering_interface_with_only_1_implementation_refund_repository MEDIUM RefundRepository.ts#L5-L30 IRefundRepository. No AC requests multiple backends.
L6 env_var_declared_without_usage_refund_timeout_ms MEDIUM .env.example#L41 REFUND_TIMEOUT_MS=3000. ZERO process.env.REFUND_TIMEOUT_MS occurrences in code.
L11 parameter_same_value_all_calls_currency_refund INFO allowlisted refundService.ts#L18 issueRefund(currency). Scope AC-1 said "single GBP currency for now". LOW→INFO downgrade applied.

7. CHECK 6 — 🧮 FINAL SCORE 0-10 (geometric mean of Scope × Lean)

Canonical blocking gate used by che-ship §0.9.1. Combines delivery and lean quality into a comparable number.

7.1 SCOPE sub-score calculation (0-10) — per S11 CHE_RULES §X

Use CHECK 1 table verdicts (formula and weights per S11 canonical):

TOTAL_ACs          = (DELIVERED+PARTIAL+MISSING)   (OOS NOT counted)
DELIVERED_weighted = count(🟢 DELIVERED)               × 1.0   (per S11)
PARTIAL_weighted   = count(🟡 PARTIAL)                 × 0.5   (per S11)
SCOPE_score = 10 × (DELIVERED_weighted + PARTIAL_weighted) / max(1, TOTAL_ACs)   (per S11 base)

⚡ SbE Bilateral Anchor Coverage adjustment (per S11 bilateral + S15 CHE_RULES §X — only applies if SBE_EXTENSION_ENABLED=true):

BILATERAL_ANCHOR_COVERAGE_PCT = (B_COVERED_BY_ANCHOR ÷ max(1, B_COUNT_SPEC)) × 100
if (BILATERAL_ANCHOR_COVERAGE_PCT >= 90):                                      # per S11 / S15 bonus 90%
    SCOPE_score = clamp(SCOPE_score + 0.5, 0, 10)
elif (BILATERAL_ANCHOR_COVERAGE_PCT < 70 AND BILATERAL_ANCHOR_COVERAGE_PCT > 0):  # per S11 / S15 penalty <70%
    SCOPE_score = clamp(SCOPE_score - 1.0, 0, 10)
# 0% anchors = BLOCK regardless of score (§8.1 verdict has 🔴 check item — S15)

Example (per S11): 8🟢 + 1🟡 + 1🔴 → base SCOPE = 10 × (8 + 0.5)/10 = 8.5 With 92% bilateral anchor coverage (SbE ON, per S15 bonus) → adjusted SCOPE = clamp(8.5 + 0.5, 0, 10) = 9.0

7.2 LEAN sub-score calculation (0-10) — per S12 CHE_RULES §X

Use CHECK 5 findings severities + NEW PREAMBLE TRIO (CANONICAL #0/#1/#2) flags. All weights, caps, trio penalties and clamp formula per S12 canonical:

LEAN_penalty =
    (count(🔴 HIGH_check5)   × 2)         # per S12 HIGH weight
  + (count(🟡 MEDIUM_check5) × 1)         # per S12 MEDIUM weight
  + (count(🔵 LOW_check5)    × 0.3)       # per S12 LOW weight
  + (5 if (HORIZONTAL_PLAN_DETECTED === true AND HORIZONTAL_OVERRIDE_LOGGED === false) else 0)   # per S12 HOR +5
  + (min(ENTROPY_DELTA, 5) if (ENTROPY_DELTA > 2 AND ENTROPY_OVERRIDE_LOGGED === false) else 0)  # per S12 ENT cap MIN(delta,5)
  + (4 if (WRAPPER_LEAKS_COUNT > 0 AND WRAPPER_OVERRIDE_OR_ANNOTATED === false) else 0)          # per S12 WRAP +4
LEAN_score = clamp(10 − LEAN_penalty ÷ 2, 0, 10)   # per S12 formula

Where (see S12 rationale CHE_RULES §X):

  • HORIZONTAL_PLAN_DETECTED = CHECK#0 §0.5 detector boolean. Override = EXPLICIT_OVERRIDE_HORIZONTAL_PLAN literal OR decisions.log event. Penalty ÷2 = ~2.5 pts.
  • ENTROPY_DELTA = CHECK#1 (§0.6) computed value. Override = EXPLICIT_OVERRIDE_ENTROPY_DELTA OR decisions.log event. Capped at +5 penalty (per S12 MIN(ENT,5)) — ÷2 scaled = max 2.5 LEAN hit (see S12 rationale: avoids killing a huge legacy lint batch).
  • WRAPPER_LEAKS_COUNT = CHECK#2 (§0.7) total leaks without TECHNICAL COUPLING annotation OR override literal. Penalty 4 (per S12) ÷ 2 = 2.0 LEAN points (S12 rationale: drops 7.5 Acceptable → 5.7 Attention).
  • S12 Combined trio rationale: horizontal (2.5) + entropy high (0.5-2.5) + wrapper leak (~2.0). Alone each is Attention-level; combined trio ensures the only way to get Excellent ≥9.0 is: clean vertical F0 + ≤+2 entropy + zero wrapper leaks (no broken windows).

Examples (per S12):

  • 1 HIGH + 5 MEDIUM + 7 LOW → penalty = 2 + 5 + 2.1 = 9.1 ÷ 2 = 4.55 → LEAN = 5.45
  • WITH horizontal plan + entropy 3.5 + 2 wrapper leaks (all un-overridden): 9.1 + 5 + 3.5 + 4 = 21.6 ÷ 2 = 10.8 → LEAN clamp = 0 (🔴 BLOCK via score <5.0)
  • CLEAN TRIO (horizontal OK, entropy 0, wrapper leaks 0): same 9.1 (check5 only) → LEAN = 5.45 (Attention because of legacy check5 findings; no new canonicals penalty added — S12 correct behaviour).

7.3 FINAL Score (geometric mean — per S13 CHE_RULES §X — requires BOTH to be good)

FINAL_score = sqrt(SCOPE_score × LEAN_score)   # per S13 (geometric — anti-loophole)

Example (S13 rationale in CHE_RULES §X): sqrt(8.5 × 5.45) = sqrt(46.3) = 6.80

7.4 FINAL CLASSIFICATION rule used in gate — per S13 CHE_RULES §X

FINAL_score threshold (per S13) Level che-ship §0.9 Action
≥ 9.0 (S13 Excellent band) Excellent Green + auto-proceed
≥ 7.0 (S13 Acceptable band — DEFAULT) Acceptable Green + auto-proceed (DEFAULT THRESHOLD)
5.0 – 6.9 (S13 Attention band) Attention 🟡 CONDITIONS → show L-M action items → ask user to proceed?
< 5.0 (S13 Poor band — HARD BLOCK) Poor 🔴 BLOCK SHIP → fix first

(See S13 DESIGN RATIONALE CHE_RULES §X: arithmetic (10+3)/2=6.5 passes → geometric SQRT(30)=5.47 barely = intentional anti-loophole.)

In addition to the numerical score, IF there is ANY 🔴 item in ANY of the 4 legacy checks (1-4), the final verdict automatically drops to 🔴 BLOCKED, regardless of the score. This is the old §6.1 rule, preserved.


8. 🎯 Final Verdict + aggregated report (UPDATED 2026-09 for 6 checks)

8.1 Calculation rule

Verdict =                                                              # thresholds per S13 CHE_RULES §X
  🔴 BLOCKED  if (ANY check has ≥1 🔴 item)  OR  (FINAL_score < 5.0)   # S13 Poor band <5.0
  🟡 CONDITIONS if (NO check has 🔴)  and  (ANY item has 🟡)  OR  (5.0 ≤ FINAL_score < 7.0)  # S13 Attention 5-6.9
  🟢 APPROVED if (FINAL_score ≥ 7.0) AND (ALL items are 🟢/⚪/INFO) AND (ZERO 🔴 items)  # S13 Acceptable ≥7.0 + Excellent ≥9.0

8.1 🔴 MANDATORY STORAGE PREFLIGHT (BEFORE WRITING REPORT)

engineering-contracts §20 MORATORIUM: No assets in worktree. Everything in che-sessions via unique helper.

Run EXACTLY this block BEFORE constructing any path:

# Resolve the `che` CLI — it owns the 5-tier Che-home cascade (CHE_HOME → HARNESS_HOME
# → ~/.che-ai → legacy ~/.trae iff CHE_RULES.md exists → ~/.che-ai fallback), identical
# to che_core/paths.py resolve_che_home. Skills MUST NOT reimplement this in bash.
command -v che >/dev/null 2>&1 || { echo "❌ FATAL: 'che' CLI not found on PATH. Zero writes without storage boundary. exit 98"; exit 98; }
SESSION_ID="${CHE_SESSION_ID:-${HARNESS_SESSION_ID:-fallback-scope-session}}"
if [ -n "${WORKTREE_ROOT:-}" ] && [ -d "$WORKTREE_ROOT" ]; then
  eval "$(che compute_paths "$WORKTREE_ROOT" "$SESSION_ID" --cwd "$PWD")"
  che ensure_dirs "$WORKTREE_ROOT" "$SESSION_ID" --cwd "$PWD"
fi

8.2 Output header + report saved at

Build path with helper — NEVER manual:

# SCOPE = workspace-shared (durable, reusable in future sessions of this worktree)
# related_id = review slug (e.g.: pr-382 or feat-FLO-714 or task-T1)
SCOPE_CHECK_PATH="$(che output_path "scope_check" "scope-check" "<related_id>" "workspace" "md")"

Example result: $CHE_WORKSPACE_SHARED/scope_check/pr-382/20260902-140000-scope-check.md → UTC timestamp in prefix = automatic sorting; related_id groups all scope-checks for the same entity.

First page of report (always at the TOP):

# 🔍 Scope Check — <slug>

## 0. Meta
- **Scope source:** (PRD path / ticket URL / task-graph / scope free-text / PR body) — pick all that were used
- **Mode:** A=GitHub PR #<id> (url) | B=Local Worktree <path> vs base <branch>
- **Diff:** N files changed / +X additions / -Y deletions
- **Final Score 0-10:** `<FINAL>` (SCOPE: `<SCOPE>` · LEAN: `<LEAN>`)

## 1. SUMMARY Verdict 6+1 checks (SbE extension added ONDA2)

| # | Check | 🟢 | 🟡 | 🔴 | ⚪ |
|---|---|---|---|---|---|
| 1 | 🔍 Full scope delivery | 8 | 1 | 1 | 2 OOS |
| 2 | 🧪 Unit/e2e test coverage | 6 | 2 | 1 | 0 |
| 2-ext | ⚡ Bilateral SbE (anchors + diagrams + ERD) | 2 rows ok | 1 ERD mismatch | 0 | 1 N/A erd_required=false |
| 3 | 📘 Updated docs | 3 | 1 | 0 | 5 N/A |
| 4 | 🔐 Declared new env vars | 1 | 1 | 1 | 0 |
| 5 | 🧩 Lean/YAGNI Overengineering | — | 5 L (MED) | 1 H (L6) | 7 allowlisted |
| 6 | 🧮 Final Score 0-10 | **6.80** | 7.0 threshold | — | — |

**👉 Final Verdict:** 🔴 BLOCKED / 🟡 CONDITIONS / 🟢 APPROVED

## 2. Action items (sorted 🔴 first)
1. 🔴 [Check 1, AC-3] Deliver offline qrcode scanner in scanner/lib/scan.ts (#L40-L120 expected)
2. 🔴 [Check 2, AC-3] Create scanner.spec.ts for offline cache fallback case
3. 🔴 [Check 4] Declare ETL_SENTRY_DSN in packages/config zod + .env.example
4. 🔴 [Check 5 L6] Remove REFUND_TIMEOUT_MS env from .env.example or add real code usage
5. 🟡 [Check 1, AC-2] Add destination stripe connect API call in refund.ts
6. 🟡 [Check 3] Validate STRIPE_CONNECT_SECRET zod runtime in env parser
...

↓ Details of each check in §2..§7 (4-column tables, RULE 7.9 names)

At the end of the report: how to fix quickly for next audit to pass (1-2 commands or 1-2 files).


7. RULE 7.9 NAMING (enforced throughout report)

NOT allowed ANYWHERE in the report:

  • ❌ good_quality, works, well_implemented, sufficient_coverage
  • ❌ AC-FLO-732-delivered, §4.2 reviewed, FLO-513 passing
  • ✅ MANDATORY: <verb_object>_for_<behavioural_target> in ALL rules of the 4 check tables.

E.g.:

full_scope_delivery_for_<ac_slug>
unit_or_e2e_test_coverage_for_<behaviour>
documentation_update_for_<change>_in_<doc>
env_var_declaration_in_parser_for_<VAR_NAME>