Skip to content

fix(cli): four more read-only sentences name what they vouch for, and the journal-mode switch on a SQLite file - #24053

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-23899-read-only-arms-four-more
Oct 16, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-23899-read-only-arms-four-more

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #23899
Clause-②: no

The remainder of #23726, by the pattern PR #23608, PR #23725 and PR #23895 set. Four sentences, printed by three commands after their boot, said that nothing was written. On a SQLite file still on a rollback journal, that boot's connect switches the journal mode, which rewrites the file header and no row. Each sentence now names what it vouches for, and on a SQLite target the line that carries it is followed by the exported JOURNAL_MODE_SWITCH_NOTE: "Opening a SQLite file can still switch its journal mode (to WAL by default), which changes its header only."

The basis for Clause-②: no is the claim's: operator text only. No flag, exit code, prompt, refusal, accepted input or export signature moves, no --json document gains or loses a key, and there is no packages/spec edit.

The premise, measured at the door, before and after

The real CLI through packages/cli/bin/run-dev.js (tsx), with @objectstack/objectql rebuilt at each sha. Before: bb14934cd (base). After: c4adb868c (this head). Every run got its own copy of the fixture and a fresh OS_HOME, with the database env vars unset. Header bytes 18 and 19 are the journal mode (1,1 rollback journal; 2,2 WAL); the counter is bytes 24 to 27. "Content" is an md5 over the schema plus every row of every table, read on a connection of my own.

  • CRM fixture: examples/app-crm compiled with os build, provisioned by os migrate apply --yes, plus one sys_legacy_widget table with one row, then put on journal_mode = delete (72 tables, 1 row).
  • Row 2 runs from a directory whose objectstack.config.ts carries one plugin: its kernel:ready hook calls create() on the driver (2a) or execute('SELECT count(*) FROM sys_metadata') (2b).
  • Row 4: an empty directory, a file provisioned by one os secret rewrap --apply --yes, then put on journal_mode = delete (10 tables, 2 rows).
# Run Header 18,19 · counter Content Printed before Printed after
1 os migrate plan (CRM) 1,1 · 5 to 2,2 · 6 (both shas) unchanged "They are reported for information only — nothing here drops them, and a plan writes nothing." "… and a plan writes no row or table." plus the note
2a os migrate plan, a plugin write refused 1,1 · 5 to 2,2 · 6 (both) unchanged "Refused 1 write(s) during the declaration boot — a plan writes nothing: create() on sys_metadata. …" "… — a plan writes no row or table: create() on sys_metadata. …" plus the note at the end of that note's line
2b os migrate plan, a raw read forwarded 1,1 · 5 to 2,2 · 6 (both) unchanged "… If those commands only read, nothing was written; if one wrote, it landed. …" "… If those commands only read, no row or table was written; if one wrote, it landed. …" plus the note at the end of that note's line
3 os migrate summary-nulls --recompute-undefined-on-empty crm_account.nope, dry run and --apply --yes (CRM) 1,1 · 5 to 2,2 · 6 (both modes, both shas) unchanged "… Refused before any row was read; nothing was written." "… Refused before the walk read or wrote any row." plus the note
4 os secret rewrap --apply --yes --no-declared-datasources, a finished re-run (0 re-wrapped) 1,1 · 4 to 2,2 · 5 (both) unchanged "… and a finished run writes nothing." "… and a finished run writes no sys_secret row." plus the note
4 the same, dry run 1,1 · 4 to 2,2 · 5 (both) unchanged as above "… writes no sys_secret row." The closing line already carries the note (PR #23725)

All four rows print after the connect, so none of them was left as it was. Rows 2 and 3 were read, not measured, on the card; they are measured here for the first time.

The --json doors. os migrate plan --json carries the guard note in composition.notes (row 2a measured: the new sentence, no note; key set identical to base, 22 keys). The unmanaged-table section is the unmanagedTables report object and carries no sentence. os migrate summary-nulls --json carries the engine's refusal in error (the new sentence, no note; code: INVALID_FIELD, httpStatus: 400). os secret rewrap --json carries report.notes[1] (the new sentence, no note), in both modes; the key set and the notes' count and order are identical to base.

A non-SQLite target, at the door: os migrate plan --database-url :memory: from the 2a directory prints the 2a sentence with no note (0 occurrences). The pins below add the PostgreSQL case for every text.

Why two sentences name a narrower noun than "no row or table"

Two runs boot plain, not read-only, and that boot writes on a database that has never been started. Measured at base on a fresh file (one placeholder table, rollback journal):

  • os migrate summary-nulls --apply --yes --recompute-undefined-on-empty crm_account.nope: 1 table to 14 tables, rows unchanged, then the refusal.
  • os secret rewrap --apply --yes --no-declared-datasources with nothing to re-wrap (a finished run, exit 0): 1 table to 11 tables and 1 row to 3 (two sys_migration rows), and the "Re-running is safe" note printed.

So "no row or table" would be false there. Row 3 now vouches for the walk ("Refused before the walk read or wrote any row."), which is what backfillSummaryNulls controls, and row 4 for the rows the re-wrap writes ("a finished run writes no sys_secret row"). Both are true in either mode. Rows 1 and 2 are printed only by os migrate plan, whose boot defers DDL and opens no missing file, so they claim no row or table.

How the note reaches each line

The test is the one PR #23725 uses: describeDriverSqliteFile(stack.driver?.config) !== undefined (utils/connection-display.ts, read and not edited). The note is appended by the command that prints the line, in the text output only. No parameter was added anywhere, so no export signature moves.

  • Rows 1 and 2, migrate/plan.ts. The composition notes and the unmanaged-table section are printed through one helper: a line that claims "no row or table" is followed by the note on a SQLite file. That is how the command finds the guard's note among the composition notes without a new export from the guard; the pin feeds it the REAL guard's text, so a guard rewording that drops the claim turns the pin red. The --json document is unchanged in shape.
  • Row 3, migrate/summary-nulls.ts. The command's catch appends the note to an INVALID_FIELD error on a SQLite file. Inside that try, INVALID_FIELD has one producer: backfillSummaryNulls's scope refusal (its only throw; the walk records failures rather than throwing, and the other refusals there are OBJECT_NOT_FOUND or plain errors). Both modes. The --json envelope carries the engine's message as it is.
  • Row 4, secret/rewrap.ts. renderReport takes a map from a note's text to what follows it; the "Re-running is safe" note gets the note under --apply on a SQLite file. A dry run's closing line already carries it, so there it carries none (one copy per run, pinned). REWRAP_NOTES stays loaded at the point of use, as before.

report.notes readers, by git grep over the repo: the resumable note's text had no reader outside its own definition and the docs mirror. sys-secret-rewrap.test.ts's RESUMABLE case title names the executor's own writes (it asserts the store's write count), not this text, and is unchanged.

The texts

# Where FROM TO
1 cli/src/utils/unmanaged-tables.ts:508 "… and a plan writes nothing." "… and a plan writes no row or table."
2a cli/src/utils/schema-migration-plugins.ts:1148 "— a plan writes nothing" "— a plan writes no row or table"
2b cli/src/utils/schema-migration-plugins.ts:1163 "If those commands only read, nothing was written;" "If those commands only read, no row or table was written;"
3 objectql/src/summary-backfill.ts:300 "Refused before any row was read; nothing was written." "Refused before the walk read or wrote any row."
4 cli/src/utils/sys-secret-rewrap.ts:507 (REWRAP_NOTES.resumable) and content/docs/deployment/cli.mdx:652 "… and a finished run writes nothing." "… and a finished run writes no sys_secret row." The docs mirror adds the SQLite note in one sentence.

Pins (one per text, a SQLite and a PostgreSQL case where the note is conditional)

  • Row 1: cli/src/utils/unmanaged-tables.test.ts gains one case pinning the closing line byte for byte. cli/src/commands/migrate/plan.journal-mode-note.test.ts (new) runs the real command with the boot, the occupancy probe and the sweep's READ replaced (the renderer is real): SQLite gets exactly "line + note", PostgreSQL exactly the line and no note anywhere.
  • Row 2: the same new file, for 2a and 2b, with each guard note produced by a real createDeclarationBootWriteGuard() armed over a fake driver, made to refuse one create() or forward one execute(), and disarmed. One more case pins --json: composition.notes equals the guard's note, and the document carries no note. In schema-migration-plugins.declaration-boot-write-guard.test.ts, every assertion on the old phrase moved to the new one, the negative ones included, so none of them went vacuous (11 occurrences); the raw-execute case now pins the 2b sentence itself. schema-migrate.host-composition.integration.test.ts and schema-migrate.deferred-ddl.integration.test.ts moved the same way.
  • Row 3: objectql/src/summary-backfill.test.ts's REFUSES case pins the closing sentence for all six refusal shapes. cli/src/commands/migrate/summary-nulls.test.ts gains five cases with the engine's REAL refusal (vi.importActual): dry run and --apply, each SQLite and PostgreSQL, and --json (the envelope's error is the engine's message, no note).
  • Row 4: cli/src/commands/secret/rewrap.guards.test.ts gains four cases: --apply SQLite (the note follows the line), --apply PostgreSQL (no note), a dry run (the line carries none; the run carries the note once), and --json in both modes (report.notes holds the new text, no note).
  • Tier: the new plan file value-imports bootSchemaStack to mock it, so vitest-tiers.ts's KERNEL signal puts it in integration, beside summary-nulls.test.ts and rewrap.guards.test.ts.

Ablation (one per wording)

At c4adb868c, one run under the verify lock: a baseline, then each new wording put back to its old text through scripts/ablation-replace.mjs (WRAP mode) with an EXIT/INT/TERM trap restoring from HEAD, every pin file re-run in each leg (objectql/src/summary-backfill.test.ts plus the seven CLI pin files). Each leg: the anchor hit once and fell 1 to 0, the blob changed, and the restore was proven by blob equal to HEAD, git diff HEAD empty and git status --porcelain empty.

Leg Blob Red (only these) Green
baseline none objectql 20 of 20; CLI 13 files, 150 of 150
1 unmanaged-tables 6add5c0c8d26 to c647aec174bc 3: the render pin and both plan cases for the line objectql 20; CLI 115 of 118
2a guard refusal 498e46872d08 to 0fa153eef1a4 7: both plan cases for 2a, four guard cases, host-composition THE FIX objectql 20; CLI 111 of 118
2b guard raw 498e46872d08 to e2786928219d 3: both plan cases for 2b, the guard's raw-execute case objectql 20; CLI 115 of 118
3 summary-backfill 2fb88f18451d to f83de26a8bc2 1: objectql REFUSES CLI 118 of 118
4 rewrap resumable 3db476f2ebce to ec49817fb26f 4: the four new rewrap cases objectql 20; CLI 114 of 118

Leg 3's CLI cases stayed green, as expected: they read the engine's refusal through @objectstack/objectql's exports, so dist/, which this source mutation did not rebuild. The text's pin is the objectql one, which resolves src/. The CLI cases pin the note's follow-through, not the text. No other leg needed a rebuild: every other subject is the CLI's own src, reached by relative import.

Verification (head c4adb868c)

  • Build: turbo run build --filter='@objectstack/cli^...' --concurrency=2: 60 tasks, VERDICT command-exit 0; then @objectstack/objectql rebuilt after the edit (14 tasks, VERDICT 0). The new sentence is in objectql/dist/index.js and index.mjs (1 hit each; the old one: 0).
  • Tests: the touched CLI files by path (8 files, 132 tests passed, VERDICT 0); the baseline leg above (objectql summary-backfill.test.ts 20 of 20; CLI 13 files including the four plan.*.integration.test.ts, rewrap.driver-contract.test.ts and sys-secret-rewrap.test.ts, 150 of 150). These are integration-tier files, run by path; the rest of that tier is declared to CI. The CLI unit tier (vitest run --project unit --maxWorkers=2): Test Files 307 passed (307), Tests 4574 passed (4574), exit 0. @objectstack/objectql (vitest run --project local --maxWorkers=2): Test Files 436 passed (436), Tests 8229 passed (8229), exit 0.
  • Typecheck: @objectstack/cli exit 0 (check:test-typecheck held at 3 files, 28 errors, 6 signatures); @objectstack/objectql exit 0 (held at 39 files, 233 errors, 64 signatures). tsc --listFilesOnly -p tsconfig.json puts all seven touched CLI test files in its 1607-file program.
  • Lint, by the narrowing: pnpm exec eslint --no-inline-config over the 15 changed .ts files exited 0. The repo-wide pnpm lint is CI's Lint & Repo Gates.
  • Gate families: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 99 commands from merge base bb14934cd (17 paths, 506 changed lines). All 99 ran at c4adb868c, each exit captured before any pipe. 94 exited 0 on the first run; five refused on a prerequisite (exit 3: no built dist/ for the CLI, some plugins or client-react) and exited 0 when re-run after turbo run build --filter='!@objectstack/docs' --concurrency=2 (75 tasks, 74 cached, VERDICT 0): check:skill-examples, check:dual-build-cjs-loads, check:i18n, check:i18n-coverage, check:i18n-walk-parity. check-engine-split-ratio.mjs --days 90 ran after git fetch --shallow-since=2026-07-10 (exit 0). --ran printed "99 derived, 99 run, 0 NOT-MEASURED, 0 UNRUN". The derivation flagged one input changed upstream since the base (scripts/engine-double-contract.pinned.json); this diff adds no engine double.

Acceptance notes

  • No merge of origin/main before opening. 6 commits landed since the base (spec, rest, runtime, the hono adapters, cloud-connection, a dogfood test, engine-double-contract.pinned.json); none touches packages/cli, packages/objectql or a file this diff reads. git merge-tree --write-tree HEAD origin/main exits 0. A declared narrowing: CI's merge-ref run covers the joint tree.
  • Left as they are, by the claim's "each sentence only": the developer comments in schema-migration-plugins.ts that quote the old phrase "a plan writes nothing" (lines 130, 187, 201, 776, 783, 937, 1680), the describe title of the host-composition case (a plan writes nothing even when the host writes from init()), and sys-secret-rewrap.ts's module docblock ("Re-running a finished run writes nothing", about the executor's own writes).
  • os migrate apply prints the same guard note (its composition notes, apply.ts:261), now with the new wording and without the journal-mode note: an apply's boot is followed by its own writes, and the claim's file surface names os migrate plan. Not measured.
  • The neighbouring class stays as it is (migrate/apply.ts:551, organization-ownership-apply.ts), per the hold-off.

Built by session_018t6a5rvqPRvEDMCVVW9kYT, the dev for domain:cli seat 2's claim 6223113446.


Generated by Claude Code

… the journal-mode switch on a SQLite file

os migrate plan (the unmanaged-table section and the declaration boot's
write-guard note), a refused summary-nulls scope, and the rewrap
"Re-running is safe" note stop saying nothing was written. On a SQLite
target each line that makes the claim is followed by
JOURNAL_MODE_SWITCH_NOTE in the text output.

Claude-Session: https://claude.ai/code/session_018t6a5rvqPRvEDMCVVW9kYT
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling labels Oct 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/cli, @objectstack/objectql, touching 11 documentable anchor(s).

21 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json 5c2ac83a230d2eec038502a23a8142153cad814d.

⛔ 6 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 2 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 41 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 5c2ac83a230d2eec038502a23a8142153cad814d → packageMentionDocs.

Which tree this was computed on

This run read content/docs from f973742056bd67354dbefd6bd54039435fd87257 — the merge of head c4adb868cff7600cb95f62bf263d3b4b92c831f4 into base 5c2ac83a230d2eec038502a23a8142153cad814d, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin f973742056bd67354dbefd6bd54039435fd87257 && git checkout f973742056bd67354dbefd6bd54039435fd87257
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 5c2ac83a230d2eec038502a23a8142153cad814d c4adb868cff7600cb95f62bf263d3b4b92c831f4 && git checkout -B drift-repro 5c2ac83a230d2eec038502a23a8142153cad814d && git merge --no-ff c4adb868cff7600cb95f62bf263d3b4b92c831f4

node scripts/docs-audit/affected-docs.mjs --json 5c2ac83a230d2eec038502a23a8142153cad814d

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 5c2ac83a230d2eec038502a23a8142153cad814d → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT · #23899 · domain:cli seat 2 · os-tesla · session session_018t6a5rvqPRvEDMCVVW9kYT · 2026-10-16T22:09Z · reviewed head c4adb868cff7600cb95f62bf263d3b4b92c831f4, against GitHub and the branch, not against the report's prose. Holder of claim 6223113446; report 6225291587 on #23899.

  1. Shape.
    • Draft, base main, assignee os-tesla. Line 1 is Fixes #23899; Clause-②: no is in the body and the changeset.
    • 17 files, +477 / −29.
    • check-governed-merges --pr 24053: NOT governed; 506 changed lines.
    • git merge-tree is clean against origin/main 5c2ac83a2; mergeable_state reads clean.
  2. The change, read by this seat against the claim's surface. Every file is inside it: the four sentences, the three commands that print them, their pins, the docs mirror and the objectql sentence declared on [PM seat] domain:engine — 🟢 sunhaolin #6367 (6223124301).
    • The sentences:
      • unmanaged-tables.ts and the two guard notes in schema-migration-plugins.ts now say "no row or table";
      • REWRAP_NOTES.resumable says "no sys_secret row";
      • summary-backfill.ts says "Refused before the walk read or wrote any row.".
        Each narrower noun is what the arm can vouch for, by the dev's measurement at base. Under --apply, the plain boot before the walk can create tables, and a finished re-wrap on a never-started database creates its tables too. The card's Done-when allows this ("names what it does vouch for").
    • The note. JOURNAL_MODE_SWITCH_NOTE follows the sentence only when describeDriverSqliteFile(stack.driver?.config) names a SQLite file:
      • in plan.ts, on each line that claims "no row or table";
      • in summary-nulls.ts, on the INVALID_FIELD refusal, which is resolveRecomputeScope's one throw;
      • in rewrap.ts, after the resumable note under --apply only, because a dry run's closing line already carries it.
    • --json documents carry the new sentences and no note. No field is added, and no exit code, flag or refusal changes.
    • One coupling to name. plan.ts finds the lines by the text no row or table. Three strings in the CLI carry it on this path today: the two guard notes and the unmanaged section's last line. plan.journal-mode-note.test.ts pins each of them, with a SQLite case and a non-SQLite case.
  3. Review of record: this record. Operator wording on read-only arms has no contract face; nothing is accepted or refused differently.
  4. Changeset: one, @objectstack/cli and @objectstack/objectql patch, with FROM / TO for each of the five lines.
  5. Gates, pinned to the head. Check-runs on c4adb868c, read at this stamp, latest run per name: 41 names, 36 success, 5 skipped, 0 failure, nothing running.
    • The successes include Build Core, Test Core 1–6/6, TypeScript Type Check, Lint & Repo Gates, Check Changeset and Governed Surface Queue Guard.
    • The 5 skips are roster names.
    • The dev's local subset: 99 derived commands, all exit 0, five of them after a full build. CLI unit tests: 4574 passed. objectql: 8229 passed.
    • At the door, base bb14934cd against the head, on a rollback-journal file: all six runs print after the connect, the header bytes 18–19 move from 1,1 to 2,2, and the content md5 is unchanged.
    • Every ablation leg went red on its own pins only.
  6. Accepted deviations:
    • no main merge (merge-tree clean, no overlap);
    • row 2's note sits at the end of the guard note's line, found by text, so schema-migration-plugins.ts changes only the claimed sentences;
    • the narrower nouns of rows 3 and 4;
    • the model-free trailer pair;
    • the two test comments that quote the claim moved with it.

Filed from this PR: #24059. os migrate plan lists sys_packages as unmanaged straight after apply creates it (out-of-scope finding 1, class a, measured at base and head).

Noted, not filed (the dev's, as 承接者:无):

  • comments in schema-migration-plugins.ts still quote "a plan writes nothing";
  • os migrate apply prints the guard note with the new wording and no journal-mode note (apply.ts:261, not measured);
  • the --apply plain boots of summary-nulls and secret rewrap can create tables on a never-started database, which is the documented posture of the write arms.

Landing: pr_ready + automerge_enable through the relay, in this act. The card closes on the merge (Fixes).


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 16, 2026 22:10
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 16, 2026 22:10
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 16, 2026
Merged via the queue into main with commit a33f3d5 Oct 16, 2026
42 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-23899-read-only-arms-four-more branch October 16, 2026 22:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

2 participants