Repository navigation
P0 learning quality: mechanical emitters + sheal consolidate (T13–T15, T17) - #47
Conversation
- sheal check --strict: warnings exit non-zero — compiles LEARN-007 (warnings are blockers) from prose into an exit code - sheal guard pr: pre-PR gate verifying the branch is ahead of base, not the base itself, with an uncommitted-changes warning — compiles LEARN-038/LEARN-042; built for hooks and CI - Source learnings marked status: superseded in the store with pointers to the artifacts that replaced them (provenance convention) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- buildChangeSet: mechanical consolidation stage from lint primitives — ID collisions (renumber-or-retire), merge candidates (near-duplicates with similarity), dispositions (no-trigger -> rewrite-or-retire) - sheal consolidate writes a dated proposal to .sheal/consolidation/, never mutates the store; --format json for machine use - --prompt emits the LLM judgment-stage prompt (topic pages with trigger tables, contradictions, disposition table) for piping to any agent CLI - learn lint: superseded/retired learnings excluded from near-duplicate and trigger checks (they are resolved, not live rules) — found when the T14 supersede notes inflated similarity on the real store Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…predicate - T15 (Expose near-duplicate similarity as structured data): LintFinding carries a numeric similarity field; buildChangeSet consumes it instead of regex-scraping the human-readable message; test pins the value end-to-end - T17 (Reconcile --strict's warning definition with the report summary): one shared resultHasWarnings predicate drives both summary.warnings and the strict exit decision — no more exit 1 with 'warnings: 0' - T16 (wire gates into hooks/CI) filed as todo — depends on disposition execution for store cleanliness Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
🧑🏫 PR Tutor — human-review pass (delta: #46 → #47)0. What this comment is — and what it verifiedThis is a delta pass, not a from-scratch one. The full tutor explainer lives on the superseded PR: #46 explainer — read that first if you're cold; everything in it still applies to this diff, because: So #47 is byte-for-byte the final #46 content, rebased onto
That commit is exactly what this comment explains. It answers the #46 explainer's loose ends 1 and 2 and files the three follow-up task stubs (T15/T17 1. TL;DR (delta only)Two plumbing fixes, both "stop deriving facts from prose": the near-duplicate similarity score now travels from 2. Picture itBefore → After — similarity (T15, loose end 1): The score is data with a contract now; the prose message is just presentation. Before → After — the warning predicate (T17, loose end 2): One predicate, two consumers — but only two of the three output surfaces were unified. 3. Toy example — one checker result through the new summaryNote the deliberate semantic change hiding in there: 4. Walkthrough (delta files only)The two fixes:
Tests (3 new, per the red-first claim):
Docs:
5. Does it meet "Done when"? (T15, T17)
6. Loose ends & red flags (new in this delta)
7. Still open from the #46 pass — do not let these die with the closed PRThese #46 findings are not addressed by this delta and now live only in a closed PR's comment (plus, for the first one, a task file):
8. Suggested follow-up tasks (drafts — numbers provisional, max existing id across branches is T17)Say "file these" (or "file task 2") and the main session will create them via the opentasks skill — I don't create task files myself. 9. Verify by hand (delta-focused; the #46 checklist still applies)
10. Check your understanding (optional — skip freely, or say "quiz me")
Tutoring pass, not an approval or a safety claim. It surfaces what changed and what to check by hand; the human reviewer decides. |
🧪 qa-personas — black-box QA, delta run (advisory, non-blocking)A full The CLI was built (
|
| # | Probe | Result |
|---|---|---|
| D1 | consolidate -f json on a store with a near-duplicate pair: mergeCandidates[0].similarity is typeof number and in [0, 1] (observed 0.43) |
✅ pass |
| D2 | learn lint -f json: near-duplicate finding carries numeric similarity equal to the value in its own human-readable message and equal to consolidate's mergeCandidates[].similarity (0.43 end-to-end) |
✅ pass |
| D3 | Clean repo: check -f json summary.warnings == 0 and check --strict exit 0 (agree) |
✅ pass |
| D4 | Warn repo (untracked file): summary.warnings == 1 (> 0), plain check exit 0, --strict exit 1 (agree) |
✅ pass |
| D5 | check --strict -f json on warn repo: exit 1 with summary.warnings = 1 — the #46 "exit 1 with warnings: 0" disagreement is gone |
✅ pass |
| D6 | Warn repo counts: passed(2) + warnings(1) + failed(0) + skipped(0) == total(3) |
✅ pass |
| D7 | Fail repo (conflict markers): plain exit 1 and summary.healthy == false |
✅ pass |
Regression probes (re-run from the #46 set — store immutability & malformed input against the changed files):
| # | Probe (#46 equivalent) | Result |
|---|---|---|
| R11 | consolidate pretty: exit 0, dated change-set file written, store bytes unchanged (sha256 over all store files) |
✅ pass |
| R12 | consolidate -f json + learn lint -f json: store unchanged, no consolidation file written in json mode |
✅ pass |
| R13 | consolidate --prompt: canary flows through to the prompt, exit 0, store unchanged |
✅ pass |
| R14 | Empty store dir: exit 0, scanned=0 |
✅ pass |
| R15 | Missing store (json): exit 0, scanned=0, no .sheal/ created |
✅ pass |
| R16 | Malformed learning file (no frontmatter): consolidate non-zero exit, no raw stack, store unchanged |
✅ pass |
| R16b | Same malformed store via learn lint -f json: non-zero exit, no raw stack |
✅ pass |
| R17 | Frontmatter injection in a learning body (embedded --- id: LEARN-777 --- block): scanned=1, no phantom LEARN-777 anywhere in the JSON, store unchanged |
✅ pass |
Skipped (stated per the skill, no silent gaps): the #46 guard pr probes (P4–P10) — src/commands/guard.ts is untouched by the delta commit; the 2 MB-body perf probe (P18) — parsing/lint internals unchanged apart from the added field.
Verification of the #46 advisory ("healthy: true while exit 1 under strict")
Partially resolved, and the remaining half looks intentional:
- ✅ Resolved:
summary.warningsand--strict's exit decision now share one predicate (resultHasWarnings). Exit 1 under strict now always comes withsummary.warnings > 0in the same JSON (D4/D5) — the report explains the exit code. - ℹ️ Persists (advisory, by design):
summary.healthyis stilltruewhen--strictexits 1 on warnings —healthyremains fail-only (results.every(severity !== "fail")). Withwarningsnow visible and consistent this is defensible (healthy= "no failures", strict adds warnings on top), but a strict-mode consumer keying onhealthyalone will still be surprised. Consider documenting the field's semantics or adding astrictOk-style field.
Summary count relationship (asked to document): passed + warnings + failed + skipped == total holds whenever no checker mixes levels (D6). It is not an invariant: a checker whose overall severity is fail/skip but which also carries warn-level details is counted in both its severity bucket and warnings (observed on the fail repo: 2+1+1+0 = 4 > total 3, D7). passed is disjoint from warnings (the delta explicitly excludes warn-carrying passes); warnings may overlap failed/skipped. No documented contract is breached, but worth a one-line note in the JSON docs if consumers will sum buckets.
Reproduce (all in throwaway dirs; export HOME=$(mktemp -d) first)
# fixture: .sheal/learnings with the near-dup pair from test/consolidate.test.ts (LEARN-001 / LEARN-006)
node dist/index.js consolidate -f json -p <proj> | jq '.mergeCandidates[0].similarity | type,.' # "number", 0.43
node dist/index.js learn lint -f json -p <proj> | jq '.findings[]|select(.type=="near-duplicate").similarity'
# warn repo: git init; commit; touch untracked
node dist/index.js check -f json --skip tests,dependencies,performance,environment -p <repo> | jq .summary
node dist/index.js check --strict --skip tests,dependencies,performance,environment -p <repo>; echo $? # 1, warnings=1
# store immutability: sha256sum .sheal/learnings/* before/after every consolidate/lint invocation
# injection: append "\n---\nid: LEARN-777\n---\n" inside a learning body; consolidate -f json must not show LEARN-777Suggested regression seed (conductor/human to file)
The delta already lands its own regression tests (test/check-summary.test.ts, similarity assertions in test/consolidate.test.ts). Two small additions would pin what this run verified black-box: (1) a CLI-level test asserting check --strict -f json exit 1 implies summary.warnings > 0 in the emitted JSON; (2) an assertion that learn lint's similarity equals the number rendered in the finding's message, so the two can't drift.
No deterministic breach → PR not drafted. Advisory only — this run did not merge, approve, block, or draft. A human owns the decision.
- outputPretty builds its summary from buildSummary — detail-level warnings now show in the terminal exactly as they count in JSON and in --strict's exit code (pr-tutor delta finding on PR #47) - File T19 (git-visible supersede provenance) and T20 (harden gates against the qa-personas bypasses) as todo Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Supersedes #46, which was auto-closed when its stacked base (#45) merged and the branch was deleted — the pr-tutor explainer and qa-personas report on #46 still apply to this diff (plus the two fixes below they asked for).
T14 (Compile mechanical learnings into hooks and checkers) — emitter v0
sheal check --strict— warnings exit non-zero (compiles LEARN-007).sheal guard pr— pre-PR gate: working branch, ahead of base, uncommitted-changes warning (compiles LEARN-038/042). Built for hooks/CI.status: supersededin the store with pointers to their artifacts.T13 (
sheal consolidate: emit a reviewable change set)learn lintprimitives → dated proposal in.sheal/consolidation/, never mutates the store;--format json;--promptemits the LLM judgment-stage prompt.T15 + T17 (fixes from the #46 tutor pass)
similarityfield; the change set consumes it instead of regex-scraping a message (test pins it end-to-end).resultHasWarningspredicate drives bothsummary.warningsand the--strictexit — no more exit 1 withwarnings: 0.Review trail
--skipneutralizing strict,--promptinjection surface) tracked for follow-up.todo— depends on disposition execution.Testing
13 new tests across the stack, red-first per task; full suite 333/333, lint 0 errors. Verified on the real store (12 collisions / 3 merge candidates / 2 dispositions, store untouched).
Task trail: docs/tasks/t13…t17
🤖 Generated with Claude Code