shapeup-sdlc 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/.claude/settings.local.example.json +20 -0
  2. package/.claude-plugin/marketplace.json +16 -0
  3. package/.claude-plugin/plugin.json +18 -0
  4. package/.env.shapeup.example +14 -0
  5. package/AGENTS.md +133 -0
  6. package/LICENSE +21 -0
  7. package/README.md +362 -0
  8. package/SECURITY.md +72 -0
  9. package/bin/init.mjs +329 -0
  10. package/commands/build.md +14 -0
  11. package/commands/eval.md +15 -0
  12. package/commands/hammer.md +12 -0
  13. package/commands/orient.md +11 -0
  14. package/commands/qa.md +12 -0
  15. package/commands/retro.md +13 -0
  16. package/commands/scopes.md +14 -0
  17. package/commands/shape.md +12 -0
  18. package/commands/ship.md +53 -0
  19. package/commands/wire.md +11 -0
  20. package/hooks/anti-rationalization.mjs +244 -0
  21. package/hooks/compact-snapshot.mjs +47 -0
  22. package/hooks/gate-deadline.mjs +151 -0
  23. package/hooks/gate-intake.mjs +110 -0
  24. package/hooks/gate-l2.mjs +161 -0
  25. package/hooks/gate-zerowork.mjs +264 -0
  26. package/hooks/hooks.json +118 -0
  27. package/hooks/lib/decision.mjs +183 -0
  28. package/hooks/safety-spine.mjs +296 -0
  29. package/hooks/sandbox-guard.mjs +172 -0
  30. package/hooks/session-rehydrate.mjs +109 -0
  31. package/hooks/slop-cleaner.mjs +176 -0
  32. package/oracles/_shared.mjs +46 -0
  33. package/oracles/http-oracle.mjs +155 -0
  34. package/oracles/index.mjs +36 -0
  35. package/oracles/process-oracle.mjs +146 -0
  36. package/oracles/snapshot-oracle.mjs +119 -0
  37. package/oracles/test-oracle.mjs +138 -0
  38. package/package.json +49 -0
  39. package/skills/advisor-protocol/SKILL.md +171 -0
  40. package/skills/ba-pitch-analyzer/SKILL.md +175 -0
  41. package/skills/ba-pitch-analyzer/assets/templates/_index.tmpl.md +84 -0
  42. package/skills/ba-pitch-analyzer/assets/templates/api-feasibility.tmpl.md +65 -0
  43. package/skills/ba-pitch-analyzer/assets/templates/assess-report.tmpl.md +127 -0
  44. package/skills/ba-pitch-analyzer/assets/templates/contracts/be-service.contract.tmpl.md +62 -0
  45. package/skills/ba-pitch-analyzer/assets/templates/contracts/offline-storage.contract.tmpl.md +92 -0
  46. package/skills/ba-pitch-analyzer/assets/templates/contracts/third-party-api.contract.tmpl.md +66 -0
  47. package/skills/ba-pitch-analyzer/assets/templates/cross-context/context-map.tmpl.md +64 -0
  48. package/skills/ba-pitch-analyzer/assets/templates/cross-context/event-choreography.tmpl.md +77 -0
  49. package/skills/ba-pitch-analyzer/assets/templates/cross-context/migration-plan.tmpl.md +104 -0
  50. package/skills/ba-pitch-analyzer/assets/templates/cross-context/team-handoff.tmpl.md +79 -0
  51. package/skills/ba-pitch-analyzer/assets/templates/domain-model.tmpl.md +79 -0
  52. package/skills/ba-pitch-analyzer/assets/templates/feedback.tmpl.md +87 -0
  53. package/skills/ba-pitch-analyzer/assets/templates/integration.tmpl.md +64 -0
  54. package/skills/ba-pitch-analyzer/assets/templates/run-state.tmpl.md +77 -0
  55. package/skills/ba-pitch-analyzer/assets/templates/scope-summary.tmpl.md +105 -0
  56. package/skills/ba-pitch-analyzer/assets/templates/synthesis.tmpl.md +215 -0
  57. package/skills/ba-pitch-analyzer/assets/templates/task-board.tmpl.md +35 -0
  58. package/skills/ba-pitch-analyzer/assets/templates/task-spike.tmpl.md +86 -0
  59. package/skills/ba-pitch-analyzer/assets/templates/task.tmpl.md +84 -0
  60. package/skills/ba-pitch-analyzer/assets/templates/usecase.tmpl.md +105 -0
  61. package/skills/ba-pitch-analyzer/assets/templates/ux-behavior.tmpl.md +65 -0
  62. package/skills/ba-pitch-analyzer/references/contract-patterns.md +152 -0
  63. package/skills/ba-pitch-analyzer/references/ddd-patterns.md +173 -0
  64. package/skills/ba-pitch-analyzer/references/doc-schemas.md +275 -0
  65. package/skills/ba-pitch-analyzer/references/integration-analysis.md +152 -0
  66. package/skills/ba-pitch-analyzer/references/task-generation.md +623 -0
  67. package/skills/ba-pitch-analyzer/references/test-surface.md +102 -0
  68. package/skills/ba-pitch-analyzer/references/ux-behavior-patterns.md +269 -0
  69. package/skills/ba-pitch-analyzer/scripts/board-derive.mjs +241 -0
  70. package/skills/ba-pitch-analyzer/scripts/spec-lint.mjs +239 -0
  71. package/skills/coach/SKILL.md +202 -0
  72. package/skills/orient/SKILL.md +262 -0
  73. package/skills/qa-edge-hunter/SKILL.md +369 -0
  74. package/skills/scope-architect/SKILL.md +111 -0
  75. package/skills/scope-hammer/SKILL.md +186 -0
  76. package/skills/shapeup/SKILL.md +403 -0
  77. package/skills/shapeup/resources/breadboard-reflection.md +88 -0
  78. package/skills/shapeup/resources/breadboarding.md +334 -0
  79. package/skills/shapeup/resources/context-compaction.md +240 -0
  80. package/skills/shapeup/resources/framing-doc.md +92 -0
  81. package/skills/shapeup/resources/kickoff-doc.md +95 -0
  82. package/skills/shapeup/resources/shaping.md +194 -0
  83. package/skills/shapeup/resources/spike.md +86 -0
  84. package/skills/solution-architect/SKILL.md +129 -0
  85. package/skills/spec-evaluator/README.md +93 -0
  86. package/skills/spec-evaluator/SKILL.md +213 -0
  87. package/skills/spec-evaluator/references/anti-leniency.md +50 -0
  88. package/skills/spec-evaluator/references/dimension-contract.md +126 -0
  89. package/skills/spec-evaluator/references/dimensions/_registry.md +40 -0
  90. package/skills/spec-evaluator/references/dimensions/completeness.md +102 -0
  91. package/skills/spec-evaluator/references/dimensions/integration.md +129 -0
  92. package/skills/spec-evaluator/references/dimensions/performance.md +48 -0
  93. package/skills/spec-evaluator/references/dimensions/security.md +60 -0
  94. package/skills/spec-evaluator/references/dimensions/spec-conformance.md +134 -0
  95. package/skills/spec-evaluator/references/dimensions/tdd-surface.md +110 -0
  96. package/skills/spec-evaluator/references/dimensions/test-surface-conformance.md +100 -0
  97. package/skills/spec-evaluator/references/probing.md +177 -0
  98. package/skills/spec-evaluator/references/report-schema.md +101 -0
  99. package/skills/spec-evaluator/references/verdict-ledger.md +92 -0
  100. package/skills/spec-evaluator/scripts/verdict-ledger.mjs +166 -0
  101. package/skills/task-executor/SKILL.md +194 -0
  102. package/skills/tech-lead/README.md +71 -0
  103. package/skills/tech-lead/SKILL.md +459 -0
  104. package/skills/tech-lead/references/delegation.md +254 -0
  105. package/skills/tech-lead/references/gates.md +379 -0
  106. package/skills/tech-lead/references/invocation.md +45 -0
  107. package/skills/tech-lead/references/ledger-schema.md +214 -0
  108. package/skills/tech-lead/references/round-protocol.md +184 -0
  109. package/skills/tech-lead/references/state-model.md +66 -0
  110. package/skills/tech-lead/references/tiny-lane.md +52 -0
  111. package/skills/tech-lead/schemas/domain.schema.json +2294 -0
  112. package/skills/tech-lead/schemas/gate-answers.schema.json +92 -0
  113. package/skills/tech-lead/schemas/work-order.schema.json +21 -0
  114. package/skills/tech-lead/schemas/work-result.schema.json +40 -0
  115. package/skills/tech-lead/scripts/aegis-digest.mjs +124 -0
  116. package/skills/tech-lead/scripts/budget-check.mjs +156 -0
  117. package/skills/tech-lead/scripts/compile-order.mjs +518 -0
  118. package/skills/tech-lead/scripts/fit-check.mjs +196 -0
  119. package/skills/tech-lead/scripts/gate-answers.mjs +338 -0
  120. package/skills/tech-lead/scripts/ingest-result.mjs +270 -0
  121. package/skills/tech-lead/scripts/init-run.mjs +326 -0
  122. package/skills/tech-lead/scripts/lib/argv.mjs +224 -0
  123. package/skills/tech-lead/scripts/lib/contract-md.mjs +481 -0
  124. package/skills/tech-lead/scripts/lib/is-main.mjs +82 -0
  125. package/skills/tech-lead/scripts/lib/paths.mjs +278 -0
  126. package/skills/tech-lead/scripts/lib/ratchet-tree.mjs +112 -0
  127. package/skills/tech-lead/scripts/run-snapshot.mjs +269 -0
  128. package/skills/tech-lead/scripts/ship-report.mjs +275 -0
  129. package/skills/tech-lead/scripts/stats.mjs +423 -0
  130. package/skills/tech-lead/scripts/t0-verify.mjs +470 -0
  131. package/skills/tech-lead/scripts/trace-lint.mjs +367 -0
  132. package/skills/tech-lead/scripts/validate-envelope.mjs +283 -0
  133. package/skills/translator/README.md +66 -0
  134. package/skills/translator/SKILL.md +258 -0
  135. package/skills/translator/references/preservation-rules.md +102 -0
@@ -0,0 +1,213 @@
1
+ ---
2
+ name: spec-evaluator
3
+ description: "Use this skill whenever the user wants to evaluate, QA, or verify that an implemented task actually matches its spec and acceptance criteria — the judge in a planner→generator→evaluator harness. Trigger on: \"evaluate task TASK-NNN\", \"QA TASK-NNN\", \"verify against spec\", \"check acceptance criteria\", \"does this match the spec\", \"grade this build\", \"run evaluator\", or a tech-lead --order dispatch. Skeptical by default — absence of evidence is a FAIL; probes the running app and files file:line bugs."
4
+ ---
5
+
6
+ # Spec Evaluator (the single judge, pure worker v1.0)
7
+
8
+ **Assume broken until proven working. Grade evidence, not claims. Return data, not writes.**
9
+
10
+ The **judge** in a planner → generator → evaluator loop. It reads the committed spec, exercises
11
+ the **running** app, and returns a hard-threshold verdict plus a file:line bug list — as a
12
+ WorkResult envelope the orchestrator ingests. The generator fixes; the evaluator re-runs.
13
+
14
+ **Core guarantee — skeptical by default.** Out-of-the-box an LLM is a lenient QA: it finds a
15
+ real defect, then talks itself into approving anyway. This skill inverts that posture. A
16
+ criterion with no collected evidence is a **FAIL**, never a pass-by-assumption.
17
+
18
+ > **Anti-leniency protocol** → `references/anti-leniency.md` — read before printing any verdict.
19
+ > **Verdict ledger (re-probe + confidence + flip detection)** → `references/verdict-ledger.md`.
20
+ > **Dimension contract (injection interface)** → `references/dimension-contract.md`.
21
+ > Where any reference file describes *writing* shared state (task files, `.verdicts` ledger,
22
+ > run-state), the pure-worker contract overrides it: that data returns in the WorkResult and
23
+ > the orchestrator's ingest script performs the write. Old gate names in reference files map
24
+ > 1:1 onto the core process below: GATE V0/V0.5 = input contract + dimension resolution,
25
+ > GATE V1 = CONTRACT, Phase A = PROBE, GATE V2 = VERDICT, Phase B = REPORT; GATE V3
26
+ > (sign-off) is retired — pausing is the caller's `interaction` policy.
27
+
28
+ ---
29
+
30
+ ## Input contract — the WorkOrder
31
+
32
+ Invoked as `--order <path>`. Fields you may rely on (absent = unknown, never inferred):
33
+
34
+ | Field | What it is |
35
+ |---|---|
36
+ | `payload.spec_folder` | The committed grading truth: `usecases/` + `domain-model.md` (+ `contracts/`, `scope-summary.md`, `_index.md`). No `usecases/` → HARD STOP, nothing to grade against |
37
+ | `payload.feature` | Feature slug — scopes the probe and names the report |
38
+ | `payload.dimensions[]` | The active dimension set (the caller resolved precedence). Absent → `[spec-conformance]` + the auto-enable rules below |
39
+ | `payload.run_cmd` | How to start the running app. Absent standalone → ask; absent orchestrated → ESCALATE, do not guess |
40
+ | `payload.t0_artifacts[]` | Per-scope T0 verdict paths for this round (scoped specs). An artifact listed but missing/red on disk, or a scoped spec with none listed → the round is NOT gradeable: return `status: failed` naming the scope — a structural precondition, not a criterion |
41
+ | `payload.browser` | `cli` (default, ~4x cheaper) \| `mcp` \| `none` |
42
+ | `payload.tasks[]` | Traceability only (which UCs a task claims): NEVER a grading source — the committed UC text is the criterion, a paraphrase mismatch is a finding |
43
+ | `substrate.allowed` | Your only write surface: `.shapeup/<slug>/evaluation/**` (the report + evidence) |
44
+
45
+ **Grading source of truth.** `spec-conformance` grades against the committed `usecases/UC-*.md`
46
+ (Steps, Error Cases, Invariants, Test Surface) and `domain-model.md` — never against a task
47
+ file's own AC paraphrase. Task boards are LOCAL, regenerable bookkeeping the judge never touches.
48
+
49
+ **Dimension resolution (craft, kept).** Base `[spec-conformance]` + always-on `tdd-surface` +
50
+ `integration` (`.be`/`.e2e`); auto-enable `completeness` when any UC has `## Invariants`,
51
+ `test-surface-conformance` when any UC has `## Test Surface`; an explicit `dimensions[]` list
52
+ overrides. Each active dimension's file must satisfy `references/dimension-contract.md` — a
53
+ half-formed dimension is SKIPPED with a warning, never run. Disabled dimensions are out of
54
+ scope; findings there are not graded (no silent widening).
55
+
56
+ ---
57
+
58
+ ## Core process
59
+
60
+ ```
61
+ CONTRACT extract every criterion from the committed spec; classify each probe
62
+ [cmd] | [ui] | [data] | [manual]; a [manual]/ambiguous criterion is a spec
63
+ defect to surface, never a silent pass
64
+ PROBE exercise the RUNNING app; collect evidence only — no grading yet
65
+ VERDICT grade each criterion vs its dimension's hard threshold, evidence-only;
66
+ re-probe every FAIL once; flips force confidence low
67
+ REPORT write EVAL report (your substrate) + return the WorkResult envelope
68
+ ```
69
+
70
+ **CONTRACT.** Criteria come from: UC `## Steps` / `## Error Cases` / `## Invariants` /
71
+ `## Test Surface` rows for every UC in scope; `domain-model.md` rules for touched aggregates;
72
+ the contract triplet (Request/Response/Error) for repository work; `scope-summary.md`
73
+ Done-when statements; `_index.md` Non-Go list. Which UCs are in scope comes from
74
+ `payload.tasks[]` traceability or, standalone, from the user (max 2 questions).
75
+
76
+ **PROBE (evidence, not grades)** — `references/probing.md`:
77
+ - `[cmd]`: run it, capture stdout/stderr + exit code.
78
+ - `[ui]`: drive the app (Playwright CLI preferred). **Affordance-only assertions**: with an
79
+ `affordance_manifest` in play, target `test_id`/`role` + `data-state` transitions — NEVER
80
+ color, font, spacing, or pixel position (Layer-3 is frozen; grading it would resurrect the
81
+ freeze through the judge). Ugly-but-correct PASSes; pretty-but-wrong-`data-state` FAILs.
82
+ - `[data]`: query the DB/storage, capture actual state.
83
+ - Contract work: send real requests, compare field-by-field.
84
+ - No evidence collected = recorded "NO EVIDENCE" → FAILs at verdict.
85
+
86
+ **VERDICT.**
87
+ - PASS only if Phase-probe evidence directly confirms; FAIL on defect evidence or no evidence.
88
+ - Re-probe every FAIL once before finalizing: agree → confidence high; disagree → keep FAIL
89
+ (no stable pass = FAIL), confidence low, note flaky.
90
+ - Read any existing `.verdicts-*.jsonl` (read-only) to detect flips vs prior runs — a flip
91
+ forces confidence low and a stability note. The new lines return in your envelope; ingest
92
+ appends them (never rewrite history).
93
+ - Dimension threshold from its file (spec-conformance: 100% of [cmd]/[ui]/[data] criteria +
94
+ contract triplet + Non-Go). Overall PASS only if ALL active dimensions pass — the halo
95
+ effect is banned; a strong dimension never lifts a failing one.
96
+ - **T0 citation (scoped specs).** Recompute each cited artifact's sha256 from disk — never
97
+ trust a handed hash. A verdict on a scoped spec without a T0 citation is structurally
98
+ invalid, regardless of how convincing your own probing looked; generator prose ("tests
99
+ pass", "verified locally") is never admissible evidence.
100
+
101
+ ---
102
+
103
+ ## Anti-rationalization table
104
+
105
+ | Excuse | Reality |
106
+ |---|---|
107
+ | "The code clearly implements it, no need to run it" | Apps that look right still break when used. Probe the running app. |
108
+ | "It failed, but the feature mostly works" | One FAIL fails the dimension. Thresholds are hard. |
109
+ | "The generator says tests pass" | Generator prose is not evidence. Your probe or the T0 artifact is. |
110
+ | "This criterion isn't really testable, count it as pass" | Untestable AC = spec defect → surface it; it blocks a clean PASS unless explicitly waived. |
111
+ | "The other dimensions are strong, round up" | Halo effect banned. Dimensions never average. |
112
+ | "The task file's checklist says done" | The checklist is the generator's paraphrase. Grade the committed UC text. |
113
+ | "Re-probing is a waste, the FAIL is obvious" | A single non-deterministic snapshot lies. Re-probe; report the flip honestly. |
114
+
115
+ ---
116
+
117
+ ## Output contract — the WorkResult
118
+
119
+ 1. Write the report `.shapeup/<slug>/evaluation/EVAL-FEATURE-<slug>.md` (or
120
+ `EVAL-<task_id>.md` for a per-task run) per `references/report-schema.md`: verdict,
121
+ per-dimension criteria table with confidence, stability block (flips), bug list (severity,
122
+ criterion, `file:line`, repro, expected vs actual), NEXT ACTION, and — scoped specs — the
123
+ T0 citations. A scoped report with no citation field is malformed; do not write it.
124
+ 2. Write `.shapeup/<slug>/results/<order-suffix>.json`:
125
+
126
+ ```json
127
+ {
128
+ "schema_version": 1,
129
+ "order_id": "<copied>",
130
+ "worker": "spec-evaluator",
131
+ "status": "done",
132
+ "verdict": {
133
+ "overall": "PASS | FAIL",
134
+ "report_path": ".shapeup/<slug>/evaluation/EVAL-FEATURE-<slug>.md",
135
+ "t0_citations": [ { "scope_id": "cart", "path": "…/t0/verdicts/r2-a3.json", "sha256": "…" } ],
136
+ "criteria": [ { "criterion": "UC-01 step 3", "dimension": "spec-conformance",
137
+ "verdict": "FAIL", "confidence": "high", "reprobed": true,
138
+ "evidence": "Pay click throws — apps/web/checkout/Pay.tsx:84" } ],
139
+ "refuted": [ { "task_id": "TASK-007", "ac": "<the checkbox text your evidence disproves>" } ],
140
+ "bugs": [ /* report-schema bug entries */ ]
141
+ }
142
+ }
143
+ ```
144
+
145
+ **Every FAIL criterion's `evidence` MUST carry a `file:line` locator** — schema-enforced, not
146
+ advice: `validate-envelope` rejects the whole result before ingest sees it. A PASS may cite plain
147
+ output. (Measured: a run returned a correct FAIL with `bugs: null` and no locator anywhere, which
148
+ is unactionable without re-investigating. The rule used to be repeated five times in this prompt
149
+ and enforced nowhere; it is now stated once and enforced by `domain.schema.json`.)
150
+
151
+ The orchestrator's ingest appends the verdict ledger, un-ticks the `refuted` boxes, and sets
152
+ `eval_verdict` frontmatter. You never touch a task file, a board, or run-state — and you
153
+ NEVER set `status: done`: the judge issues verdicts; closure belongs elsewhere. That
154
+ separation is the whole point of the architecture.
155
+
156
+ ---
157
+
158
+ ## Verification checklist
159
+
160
+ - [ ] Every criterion traces to committed spec text (UC/domain-model/contract/Done-when/Non-Go)
161
+ - [ ] Every PASS cites a confirming probe; every FAIL cites evidence or "NO EVIDENCE"
162
+ - [ ] Every FAIL was re-probed once; confidence assigned per the ledger rule
163
+ - [ ] Scoped spec → T0 citations present with recomputed sha256 (else the run returned `failed`)
164
+ - [ ] Report written inside `evaluation/**` only; no other file touched
165
+ - [ ] `refuted[]` lists exactly the boxes your evidence disproves (un-ticking is ingest's act)
166
+ - [ ] The WorkResult validates against `work-result.schema.json`
167
+
168
+ ---
169
+
170
+ ## Dimension model — how future injection works
171
+
172
+ The core loops over a **set of dimensions**; each is a self-contained file satisfying
173
+ `references/dimension-contract.md` (id, weight, hard_threshold, applies_to, criteria[],
174
+ bug_template). Adding one (e.g. security) = write `references/dimensions/security.md`, flip
175
+ `enabled: true` in `references/dimensions/_registry.md` (or pass it in `dimensions[]`), re-run
176
+ — zero core edits. Disabled security/performance stubs ship as worked examples.
177
+
178
+ ---
179
+
180
+ ## Invocation
181
+
182
+ ```bash
183
+ # Orchestrated (once per round, after GATE L2) — the canonical form
184
+ /spec-evaluator --order .shapeup/checkout-vnpay/orders/evaluate-r2.json
185
+
186
+ # Standalone — the preamble shim compiles a minimal order, then the single code path runs:
187
+ # node "${CLAUDE_PLUGIN_ROOT}/skills/tech-lead/scripts/compile-order.mjs" --operation evaluate --slug <slug> \
188
+ # --worker spec-evaluator [--payload '{"dimensions": [...], "run_cmd": "..."}']
189
+ /spec-evaluator --spec shapeup/checkout-vnpay/spec/ --task TASK-007
190
+ /spec-evaluator --spec shapeup/checkout-vnpay/spec/ --feature checkout-vnpay --single-pass
191
+ ```
192
+
193
+ Standalone keeps `--task` (per-task check, not round-gated) and `--single-pass` (feature-level)
194
+ — the shim maps them onto the order's payload; missing run command → ask. After writing the
195
+ WorkResult, run `node "${CLAUDE_PLUGIN_ROOT}/skills/tech-lead/scripts/ingest-result.mjs" <result path>` and show its
196
+ summary — standalone has no orchestrator to ingest for you.
197
+
198
+ ---
199
+
200
+ ## Hard Rules (never override without explicit user instruction)
201
+
202
+ | Rule | Rationale |
203
+ |------|-----------|
204
+ | Absence of evidence = FAIL | Kills pass-by-assumption, the core QA failure mode |
205
+ | Halo effect banned | A strong dimension never lifts a failing one |
206
+ | Disabled dimensions are out of scope | No silent scope creep |
207
+ | Evaluator never sets `status: done`, never edits task files/boards | Judge ≠ doer; refuted boxes return as data, ingest writes |
208
+ | Untestable AC blocks a clean PASS | Forces the spec to be verifiable, not vibes |
209
+ | Probe the RUNNING app, not the source alone | Apps that look right still break when used |
210
+ | Re-probe every FAIL; flip ⇒ confidence low | A single snapshot lies; the ledger makes it visible |
211
+ | Verdict-ledger lines are returned, appended by ingest, never rewritten | Verdict history is how a single-snapshot judge becomes measurable |
212
+ | A verdict on a scoped spec without a T0 citation is structurally invalid | T0 is a machine fact the generator cannot fabricate (DD-7, PA4) |
213
+ | UI assertions target affordances only (test_id/role/data-state) | Layer-3 styling is frozen; grading it resurrects the freeze through the judge |
@@ -0,0 +1,50 @@
1
+ # Anti-Leniency Protocol
2
+
3
+ Read this immediately before GATE V2. It exists because of a documented failure mode: an
4
+ LLM asked to grade work — even another agent's — finds real defects and then talks itself
5
+ into approving anyway, and tests superficially rather than probing edge cases. This skill
6
+ counteracts that. The evaluator's job is to be the skeptic the generator cannot be about
7
+ its own output.
8
+
9
+ ## The posture
10
+ **Assume broken until proven working.** A criterion is FAIL by default and only earns PASS
11
+ when Phase A produced evidence that directly confirms it. The burden of proof is on the
12
+ build, not on the evaluator to find a reason to fail it.
13
+
14
+ ## Evidence rules
15
+ 1. **No evidence → FAIL.** If Phase A collected nothing for a criterion (forgot to probe,
16
+ app wouldn't start, probe inconclusive), it is a FAIL labeled `NO EVIDENCE` — never a
17
+ pass-by-assumption. Re-probe if you want a different verdict.
18
+ 2. **Every PASS cites the confirming probe.** "PASS — pnpm --filter api test → 12 passed."
19
+ A PASS with no citation is invalid.
20
+ 3. **Every FAIL cites concrete evidence** at a locator: file:line, endpoint, or
21
+ screen+element, plus the output/console/DB state observed. A FAIL with no evidence is
22
+ not actionable → re-probe to localize it.
23
+ 4. **Probe behavior, not code presence.** A function named `fillRectangle` existing is not
24
+ evidence the rectangle fill works. Exercise it. Code that looks correct but is wired
25
+ wrong is the exact defect class this skill targets.
26
+
27
+ ## Banned moves
28
+ - **Halo effect.** A strong dimension or a mostly-working build never lifts a failing
29
+ criterion. Grade each criterion in isolation, then AND.
30
+ - **Self-negotiation.** Do not reason "this is a minor issue, probably acceptable." If it
31
+ violates the criterion, it FAILS at the severity it warrants; acceptability is the user's
32
+ call at GATE V3, not the grader's at V2.
33
+ - **Superficial probing.** Don't stop at the happy path when the criterion implies edge
34
+ cases (error codes, empty states, invalid input). Under-probing produces false PASSes.
35
+
36
+ ## Forbidden phrases in a verdict
37
+ These are the tells of lenient self-grading. If one appears, the verdict is not done:
38
+ - "looks good" / "looks correct" / "seems to work"
39
+ - "probably works" / "should be fine" / "likely passes"
40
+ - "minor issue, acceptable" / "good enough" / "close enough"
41
+ - "I'll assume" / "presumably" / "in principle this should…"
42
+
43
+ Replace each with an evidence-anchored statement or a FAIL.
44
+
45
+ ## Calibration toward skepticism
46
+ If across a session the verdicts feel generous, recalibrate by re-reading the FAILs: each
47
+ must survive the test "could the generator act on this without asking a single follow-up
48
+ question?" If not, it is under-specified — tighten the locator and the expected-vs-actual.
49
+ Tuning the grader to be skeptical is more tractable than making the generator self-critical;
50
+ that is the entire reason the judge is a separate agent.
@@ -0,0 +1,126 @@
1
+ # Dimension Contract
2
+
3
+ The injection interface. Every file in `dimensions/` must satisfy this contract so the
4
+ core can load and run it without modification. This is the *only* thing the core knows
5
+ about a dimension. Adding a new evaluation concern = writing one file that conforms here.
6
+
7
+ ---
8
+
9
+ ## Required frontmatter
10
+
11
+ ```yaml
12
+ ---
13
+ id: spec-conformance # unique, kebab-case. Matches filename.
14
+ title: "Spec conformance" # human label printed in verdicts
15
+ enabled: true # core loads it only if true (or named in --dimensions)
16
+ weight: 1.0 # informational; thresholds are hard, not weighted averages
17
+ hard_threshold: all-pass # see "Threshold vocabulary" below
18
+ applies_to: # scoping — omit a key to mean "all"
19
+ lens: [lite, standard] # which ba-pitch-analyzer lenses
20
+ package: any # apps/api | apps/web | apps/mobile | packages/shared | any
21
+ variant: any # shared | be | web | mobile | e2e | any
22
+ requires_browser: true # does this dimension need the running app in a browser?
23
+ ---
24
+ ```
25
+
26
+ The core (GATE V0.5) validates these fields. A dimension missing `id`, `enabled`,
27
+ `hard_threshold`, or a malformed `criteria` block is **skipped with a warning** — never
28
+ run half-formed.
29
+
30
+ ---
31
+
32
+ ## Required body sections
33
+
34
+ ### `## Criteria`
35
+ A list. Each criterion is the atomic unit the evaluator grades. Schema per criterion:
36
+
37
+ ```yaml
38
+ - id: SC-1 # unique within the dimension
39
+ statement: "Every AC checkbox in the task passes by command or UI probe."
40
+ probe: cmd | ui | data | static # how Phase A collects evidence for it
41
+ evidence_required: true # if true, NO EVIDENCE → automatic FAIL
42
+ pass_rule: > # the objective condition for PASS, evidence-based
43
+ All "- [ ]" items verified PASS via their probe; zero failures.
44
+ source: usecases | index | task | contract | scope-summary | code # where the criterion is read from
45
+ # (usecases/domain-model.md are the grading source of truth
46
+ # since v0.9 — task is traceability-only, never substituted)
47
+ ```
48
+
49
+ `probe` values map directly to Phase A handlers:
50
+ - `cmd` → run a shell command, capture output + exit code
51
+ - `ui` → drive the running app (Playwright CLI by default)
52
+ - `data` → query DB / inspect storage state
53
+ - `static` → read code/files (use sparingly; prefer probing the running app)
54
+
55
+ ### `## Threshold`
56
+ State the hard threshold in prose + the vocabulary token. The core ANDs all active
57
+ dimensions; within a dimension, the threshold decides PASS/FAIL from its criteria.
58
+
59
+ ### `## Bug template`
60
+ A fenced block the report uses for every FAIL this dimension produces. Must include at
61
+ least: `severity`, `criterion`, `location (file:line or endpoint)`, `repro`,
62
+ `expected`, `actual`. Keeps generator handoff uniform across dimensions.
63
+
64
+ ---
65
+
66
+ ## Threshold vocabulary
67
+
68
+ | Token | Meaning |
69
+ |-------|---------|
70
+ | `all-pass` | Every criterion must PASS. One FAIL → dimension FAILS. (Use for correctness.) |
71
+ | `no-critical` | FAILs allowed only below `critical` severity. Any `critical` → dimension FAILS. |
72
+ | `count<=N` | At most N FAILs of any severity tolerated. |
73
+ | `score>=N` | For genuinely graded/subjective dimensions: a 0–100 rubric score ≥ N. |
74
+
75
+ `spec-conformance` uses `all-pass` — correctness is not negotiable and leniency is the
76
+ enemy. A subjective dimension (e.g. `visual`) would use `score>=N` with a calibrated rubric.
77
+
78
+ ---
79
+
80
+ ## What the core promises a dimension
81
+
82
+ 1. It reads `applies_to` and runs the dimension only on matching tasks.
83
+ 2. It calls the right Phase A handler for each criterion's `probe`.
84
+ 3. It enforces `evidence_required` (no evidence → FAIL) before applying `pass_rule`.
85
+ 4. It applies the dimension's own `hard_threshold` independently, then ANDs across dimensions.
86
+ 5. It renders FAILs using the dimension's own `## Bug template`.
87
+
88
+ A dimension author therefore writes *criteria + probes + threshold + bug shape* — never
89
+ touches the gate logic, the loop, or the report assembler.
90
+
91
+ ---
92
+
93
+ ## Minimal valid dimension (copy to start a new one)
94
+
95
+ ```markdown
96
+ ---
97
+ id: my-dimension
98
+ title: "My dimension"
99
+ enabled: false
100
+ weight: 1.0
101
+ hard_threshold: all-pass
102
+ applies_to: { variant: web }
103
+ requires_browser: true
104
+ ---
105
+
106
+ ## Criteria
107
+ - id: MD-1
108
+ statement: "<the thing to verify>"
109
+ probe: ui
110
+ evidence_required: true
111
+ pass_rule: "<objective, evidence-based PASS condition>"
112
+ source: task
113
+
114
+ ## Threshold
115
+ all-pass — every criterion must PASS.
116
+
117
+ ## Bug template
118
+ ```
119
+ severity: [critical|major|minor]
120
+ criterion: MD-1
121
+ location: <file:line | endpoint | screen>
122
+ repro: <steps>
123
+ expected: <from criterion>
124
+ actual: <observed evidence>
125
+ ```
126
+ ```
@@ -0,0 +1,40 @@
1
+ # Dimension Registry
2
+
3
+ The core reads this at GATE V0.5 to decide which dimensions to load. `--dimensions` on the
4
+ command line overrides this table for a single run.
5
+
6
+ ## Active set
7
+
8
+ | id | enabled | file | applies_to (summary) | notes |
9
+ |----|---------|------|----------------------|-------|
10
+ | `spec-conformance` | ✅ true | `dimensions/spec-conformance.md` | all tasks | baseline correctness: AC + Done-when + contract shapes + non-go |
11
+ | `tdd-surface` | ✅ true | `dimensions/tdd-surface.md` | all tasks | suite green + companion test files for new code; TDD-1/2 critical, TDD-3 advisory |
12
+ | `integration` | ✅ true | `dimensions/integration.md` | `.be` and `.e2e` variants only | full-stack integration test + auth boundary + RLS-JWT pattern; runs only when variant matches |
13
+ | `completeness` | ⚙️ auto | `dimensions/completeness.md` | all tasks (lens lite/standard) | auto-ON when spec has UC `## Invariants` (v2.8+); no-op on older specs |
14
+ | `test-surface-conformance` | ⚙️ auto | `dimensions/test-surface-conformance.md` | all tasks (lens lite/standard) | auto-ON when spec has UC `## Test Surface` (v2.9+ or `--surface-only` retrofit); no-op otherwise. Report must list every TS row probed — qa-edge-hunter's negative-space input |
15
+ | `security` | ⛔ false | `dimensions/security.md` | be / shared | stub — flip on when ready |
16
+ | `performance` | ⛔ false | `dimensions/performance.md` | be / web | stub — flip on when ready |
17
+
18
+ Default active set = every row with `enabled: true` whose `applies_to` matches the task variant, **plus** each ⚙️ auto dimension when its auto-enable condition holds:
19
+ - All tasks: **`[spec-conformance, tdd-surface]`** (baseline)
20
+ - `.be` / `.e2e` tasks additionally pick up: **`[integration]`**
21
+ - When any UC declares `## Invariants`: also **`[completeness]`**
22
+ - When any UC carries `## Test Surface`: also **`[test-surface-conformance]`**
23
+
24
+ ## Load order
25
+ Dimensions run in table order. `spec-conformance` first so a correctness failure is the
26
+ headline; cosmetic / non-functional dimensions report after it. Order does not change the
27
+ overall verdict (all dimensions are ANDed) — it only affects reading order in the report.
28
+
29
+ ## How to add a dimension (the injection recipe)
30
+ 1. Copy the "Minimal valid dimension" block from `dimension-contract.md` to
31
+ `dimensions/<id>.md` and fill in criteria, probes, threshold, bug template.
32
+ 2. Add a row here and set `enabled: true` — or leave it false and turn it on per-run with
33
+ `--dimensions spec-conformance,<id>`.
34
+ 3. Run. No core file changes. GATE V0.5 validates it against the contract; if it doesn't
35
+ conform, it is skipped with a warning rather than silently mis-run.
36
+
37
+ ## Scoping reminder
38
+ A dimension only grades tasks its `applies_to` matches. Example: `security` with
39
+ `applies_to: { variant: [be, shared] }` is ignored when evaluating `TASK-007.web`, even if
40
+ it is enabled. This keeps the active set honest per task.
@@ -0,0 +1,102 @@
1
+ ---
2
+ id: completeness
3
+ title: "Spec completeness"
4
+ enabled: false
5
+ weight: 1.0
6
+ hard_threshold: no-critical
7
+ applies_to:
8
+ lens: [lite, standard]
9
+ package: any
10
+ variant: any
11
+ requires_browser: false
12
+ ---
13
+
14
+ # Completeness Dimension
15
+
16
+ > **Auto-enable rule (read at GATE V0.5):** This dimension stays `enabled: false` by
17
+ > default so it is a no-op on every spec generated before `ba-pitch-analyzer` v2.8.
18
+ > The core flips it ON for a run **only when** the spec's `usecases/` contains at least
19
+ > one `## Invariants` section. A pre-v2.8 spec has no invariants → this dimension never
20
+ > activates → existing audit scores and verdicts are unchanged (non-regression guarantee).
21
+ > An explicit `--dimensions ...,completeness` always wins over the auto rule.
22
+
23
+ **Why this exists.** `spec-conformance` answers *"does each task pass its acceptance
24
+ criteria?"* — it grades the tasks that exist. It cannot see the task that was never
25
+ written. Shape Up's discovered-task philosophy says the real bulk of work is found while
26
+ building; an invariant declared on a UC but never backed by a regression task is exactly
27
+ that blind spot. This dimension converts "absence of a task" from invisible into a graded
28
+ **GAP** — the judge surfaces it; the planner (a ba-pitch-analyzer reconcile/generate-board order) fills it.
29
+ Judge never fills it itself.
30
+
31
+ **Distinction held:** conformance = *the tasks that exist are correct*; completeness =
32
+ *the tasks that must exist, do exist*. A spec can be 100% conformant and still incomplete.
33
+ Definition of Done for the harness = conformance PASS **and** completeness no-critical.
34
+
35
+ ---
36
+
37
+ ## Criteria
38
+
39
+ - id: CMP-1
40
+ statement: "Every `[INV-NN]` declared in any usecases/UC-*.md ## Invariants section is backed by ≥1 task whose use_case_refs points to that UC AND whose AC references the invariant (by INV id or its assertion)."
41
+ probe: static
42
+ evidence_required: true
43
+ pass_rule: >
44
+ For each INV-NN found across usecases/: a task backs it only if BOTH hold — (a) the
45
+ task lists that UC in use_case_refs, and (b) the task names the invariant unambiguously,
46
+ i.e. cites the INV id (e.g. "INV-02") or links the UC's #Invariants anchor AND its AC
47
+ restates that specific assertion as a command/observable check. Matching a loose keyword
48
+ shared by several invariants is NOT sufficient — require the INV id or a 1:1 assertion
49
+ match to avoid crediting one task for an invariant it does not actually test. Zero
50
+ uncovered invariants → PASS. Any invariant with no backing task → FAIL at `critical`
51
+ severity (a missing must-have regression, not a cosmetic gap).
52
+ source: code
53
+
54
+ - id: CMP-2
55
+ statement: "Every use case has ≥1 task referencing it via use_case_refs (no stranded UC with zero tasks pointing at it)."
56
+ probe: static
57
+ evidence_required: true
58
+ pass_rule: >
59
+ For each usecases/UC-*.md: ≥1 task file lists it in use_case_refs. A UC with zero
60
+ referencing tasks → FAIL at `critical` (this is the Coverage 🔴 condition, re-checked
61
+ from the evaluator side as a completeness gap rather than trusting synthesis alone).
62
+ source: code
63
+
64
+ - id: CMP-3
65
+ statement: "Discovery ledger surprise rate is within a healthy band (shaping-quality signal)."
66
+ probe: static
67
+ evidence_required: false
68
+ pass_rule: >
69
+ If a discovery ledger exists (discovery/ledger.md or a *discovered-tasks*.md whose
70
+ feature matches run-state.feature), count discovered items marked `[+]` vs imagined
71
+ `[ ]`. surprise_ratio = discovered / (imagined + discovered). PASS (no defect) when
72
+ ratio ≤ 0.5. When ratio > 0.5 → emit a `minor` finding "high surprise rate —
73
+ shaping may have under-specified scope; flag to PO", NOT a critical. evidence_required
74
+ is false, so a spec with no ledger simply yields no CMP-3 finding (not a FAIL).
75
+ source: code
76
+
77
+ ---
78
+
79
+ ## Threshold
80
+
81
+ `no-critical` — `critical` FAILs (CMP-1 uncovered invariant, CMP-2 stranded UC) block the
82
+ dimension. `minor` findings (CMP-3 high surprise) are reported but do not fail the
83
+ dimension. This keeps the gate honest about *missing must-have work* while treating the
84
+ surprise signal as advisory feedback to the PO, not a build blocker.
85
+
86
+ Rationale for `no-critical` rather than `all-pass`: CMP-3 is a deliberately non-blocking
87
+ signal. Using `all-pass` would let an advisory surprise-rate note sink the verdict, which
88
+ would punish healthy discovery — the opposite of the intent.
89
+
90
+ ---
91
+
92
+ ## Bug template
93
+
94
+ ```
95
+ severity: [critical|minor]
96
+ criterion: [CMP-1|CMP-2|CMP-3]
97
+ location: [usecases/UC-Name.md#Invariants INV-NN | usecases/UC-Name.md | discovery/ledger.md]
98
+ repro: <how the gap was detected — e.g. "grep use_case_refs across tasks/ found 0 backing TASK for INV-02">
99
+ expected: <e.g. "≥1 regression task referencing UC-ConnectObjects covering INV-02">
100
+ actual: <e.g. "no task references INV-02; invariant declared but unverified">
101
+ next: <handoff — e.g. "a ba-pitch-analyzer reconcile order over <ledger> to generate the missing regression task">
102
+ ```
@@ -0,0 +1,129 @@
1
+ ---
2
+ id: integration
3
+ title: "System integration"
4
+ enabled: true
5
+ weight: 1.0
6
+ hard_threshold: no-critical
7
+ applies_to:
8
+ lens: [lite, standard]
9
+ package: any
10
+ variant: [be, e2e]
11
+ requires_browser: false
12
+ ---
13
+
14
+ # System Integration Dimension
15
+
16
+ **Why this exists.** Unit tests verify isolated logic. `spec-conformance` drives the
17
+ running app and confirms AC at the surface. Neither catches the class of bug where each
18
+ layer works in isolation but the *seam* is broken: wrong auth header forwarded, RLS policy
19
+ not invoked because the transaction is missing, Prisma schema migration not applied in the
20
+ test database, or a contract mismatch between web's api-client and the API controller.
21
+ This dimension fills that seam.
22
+
23
+ **What "system integration" means here** (scoped to the IVS ADR-005 stack):
24
+ the full request path — web api-client → API controller → service → repository (Prisma +
25
+ RLS) → database — exercised by tests that use a real database, not mocked layers.
26
+ An integration test that mocks `PrismaClient` or replaces the repository with an in-memory
27
+ stub is a unit test wearing integration clothing and does not satisfy this dimension.
28
+
29
+ **Project context (read AGENTS.md Section 2 rules 3 & 4 before probing):**
30
+ - All user-scoped queries must run inside a transaction that injects the JWT claims so RLS
31
+ fires (`SET LOCAL request.jwt.claims`). An integration test that queries without this
32
+ transaction bypasses RLS and produces a false-positive PASS.
33
+ - The connection string must point to the **transaction-mode pooler** (port 6543), not the
34
+ session-mode direct connection (port 5432). Session mode allows `SET LOCAL` to leak.
35
+ - Never trust a `userId` sent in the request body — derive it from the verified JWT.
36
+
37
+ **`.e2e` variant:** the e2e suite inherently exercises the full stack. INT-1 (integration
38
+ test existence) maps to the e2e test file(s) added by the task. INT-2 and INT-3 apply if
39
+ the e2e task introduces any auth-scoped endpoint.
40
+
41
+ ---
42
+
43
+ ## Criteria
44
+
45
+ ```yaml
46
+ - id: INT-1
47
+ statement: "At least one integration/e2e test exercises the feature's main flow end-to-end with a real database — no mocked repository or Prisma layer."
48
+ probe: static + cmd
49
+ evidence_required: true
50
+ pass_rule: >
51
+ Locate integration test files for this task's package (typically `*.integration.spec.ts`,
52
+ `*.e2e-spec.ts`, or a `test/` directory at the package root). Grep for test cases that
53
+ touch the happy-path scenario the task implements. Verify the test does NOT mock the
54
+ data layer: `grep -rE 'jest\.mock|vi\.mock' <integration-test-file>` should return no
55
+ hits on repository, Prisma, or database imports. Then run `pnpm --filter <pkg> test:e2e`
56
+ (or the integration-specific script) and capture exit code + test count.
57
+ Zero integration test files → FAIL at `critical`. Any integration test that mocks the
58
+ DB layer → FAIL at `critical` (evidence: the jest.mock / vi.mock import path).
59
+ source: code
60
+
61
+ - id: INT-2
62
+ statement: "An unauthorized-access scenario is covered: a request from a different user/org is rejected with the correct error (403 or empty set), exercised by an integration test."
63
+ probe: cmd + data
64
+ evidence_required: true
65
+ pass_rule: >
66
+ Find the integration test that exercises the unauthorized path: a user from a different
67
+ org (or an unauthenticated caller) attempts to read/write the resource this task
68
+ introduces. The test asserts the correct rejection: 403 Forbidden, an empty result set,
69
+ or an RLS-filtered response. If the resource is read-only to viewers (role_tag check),
70
+ also verify a `viewer` role cannot mutate it. Evidence = the test case text + the run
71
+ output showing it passes. No such test → FAIL at `critical` — this is the gap that lets
72
+ real authorization bugs slip through conformance testing.
73
+ source: code
74
+
75
+ - id: INT-3
76
+ statement: "Integration tests inject JWT claims via the RLS transaction pattern (set_config inside a transaction) and target the transaction-mode pooler (port 6543)."
77
+ probe: static
78
+ evidence_required: true
79
+ pass_rule: >
80
+ Inspect the integration test setup (beforeAll / test helper): it must execute
81
+ `SET LOCAL request.jwt.claims` (or the equivalent `$executeRaw\`select set_config(...)\``)
82
+ inside a Prisma `$transaction` block before any user-scoped query. Also check the
83
+ DATABASE_URL used in the test config: it must include port 6543 (transaction pooler),
84
+ not 5432 (session mode). A test that queries without the JWT injection calls the DB as
85
+ the app role without RLS and will PASS rows it should not see → FAIL at `major`.
86
+ A test using port 5432 risks SET LOCAL leaking across pooled connections → FAIL at `major`.
87
+ If both problems are present, report as two separate findings.
88
+ source: code
89
+ ```
90
+
91
+ ---
92
+
93
+ ## Threshold
94
+
95
+ `no-critical` — INT-1 and INT-2 are `critical`: a task that cannot prove end-to-end
96
+ correctness and cannot prove its authorization boundary is not integration-tested and is
97
+ not done. INT-3 (JWT transaction pattern + pooler port) is `major`: incorrect plumbing in
98
+ the test setup produces false-positive passes (the test appears to work without RLS firing),
99
+ which is a confidence-undermining failure — but it is one step removed from observable
100
+ breakage and is reported as major rather than critical.
101
+
102
+ ---
103
+
104
+ ## Bug template
105
+
106
+ ```
107
+ severity: [critical|major]
108
+ criterion: [INT-1|INT-2|INT-3]
109
+ location: <test file path | package/test/setup.ts | .env.test | file:line>
110
+ repro: <e.g. "grep -rE 'jest.mock.*repository' test/" | "grep DATABASE_URL .env.test" | "pnpm --filter api test:e2e">
111
+ expected: <e.g. "integration test exists targeting BoardRepository with real DB" | "DATABASE_URL ends with :6543/...">
112
+ actual: <e.g. "no *.integration.spec.ts in apps/api/src/boards/" | "DATABASE_URL uses port 5432" | "vi.mock('../../infrastructure/BoardRepository')">
113
+ fix_hint: <e.g. "add boards/boards.integration.spec.ts; use PrismaService with a real test DB; wrap queries in $transaction with set_config">
114
+ ```
115
+
116
+ ## Probing notes
117
+
118
+ - Integration test scripts are often separate from unit tests: `test:e2e` / `test:int` /
119
+ `test:integration` in package.json. Check all scripts, not just `test`.
120
+ - For mocking detection: `grep -rn "jest\.mock\|vi\.mock" <test-file>` and inspect what
121
+ is being mocked. Mocking HTTP clients, external APIs, or filesystem utilities is fine;
122
+ mocking the Prisma client or the repository class defeats integration testing.
123
+ - For port check: the integration DB URL is typically in `.env.test` or `jest.config.ts`
124
+ (globalSetup). Check `DATABASE_URL` and `DIRECT_URL` separately — some setups split them.
125
+ - For `.e2e` tasks: `supertest` or `axios` against the running NestJS server counts as
126
+ an integration test if it hits a real DB. Verify the test server's DB config, not just
127
+ the production server's config.
128
+ - Do not skip INT-3 because the test "seems to work." RLS without `set_config` silently
129
+ returns all rows as the app role — the test passes but the auth boundary is untested.