Skip to content

Retire the verbs, reads and views no surface reaches - #608

Merged
danbarua merged 3 commits into
mainfrom
retire-unreached-verbs
Sep 30, 2026
Merged

danbarua merged 3 commits into
mainfrom
retire-unreached-verbs

Conversation

@danbarua

Copy link
Copy Markdown
Owner

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 in packages/app-cli/commands/*.ts and packages/app-mcp/tools.ts, and none of the items below is among them.

  • Write verbs (packages/core-domain/write/): sharpen, recordReview, closeGate, reverify, isUndecided, undo, replaceAnalysis, keep, reinterpret. Their bodies, their command shapes in commands.ts, and the report codecs only they returned are gone. Revising now holds only isConfirmed.
  • Reads (packages/core-domain/read/): pursuitsOf, originOf, whatWasKnown, criteriaGoverning, designHistory, reproductionOf, interpretationHistory, doTheseConflict, reproducibilityOf, whatDependsOn, how and learned. Also gone:
    • LearnedGroup and read/learned.ts;
    • their queries in queries.ts;
    • their codecs and types in reports.ts and report.ts;
    • the barrel exports;
    • the private helpers only they called: sideOf, restingOnArtefact, artefactNamed, amendmentChain, assertedBy, decidedOnTheStrengthOf, findingFor, conclusionEvents, enquiryOf, supersessionOf.
  • What why and now call stays. explain.ts calls analysisRevision, contractFor, criterionStanding, enquiryInContext, enquiryStatus, gateStatus, neighboursOf, proseFor, stoppedWork, whatIsKnown, whySupported and workList, plus reachable through why. I checked with a grep of self.*.
  • CLI views that nothing outside tests/cli/views.test.ts called: renderPursuits, renderAffects, renderInterpretation, renderHistorical, renderHow, renderCriteria, renderDesign, renderLearned, renderEnquiry, renderOrigin, renderReproduction, renderReproducibility, renderContract, renderGate, renderConflict, and the partLine formatter. views/analysis.ts, views/enquiry.ts and views/learned.ts held nothing else.
  • Old events: RetiredOperation and PromoteCommand are deleted. No record holds data worth carrying forward. Schema and migrations are unchanged; the edge labels in packages/core-db/domain.ts stay.
  • CLI vocabulary: twelve readings are removed for words no remaining schema produces (tests/cli/vocabulary.test.ts requires that).

app-mcp/schemas.ts loses 18 re-exports of deleted codecs. They sit next to the registeredSession line, so git may report a conflict with the PR that drops register_session. Keep both deletions.

Lines

Against main (37b97f1): 100 files, +455 / −9,639.

Area Files Added Removed
packages/ 28 39 3,163
tests/ 50 376 6,031
docs/scenarios 22 40 445

Tests

Counts (full bun test --timeout 20000, read from the output file):

Tests Files Pass Skip Fail
Before 1,765 232 1,762 2 1
After 1,632 216 1,629 2 1

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: how only.
  • tests/consumer/historical_survey.test.ts: whatWasKnown only.
  • tests/domain/survey-after-reinterpretation.test.ts: how reinterpret itself behaves.
  • s9c, s9e: reproducibilityOf only.
  • s10b, s10c, s10d, s10e: reverify with reproductionOf, reproducibilityOf and whatDependsOn.
  • s11c, s11d: whatDependsOn and reproducibilityOf only.
  • s11b: the reason a finding was superseded was the verdict of the review that replaceAnalysis cited. Only that verb wrote this.
  • s12b: chains of reinterpret read through interpretationHistory.
  • 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:

  • the test helper replaceAnalysis became reanalyse: a fresh run whose conclusions name what they replace, through conclude --replacing;
  • sharpen became note + pose;
  • reverify became recordAnalysis + conclude;
  • reinterpret became a conclude on the same analysis with the narrower proposition, replacing the claim;
  • reads of retired reports became why, whySupported, gateStatus or a list read.

Each rewrite was checked by removing the replacing it depends on, and the test then failed: domain-session's withdrawn-verdict test, s3b, and five of s3c's tests. Where an assertion held only because of the retired verb's graph shape, it was deleted rather than weakened:

  • the lineage decision behind analysisRevision;
  • the implied pairing in s11g;
  • s4's why on 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:quick passes (typecheck, depcruise, every check:*, including check:scenario-docs after bun run scenarios:build).
  • bun run bonsai:record passes with gates.toml from 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:

  • the review kind in why;
  • SHARPENS in originAlready;
  • the closed gate state;
  • undecided standing;
  • retracted nodes, and the undo line in happened;
  • revisedBy and INVALIDATED_BY lineage.

Retiring it is a separate decision.

The renderGateList column misalignment in colour (rows() pads by string length including escape codes) is an existing defect. The deleted renderGate padding test was its only coverage.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FWZHuy7xJbVqxyLipnvwdY

danbarua and others added 3 commits October 1, 2026 00:29
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
@github-actions github-actions Bot added the area:record The record: core-domain, core-db, the CLI, the MCP server, the domain model and workspaces label Sep 30, 2026
@danbarua
danbarua enabled auto-merge (squash) September 30, 2026 23:56
@danbarua
danbarua merged commit 5a0a85b into main Sep 30, 2026
10 checks passed
@danbarua
danbarua deleted the retire-unreached-verbs branch September 30, 2026 23:57
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:record The record: core-domain, core-db, the CLI, the MCP server, the domain model and workspaces

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant