Skip to content

okf: CLI honesty batch — report skips, exit taxonomy, oversize preflight, twin-CLI guardrail (ADR-003–006) - #56

Open
indexzero wants to merge 4 commits into
feat/okf-bundle-api-adr-008from
feat/okf-cli-honesty-adr-003-006
Open

okf: CLI honesty batch — report skips, exit taxonomy, oversize preflight, twin-CLI guardrail (ADR-003–006)#56
indexzero wants to merge 4 commits into
feat/okf-bundle-api-adr-008from
feat/okf-cli-honesty-adr-003-006

Conversation

@indexzero

Copy link
Copy Markdown
Owner

What

PR 2 of the ADR stack (base: #54). Four small accepted ADRs batched by theme — every command tells the truth about what happened — one commit per ADR:

  • ADR-003: the ingest report distinguishes hash-match skips from nothing-found: ingest complete: 0 op(s) from 0 file(s); 1 file(s) already up to date. Empty bundle omits the clause entirely.
  • ADR-004: materialize exit taxonomy — 0 clean, 3 everything-owned-projected but foreign pending file(s) left in place (now notice:-prefixed on stderr), 1 real failure. Placement of the other report lists: stale rides as a notice with 0/3 (left in place by design); unsafe fails with exit 1, because an escapes-root path means part of the owned projection was not written — exit 3's premise doesn't hold — and §9 containment tripping should stop pipelines. Also fixes a latent label bug where the unsafe stderr line printed undefined (report pushes { rel, reason }; the printer read u.path || u.id). SPEC §9's N3 sentence and the CLI README's exit-code table updated.
  • ADR-005: ingest preflights the op-size limit before any op is appended: error: big.md body is 70,000 bytes; maxOpBytes is 65,536, exit 1, siblings in the same run unaffected. The limit is imported from @auto-okf/core (new frozen DEFAULT_LIMITS export), not duplicated; the apply-side oversize-op defense stays untouched (defense in depth — per-op encoding overhead still lands there). CLI README documents the limit and the remedy (split the document).
  • ADR-006: the twin CLIs refuse each other's storage by marker file, on every open path (init/join/watch included): error: <dir> is an auto-okf vault (okf-vault.json present); use auto-okf, not autovault-ld — and the symmetric refusal on the okf side. The one place this stack touches vault-ld/ (its CLI open path + its test suite). Tests assert exit 1, the anchored message, and storage untouched.

Verification

  • pnpm test:okf → 130/130
  • pnpm test (vault-ld + okf) → 245/245
  • New tests pin: both report forms (ADR-003); all three exit codes incl. the previously-untested pending case (ADR-004); named oversize refusal + zero ops + sibling ingest (ADR-005); both refusal directions with storage-untouched assertions (ADR-006)

Notes for review

  • No CHANGELOG entry for the exit-code change: the repo has no CHANGELOG file. ADR-004's consequence line is satisfied by the SPEC + README updates; say the word if you want a CHANGELOG started.
  • ADR-006's "vault-ld SPEC CLI section" update landed in vault-ld/cli/README.md instead — vault-ld has no committed SPEC file.

indexzero and others added 4 commits August 1, 2026 00:53
The one-line ingest report now distinguishes "already in log" from
"nothing found": hash-match skips (manifest/echo suppression, reason
"unchanged") append "; N file(s) already up to date". Decided once: the
clause is omitted entirely when no skips occurred — an empty bundle
still prints the bare "0 op(s) from 0 file(s)" line. Exit codes are
untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… failure (ADR-004)

The single `return report.ok ? 0 : 1` (the N3 comment) becomes the
three-code taxonomy: 0 = projected everything, no pending files; 3 =
projected everything owned, N foreign pending-ingest file(s) left in
place, listed on stderr as `notice: pending ingest (unsaved external
edit, left in place): <rel>`; 1 = real failure. Scripts treating any
nonzero as failure keep the stop (3 is nonzero).

Placement of the other report lists: `stale` is a notice accompanying
exit 0/3 (the files are left in place by design — derived faces a human
edited, not a fault of this run). `unsafe` stays failure-side (exit 1,
`error:` prefix): an escapes-root path means part of the owned
projection was NOT written, so "everything owned was projected" — the
premise of exit 3 — does not hold, and §9 containment is a defense whose
tripping should stop pipelines. SPEC §9 and the CLI README updated; the
missing pending-case CLI test added alongside pins for 0 and 1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…I docs (ADR-005)

A candidate whose body would exceed the 65,536-byte maxOpBytes bound is
refused at ingest, by name, before any op for that file is appended —
`error: drafts/big.md body is 71,204 bytes; maxOpBytes is 65,536` —
exit 1; sibling files in the same run still ingest. The limit is a new
DEFAULT_LIMITS export from @auto-okf/core (the same values apply defends
with, never a duplicated constant); apply-side oversize-op stays as
defense in depth. The CLI README states the limit and the remedy (split
the document).

The preflight boundary is body bytes vs maxOpBytes; the per-op encoding
overhead on top of the body remains apply territory, so tests pin one
byte over plus a comfortably-under sibling rather than a fragile
exact-overhead fixture.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Each CLI checks for the sibling system's marker on every vault-open
path (withVault, init, join, watch) and refuses pointedly, naming the
right tool: `error: <dir> is an auto-okf vault (okf-vault.json present);
use auto-okf, not autovault-ld` — and symmetrically for vault-ld.json in
auto-okf. One stat on the open path; exit 1; storage untouched. The
marker filenames are stable contract between the twin systems, so each
CLI carries the sibling name as a constant rather than importing across
systems. The one cross-system change in this batch (the vault-ld CLI
gains the symmetric check + test).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant