Repository navigation
v0.9.0
Nineteen rough edges from one real dogfooding session (an eid-gateway feature and a 26-FR/47-TC phase), grouped by what they actually cost. Group 1 is false-green / lost-evidence: a command reports success, or asserts nothing, while the thing it claims to check never happened. Group 2 is noise that wastes time without corrupting the result. Both groups share one theme this release keeps hitting: an engine that silently drops the SECOND (or third, or fourth) matching thing — a table, a repo, an FR, a stderr stream — instead of merging or naming what it dropped.
Group 1 — false-green / lost evidence
verify-collectnever wroteVERIFICATION.md. It computed{status, passed, failed, truths}and returned them as JSON only —commands/manual-test.mdstep 4 says "This writes VERIFICATION.md," but nothing did.status-report,doctor's verify-integrity check, andtask-baselineall read that file to decide a feature is verified; a full 68/68 pass reportedstatus:"passed"on stdout while the feature stayed "not verified" forever and the ship gate never opened. It now actually writes the file, in thestatus: passed|failed+- TC-xxx: verifiedshape those readers already expect. Also fixed in the same command:--resultsonly accepted a file path, but the doc's own worked example passes the JSON result line inline — now accepts either.--featureis required (no inference from the shared active-feature mirror — the same write-safety ruletrace-linkalready enforces).- A dict
expect:on a SQLverify:/setup:/teardown:step was never asserted.db-query.sh -thas no column headers to match a dict key by, socheck_scalar()treated a dict expect as purely descriptive — which is also the MOST NATURAL way to write a multi-column assertion (it's exactly howexpect.bodyworks for HTTP). A real bug in one of those columns printed green.lint-checklist.shnow flags it before the run: split into one scalarverify:row per column, or assert via the HTTP response'sjson_path. expect:had nocontainsoperator.expect: "contains idx_foo"fell to the exact-string fallback — the whole (often multi-line) scalar result compared against the literal string"contains idx_foo", never equal, so an EXPLAIN-plan-has-this-index assertion always failed regardless of whether the index was actually there. Addedcontains/not contains(substring match) tocheck_scalar/cmp, matching whatjson_path:assertions already supported.- A failed
teardown:step vanished into console-only output.teardown:is deliberately best-effort (never aborts the run), but a teardown step is often RECOVERY — undo a mutation, restore a row — and a failed recovery only printed a line to stdout; the caller had no way to know it happened. Its dirty leftover state then broke a LATER, unrelated test with no link back to the real cause.setup.run_steps()now returns the warning list instead of swallowing it;run-checklist.sh --jsonreports it asteardownWarnings, andverify-collectrecords it inVERIFICATION.mdunder its own heading — visible, not misattributed. sd-skeletonharvested only the FIRST FR-prefixed table. An SRS with FRs split across several sub-tables (one per module — 26 FRs across 4 tables is a real, common shape) reportedfr: 6and silently dropped the rest. Same bug for the TC/NFR ID-prefix fallbacks.tableByIdPrefixnow merges every matching table's rows instead of.find()-ing the first.sd-skeleton's §12.2 error-code harvest never read the SRS's own §6.2 "Error & Notification Messages" table — only per-story Edge Cases bullets, even though the generated §12.2 fallback text has said "derive from SRS §6.2" since Pass-1 existed. A project that lists its error codes in that dedicated table (rather than as story edge-case prose) harvested 0, with no signal. It's now read and merged, with the table's own Message column used as the real user-facing message instead of a TODO.trace-buildbuilt tables purely by content-matched header keywords, with no cross-check against the section NUMBER. A§12.2(or§5.1/§13.2/§10.4/§5.2) table whose column headers drifted — translated, renamed — silently produced 0 nodes for that whole section: the SD reads complete, the trace quietly drops it. Now warns by name when the numbered section exists with a table the content-matcher didn't recognize, quoting the actual headers found.trace-buildnever flagged an FR with zero linked TCs. An orphaned requirement — present in §5.1, uncovered by any §13.2 test — built a valid-looking trace. Now warned by FR id.checklist-gen's auth detection scanned the WHOLE multi-repo hub, majority-voting across everyconfig.reposentry — a feature scoped to one no-auth/HMAC repo in a hub of Summer/APISIX services got the hub'ssummerclassification and 401'd every generated test. Now resolves the feature's own repo scope first (--repos> declaredtrace-repos> file-links inference — the same precedenceverify-codealready uses) and detects against that one repo when it resolves unambiguously.detect-auth.shdefaulted an unrecognized custom scheme tono-auth. A gateway verifying its own HMAC/signature header (no Spring Security dep, noAuthorization: Bearer) has real access control, andno-authscaffolds unsigned tests that never exercise it. Added an HMAC/signature-pattern check (X-Signature,Mac.getInstance("Hmac..."),crypto.createHmac, …) that now classifies asunknowninstead — same safe-default fix as the 0.7.4 Summer/X-Userinfoflip, one auth family later.parse-prd/expand, run as a bare CLI subcommand, look like they finish in one shot — they don't. DefaulttaskCore.aiModeisagent-native: the command prints aGenerationSpecand exits 0 without calling an LLM (zero-network by design).commands/ingest.mdandcommands/phase.mdpresented it as a single self-sufficient bash command; the agent could read exit 0 + a large JSON blob as "done," runuse-tagsuccessfully, and leavetasks.jsonwith zero tasks for that tag — silent, textbook false-success. Both docs now spell out the handoff: parse the printed spec, generate theTask[]yourself (Phase 2), thentasks-import --file <path>(Phase 3) —{"imported": N}is the real signal, not the Phase 1 exit code.
Group 2 — noise, not wrong
srs-snapshot's filename-derived-slug warning only fired for a date-prefixed filename. Any other filename shape (phase-3-agentgw-client.md, drifting from a project's own naming convention) was silently adopted. Now warns whenever the slug came from the filename at all (noFeature:line, no--feature).checklist-genleakeddetect-auth.sh's stderr diagnostics into a caller that merges stdout+stderr. The engine's own stdout stayed pure JSON, butexecFileSync's defaultstdioinherits the child's stderr straight into this process's — any2>&1capture (or a tool wrapper that combines streams) saw prose land BEFORE the JSON and failed to parse it, even though the command had already succeeded. Now explicitly captured and discarded.checklist-genalways scaffoldedconfig.db/config.redis/cleanup:, even for a phase with no persistence at all.sd-template.md's own §7 convention is "no DB change → delete this section" — now honored: no §7 (or a §7 that says N/A/stateless/no database), no db/redis/cleanup block, with a warning explaining why.checklist-gensilently degenerated to one suite per test. When every §13.2 TC row carries a distinctFlowvalue (common for business-rule-derived TCs — 26 rules, 28 suites), grouping-by-Flow produces 28 singleton suites, and a singleton suite's lone test is ALWAYS taggedsmoke, neverregression(the rule needs ≥2 tests per flow to ever emit aregressiontag) — so--tag regressionsilently skips the entire checklist with no error. Now warned by name, with the count.genSd's §10.4 State Management forced aTODO:MANUAL-REVIEWmarker on every internal/hybrid phase, even one with no state machine at all.sd-template.md's own convention is "no state → delete this sub-section," the same carve-out §7 gets — but Pass-1 never offered it, so a plain batch job or pure transform carried a permanently unresolvable marker blocking approval until someone hand-deleted the section. Now a plain advisory note, not a TODO.
857 Node tests green (15 new), 68 Python checklist_lib unit tests green (3 new).