labkit list <node_type> shows the stored nodes of one type; get and list embed two hops by default - #609
Merged
Conversation
…wo hops by default `list` reads `labkit_get_collection_as_hal` through a new `collection` read, with `--offset`, `--limit` (default 25, at most 200) and the global `--depth`. An unknown type is refused before the record opens, naming every type in the domain's label list. `get` and `list` share a help group, "The record as stored", and `--depth` defaults to 2 for both. Plain output indents each farther hop beneath the neighbour it is reached from. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FWZHuy7xJbVqxyLipnvwdY
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FWZHuy7xJbVqxyLipnvwdY
danbarua
enabled auto-merge (squash)
October 1, 2026 00:08
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.
labkit get <handle>andlabkit list <node_type>show the record as stored, for an agent that wants the raw nodes rather than an answer. Both take--jsonand--depth <n>, and--depthnow defaults to 2 for both.What changed
list <node_type>readspublic.labkit_get_collection_as_hal(p_tenant_id, p_label, p_offset, p_limit, p_depth). It goes through a newcollectionread onReadSurface, withcollectionQueryinpackages/core-domain/queries.tsandTenantGraph.collectionAsHalinpackages/core-db/graph.ts, the same routegettakes throughresource/entityAsHal.app-cliimports nothing fromcore-dbfor it.--offset(default 0) and--limit(default 25, at most 200), plus the global--depth.NODE_LABELSthrough the schema's enum. The<node_type>help lists them from the same place.getdoes, under aClaim 1-2heading, and ends withMore: --offset Nwhen the procedure returns anextlink.--jsonemits the procedure's HAL document unchanged.getnow defaults to depth 2. Before, the default was 1.--depth, still global. It defaults to 2, is refused unless it is a whole number from 0 to 6, and its help names both commands.DEFAULT_DEPTH,MAX_DEPTH,DEFAULT_PAGEandMAX_PAGEare each defined once inqueries.ts, and the help text reads them.READ_GROUPSafter "What was done".getmoved there from "Finding a handle", andlistjoined it. Each description says it shows the record with no interpretation, andget's says thatwhyanswers the question whilegetshows what the answer was drawn from.get's plain output used to show only the first hop. Each farther hop is now indented beneath the neighbour it is reached from. This also changesget's plain output.Choices to overrule
--limitis 25, the same as the HTTP collection handler's default. The procedure has no default of its own.getmoved out of "Finding a handle".How the procedure compares with the brief
limitat 200 anddepthat 6. The CLI validates both before the record opens.NODE_LABELS. The CLI refuses an unknown type before the procedure is called._linksare relative HTTP paths (/collections/...,/graph/...).--jsonemits them as they are.Not done: an out-of-range
--limitor--depthgets zod's generic message, for exampleToo big: expected number to be <=200, which does not name the flag.Tests
tests/persistence/collection-as-hal.test.ts(2 tests, at theTenantGraphlevel whereentityAsHalis tested):limitandoffsetpage through three claims, with the rightnext/prevlinks. Each item equalsentityAsHalfor the same handle at depth 0 and at depth 2.tests/cli/record-as-stored.test.ts(3 tests, run throughbuildProgram):getembeds two hops by default,--depth 1narrows it to one, and the plain output shows the second hop.list Claim --limit 1returns the itemgetreturns, and its plain output saysMore: --offset 1.list Clamis refused before anything runs, and the message names every entry ofNODE_LABELS.bun test tests/persistence/collection-as-hal.test.ts tests/cli/ packages/app-web/tests/collection-as-hal.test.ts: 65 pass, 0 fail.bun test tests/mcp tests/domain tests/consumer: 113 pass, 0 fail.bun run check:quick: all 25 passed.check:cli: 23 assertions passed.check:binary: OK.Measured with the compiled binary
The binary was built with
bun run cli:buildand run against a throwaway record created with--dbin a scratch directory. It was seeded through the write commands: an enquiry, observations, an analysis, two concluded claims (CLM_4,CLM_5), and a note onCLM_4.labkit get CLM_4with no--depthshows the second hop (ART_3,EU_3beneathEV_4).--depth 1does not.🤖 Generated with Claude Code
https://claude.ai/code/session_01FWZHuy7xJbVqxyLipnvwdY