Retire the verbs, reads and views no surface reaches - #608
Merged
Merged
Conversation
Adds a fixture: one small record's event log as sharpen, recordReview, keep, replaceAnalysis, reverify, reinterpret, isUndecided, closeGate and undo wrote it. The test loads it through the Postgres event store and the graph projector and checks that happened, now, every list and why answer over it. RetiredOperation names the nine operations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FWZHuy7xJbVqxyLipnvwdY
…aches Write verbs: sharpen, recordReview, closeGate, reverify, isUndecided, undo, replaceAnalysis, keep, reinterpret, with their command shapes and the report codecs only they returned. Reads: pursuitsOf, originOf, whatWasKnown, criteriaGoverning, designHistory, reproductionOf, interpretationHistory, doTheseConflict, reproducibilityOf, whatDependsOn, how and learned, with their queries, codecs, the LearnedGroup, and the private helpers only they called. CLI views: renderPursuits, renderAffects, renderInterpretation, renderHistorical, renderHow, renderCriteria, renderDesign, renderLearned, renderEnquiry, renderOrigin, renderReproduction, renderReproducibility, renderContract, renderGate and renderConflict. views/analysis.ts, views/enquiry.ts and views/learned.ts had nothing else in them. The code that reads what these verbs wrote stays: why, now, the lists and happened still answer over a record holding their events (see tests/events/retired-operations-still-read.test.ts). The tests helper replaceAnalysis becomes reanalyse: a fresh run whose conclusions name what they replace through conclude --replacing. The tests are updated in the next commit; this one does not typecheck on its own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FWZHuy7xJbVqxyLipnvwdY
Tests that existed only to exercise a retired verb, read or view are deleted. Where a retired verb was a step on the way to a live read, the step is said with the remaining verbs: a replaced analysis is a fresh run whose conclusions name what they replace (`reanalyse`), a sharpened question is a note and a pose, a re-run is recordAnalysis plus conclude, a narrowed reading is a conclusion replacing the claim. Reads of retired reports are replaced by why, whySupported, gateStatus or the lists where the assertion was about something live. No record holds data worth carrying forward, so events of the retired operations are not kept readable: RetiredOperation and PromoteCommand are deleted, and so is the old-events fixture test added earlier on this branch. The CLI vocabulary drops twelve readings for words no remaining schema can produce. docs/scenarios is regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FWZHuy7xJbVqxyLipnvwdY
danbarua
enabled auto-merge (squash)
September 30, 2026 23:56
danbarua
added a commit
that referenced
this pull request
Oct 1, 2026
…ted enquiries say why (#611) The follow-up to #608, #609 and #610. Measured with the compiled binary (`bun run cli:build`) against throwaway records, always with `--db`. The "before" binary was built from `main` at e4250a4. ## 1. Reads stop handling shapes only the retired verbs wrote For each shape, I grepped every staged write in `packages/core-domain/write/*.ts` (`unitOfWork.node`/`edge`). `UnitOfWork.set` and `setEdge` have no callers, so no live write sets any property, `retracted` included. None of the shapes below is produced by a live write: | shape | written only by | what went | |---|---|---| | `review` kind in `why` | `recordReview` | **kept**, see below. Removed: the `ReviewRef` alias and the minted `reviewRef` codec | | sharpening arm of `originAlready` (Decision MOTIVATES Question + SHARPENS) | `sharpen` | the arm; `originAlready` now reads only the note | | `closed` gate state (Decision CLOSES Gate) | `closeGate` | the state, `GateStatus.closure`, the `closed` lookups in `gateStatus`/`gateList`, the `explainGate` case, `closed` in `GATE_STATES` and in the `why` descriptions | | `undecided` standing (GRADES) | `isUndecided` | the verdict, the standing, the GRADES lookup in `standingsConferred`, the view branch, the vocabulary word | | retracted nodes | `undo` | every `retracted IS NULL` / `retracted !== true` filter in `explain`, `happened`, `inventory`, `standing`, `core`; `everGated` and the unlabelled `ever` match in `workList`; `retractedIn` and the `retracting` line in `happened` | | `revisedBy` (Decision MOTIVATES/SUPERSEDES Computation) and the review behind it | `replaceAnalysis` | `revisedBy`, `impliedSupersession`, `conclusionsOf`, the BASED_ON-review edge in `concluding`; the Review match and `because` on `analysisRevision`; the Review lookups in `whySupported` (EVALUATES, BASED_ON Review, INVALIDATED_BY) | **Not done: the `review` kind.** `search` walks every searchable label in `SEARCHABLE_TEXT` (core-db/domain.ts) and refuses a label with no kind. `Review` is one of them. With the kind removed, every search failed: ``` $ labkit --db …/rec1 --no-ansi search seed labkit: Review is searchable but names no research-concept kind exit 1 ``` Removing the kind needs one of two changes that this PR does not make: take `Review` out of the schema's searchable annotations, or make `search` skip a label with no kind (its comment says that case should fail loudly). The kind is back, and so is ", review" in the `why` descriptions. Tests: the undecided view test and the undecided verdict case are gone; the `workStateFrom` test lost its retracted-gate case; `tests/events/retraction.test.ts` is gone, since it checked that reads hide retracted nodes. `closed` leaves the CLI vocabulary because `tests/cli/vocabulary.test.ts` failed once no schema could say it (`+ "closed"`). The enquiry list still prints `closed`, uncoloured. The same reads over two records, before and after: the throwaway record (`happened` 52 lines, `now` 16 lines, `why CLM_6` on a replaced claim) and the rebuilt Bonsai record (`now` 82 lines, `claims` 51, `enquiries` 29, `work` 11, `gates` 20). `diff` printed nothing for any of them. The one visible change is `gates --state closed`: ``` before: Gates — 0 / nothing (exit 0) after: labkit: Invalid option: expected one of "never-evaluated"|"incomplete"|"blocked"|"satisfied" (exit 1) ``` `bun run bonsai:record` with this binary: ``` 107/107 criteria recorded 107/107 evaluations recorded, 57 citing a measurement gate GATE_243, task TASK_135, enquiry LOE_134, 107 criteria/evaluations from experiments/stage2b_denoising/gates.toml OK: the live event stream and graph state are exactly what the scripts produce, commit hashes and clocks aside. OK: record built (…/bin/labkit --db …/tmp/bonsai), replay clean. ``` ### What nothing writes any more Graph schema and migrations are unchanged. Declared: 14 node labels and 31 edge labels; written: 13 and 25. - **Label:** `Review`. - **Edge labels:** `REVERIFIES`, `GRADES`, `KEEPS`, `SHARPENS`, `EVALUATES`, `INVALIDATED_BY`. - **Endpoint pairs on edges still written elsewhere:** MOTIVATES Decision→Question, MOTIVATES Decision→Computation, SUPERSEDES Decision→Computation, CLOSES Decision→Gate, BASED_ON Decision→Review, GATES Gate→Computation, PRODUCES Task→Computation, PRODUCES Task→Artefact, and CONCERNS/MENTIONS Note→Review. - **Properties:** `retracted` (nothing calls `UnitOfWork.set`). `ClaimProps.kind` still allows `"undecided"`. `provisioning.ts` still installs the `<label>_hide_retracted` RLS policy, and with `retraction.test.ts` gone no test exercises it. Reads of these that were outside this brief and are still in place: `reverifiedBy`/REVERIFIES in `whySupported` and the "Re-checked by" view; the Computation lineage, KEEPS and the changed/restated/unpaired lists in `analysisRevision`, which now always return the empty shape; and the withdrawn-verdict helper in `checks.ts`. ## 2. `labkit gates` lines its columns up with colour on `rows()` padded by string length, escape codes included. It now pads by visible width. The test renders `renderGateList` with colour on, strips the escape codes, and compares the result with the plain rendering. It failed before the fix: ``` - "blocked GATE_1 no publication + "blocked GATE_1 no publication ``` Compiled binary, `FORCE_COLOR=1 labkit gates | strip-ansi`: ``` before: after: never-evaluated GATE_4 no publication never-evaluated GATE_4 no publication holding up TASK_3 write it up holding up TASK_3 write it up ``` ## 3. The record daemon's log lines take the CLI's colour decision `colourWanted` moves to `packages/core-db/colour.ts`, because core-db cannot import app-cli. `stderrLine` sits beside it and writes one line to stderr, in red when `colourWanted` says so. The daemon's `log()`, the wire host's two lines, the daemon entry's usage line, the client's "replacing it" line and the CLI's refusals all go through it. core-db has no access to `--no-ansi`, so the daemon decides from the environment alone. Bun colours `console.error` whenever `FORCE_COLOR` is set, even beside `NO_COLOR=1`: ``` $ echo 'console.error("x")' > ce.ts $ NO_COLOR=1 bun ce.ts 2>&1 | od -c # FORCE_COLOR=3 inherited from the shell 033 [ 0 m 033 [ 3 1 m x 033 [ 0 m \n ``` Daemon log, fresh `--db` each, under `FORCE_COLOR=3 NO_COLOR=1`: ``` before: ^[[0m^[[31m2026-10-01T00:30:39.226Z [labkit daemon 63989] serving …^[[0m after: 2026-10-01T00:30:42.025Z [labkit daemon 64191] serving … ``` `tests/persistence/daemon-log-colour.test.ts` runs the daemon entry under `FORCE_COLOR=1` (the control, which must be coloured), `FORCE_COLOR=3 NO_COLOR=1` and `FORCE_COLOR=0`. ## 4. The `--http` launcher test clears the colour variables for its child The child now gets `process.env` without `FORCE_COLOR`, `NO_COLOR`, `CI` and `TERM`, which are the variables `colourWanted` reads. ``` before: FORCE_COLOR=3 bun test packages/app-acp/http.test.ts Received: "http://127.0.0.1:55791/acp\u001B[0m" 13 pass, 1 fail after: FORCE_COLOR=3 bun test packages/app-acp/http.test.ts 14 pass, 0 fail ``` ## 5. `labkit why` on an accepted enquiry prints the reason and the reopening condition ``` before: LOE_1 is open — its question is accepted as unresolved because - (Q_1) does the schedule move convergence? — currently accepted after: LOE_1 is open — its question is accepted as unresolved because - (Q_1) does the schedule move convergence? — currently accepted accepted because: the effect is below seed noise reopens if: a run with ten seeds ``` The `accept` help text now points at `labkit why` and no longer at `labkit --json why`. `tests/cli/enquiries.test.ts` asserts both lines through the CLI. ## 6. The deleted root README `infra/ci/triggers.tf` no longer lists `README.md` in `ignored_files`. `infra/ci/README.md` had the same list in prose, and it is updated too. No other file referred to the root README; every other `README.md` hit is a package README or test data. ## Not in this PR: removing the MCP read-only mode The coordinator also passed on a request to remove `labkit mcp --read-only`, the `readOnly` paths in `app-mcp/server.ts` and `docs.ts` with their tests, and the `MCP_CALL_WRITE` switch in the skill's `mcp-call.sh`. I made the change, and typecheck, the MCP tests (22 pass) and `mcp-call.sh tools/list` (9 tools, 4 with `readOnlyHint`) all passed. The permission system then refused the commit as test removal. The change is not in this branch. It is saved as a patch outside the repo for Dan to decide on. ## Checks - `bun test`, before: 1644 pass, 2 skip, **1 fail** (`http.test.ts` under this shell's `FORCE_COLOR=3`), 1647 tests across 221 files. - `bun test`, after: **1647 pass, 2 skip, 0 fail**, 1649 tests across 221 files. The difference: +1 gates-colour test, +3 daemon-colour tests in a new file, −1 undecided view test, −1 `retraction.test.ts` (one test, one file). - `bun run check:quick`: all 25 passed. - Rebased onto `origin/main` (e4250a4) before pushing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01FWZHuy7xJbVqxyLipnvwdY --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this retires
Anything that no CLI command or MCP tool reaches. I checked each item with a grep of
read./write.calls in app-cli, app-mcp, app-web, app-vscode, view-model and records. The only verbs those call are the ones listed inpackages/app-cli/commands/*.tsandpackages/app-mcp/tools.ts, and none of the items below is among them.packages/core-domain/write/):sharpen,recordReview,closeGate,reverify,isUndecided,undo,replaceAnalysis,keep,reinterpret. Their bodies, their command shapes incommands.ts, and the report codecs only they returned are gone.Revisingnow holds onlyisConfirmed.packages/core-domain/read/):pursuitsOf,originOf,whatWasKnown,criteriaGoverning,designHistory,reproductionOf,interpretationHistory,doTheseConflict,reproducibilityOf,whatDependsOn,howandlearned. Also gone:LearnedGroupandread/learned.ts;queries.ts;reports.tsandreport.ts;sideOf,restingOnArtefact,artefactNamed,amendmentChain,assertedBy,decidedOnTheStrengthOf,findingFor,conclusionEvents,enquiryOf,supersessionOf.whyandnowcall stays.explain.tscallsanalysisRevision,contractFor,criterionStanding,enquiryInContext,enquiryStatus,gateStatus,neighboursOf,proseFor,stoppedWork,whatIsKnown,whySupportedandworkList, plusreachablethroughwhy. I checked with a grep ofself.*.tests/cli/views.test.tscalled:renderPursuits,renderAffects,renderInterpretation,renderHistorical,renderHow,renderCriteria,renderDesign,renderLearned,renderEnquiry,renderOrigin,renderReproduction,renderReproducibility,renderContract,renderGate,renderConflict, and thepartLineformatter.views/analysis.ts,views/enquiry.tsandviews/learned.tsheld nothing else.RetiredOperationandPromoteCommandare deleted. No record holds data worth carrying forward. Schema and migrations are unchanged; the edge labels inpackages/core-db/domain.tsstay.tests/cli/vocabulary.test.tsrequires that).app-mcp/schemas.tsloses 18 re-exports of deleted codecs. They sit next to theregisteredSessionline, so git may report a conflict with the PR that dropsregister_session. Keep both deletions.Lines
Against
main(37b97f1): 100 files, +455 / −9,639.packages/tests/docs/scenariosTests
Counts (full
bun test --timeout 20000, read from the output file):The one failure is the same test both times:
packages/app-acp/http.test.ts"the CLI serves --http until SIGTERM…". It fails on an ANSI reset in the URL it reads, and this PR does not touch it.Deleted test files, and why nothing live lost coverage. Each one tested only a retired verb or a retired read:
tests/domain/how.test.ts:howonly.tests/consumer/historical_survey.test.ts:whatWasKnownonly.tests/domain/survey-after-reinterpretation.test.ts: howreinterpretitself behaves.s9c,s9e:reproducibilityOfonly.s10b,s10c,s10d,s10e:reverifywithreproductionOf,reproducibilityOfandwhatDependsOn.s11c,s11d:whatDependsOnandreproducibilityOfonly.s11b: the reason a finding was superseded was the verdict of the review thatreplaceAnalysiscited. Only that verb wrote this.s12b: chains ofreinterpretread throughinterpretationHistory.s20:isUndecided, which has no remaining equivalent.s24,s24b:undo, which has no remaining equivalent.Rewritten in the remaining verbs where a retired verb was one step on the way to a live assertion:
replaceAnalysisbecamereanalyse: a fresh run whose conclusions name what they replace, throughconclude --replacing;sharpenbecamenote+pose;reverifybecamerecordAnalysis+conclude;reinterpretbecame aconcludeon the same analysis with the narrower proposition,replacingthe claim;why,whySupported,gateStatusor a list read.Each rewrite was checked by removing the
replacingit depends on, and the test then failed:domain-session's withdrawn-verdict test,s3b, and five ofs3c's tests. Where an assertion held only because of the retired verb's graph shape, it was deleted rather than weakened:analysisRevision;s11g;s4'swhyon the replacement.One test that was not checking anything is fixed:
s9b's comparison read an empty bucket in both worlds.Evidence
bun run check:quickpasses (typecheck, depcruise, everycheck:*, includingcheck:scenario-docsafterbun run scenarios:build).bun run bonsai:recordpasses withgates.tomlfrom the Bonsai checkout, ending "OK: record built …, replay clean." The probe scripts call only CLI commands, so none of them called a retired verb.Follow-up, not done here
Now that old records need not be read, some live read code handles only shapes the retired verbs wrote. It is still reachable through
why, but no remaining verb produces these shapes:reviewkind inwhy;SHARPENSinoriginAlready;closedgate state;undecidedstanding;retractednodes, and theundoline inhappened;revisedByandINVALIDATED_BYlineage.Retiring it is a separate decision.
The
renderGateListcolumn misalignment in colour (rows()pads by string length including escape codes) is an existing defect. The deletedrenderGatepadding test was its only coverage.🤖 Generated with Claude Code
https://claude.ai/code/session_01FWZHuy7xJbVqxyLipnvwdY