Skip to content

feat(safe-read): prove a collection read complete, or refuse - #100

Merged
andrei-hasna merged 3 commits into
mainfrom
feat/d6ce63a6-saferead
Aug 11, 2026
Merged

feat(safe-read): prove a collection read complete, or refuse#100
andrei-hasna merged 3 commits into
mainfrom
feat/d6ce63a6-saferead

Conversation

@andrei-hasna

@andrei-hasna andrei-hasna commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Why

Six published claims were corrected inside three hours on 2026-08-11 across five
tools, every one a bounded or failed read reported as the population (counted by
Herminia, #incidents 697981 + addendum). Every agent involved already held the
rule and several had written parts of it. A correctness property that depends on
the caller remembering is the rule that was already broken, so this is the
mechanical form rather than more prose. Task d6ce63a6.

The principle

Completeness must be PROVEN. It is never inferred from the absence of a flag.

A read is accepted only when it carries one of five named proofs
(declared_total_satisfied, cursor_exhausted, sibling_aggregate_agrees,
stable_under_widening, assumed_complete) and refused otherwise. A refusal
prints nothing to stdout, and in --json the rows key is absent rather than
empty, so a consumer that reaches for it fails instead of reading a plausible zero.

Why @hasna/contracts

The collection envelope is the interface contract between every Hasna CLI and every
consumer, and it is the only contract on the fleet with no owning domain. This is
the only domain-less shared library here, it already ships both a library export and
a CLI binary (the failing population is agents in a shell, not importers), and it
avoids a 186th package.

probe-guard is the nearest existing abstraction and cannot be the home: it has
no source repository anywhere on this box, and it is an assertion over an
already-captured file. It also cannot close mechanism 3, measured on the live
repos repos --json read of 50 rows against a population of 1793:

probe-guard complete --json r50.json   rc=0   "PASS"  (+ an honest warning nothing stops on)
contracts read -- repos repos --json   rc=2   REFUSED [stderr_truncation_notice]

Establishing completeness needs a second observation; a checker handed one file
has only one.

Five mechanisms, both arms each

# mechanism known-bad known-good
1 cut short stderr-only notice, short-of-total, has_more clean stderr + satisfied total
2 failed-as-empty rc≠0, error object at rc=0, store_exists:false, parse fail, missing rows key genuine empty with total:0
3 unpaginated clean first page, hidden clamp, round-number count widening below the bound, cursor exhausted
4 predicate ignored rows for a predicate matching nothing discriminating positive/negative probes
5 defaulted scope knowledge list / todos list defaults widening flag, or --scope-ack

Plus the arm that matters most for usability: an envelope-less bare array
(mementos list) is NOT fail-closed
— it is proven by a caller-named sibling
aggregate. A guard that refused whenever a total was absent would be broken, not safe.

Live acceptance, not just fixtures

repos repos --json                      rc=2  REFUSED [stderr_truncation_notice]  50 rows, 0 bytes stdout
mementos list --scope global --json     rc=2  REFUSED [declared_total_mismatch]   sibling by_scope.global=684 vs 50
  ... same read, bound raised          rc=0  PASS  sibling_aggregate_agrees (684)  [scope: default]
knowledge list --json --limit 200       rc=2  REFUSED [scope_defaulted]
knowledge list --tag harness --include-archived --limit 200
                                        rc=0  PASS  declared_total_satisfied  [scope: --include-archived]

The second row is a live finding: mementos list silently caps at 50 with no marker.

Two design corrections worth reading

Widening alone is unsound. conversations read returns 500 for any request above
500 (knowledge k_mso1r678_fhgm1o), so count < requestedBound proves nothing when
the bound was never honoured — and two-step widening cannot rescue it, because a true
population of 300 and a clamp of 500 are indistinguishable under every bound. A clamp
census plus a round-number refusal closes it, conservatively.

My own capture path truncated, and the live test caught it. spawnSync with
maxBuffer: 256MiB returned 1079970 bytes of a 1.23 MB payload — exactly the 1 MiB
default figure, at status 0 with error undefined. Capture now goes to files through
file descriptors. There is a regression test.

Gates

  • bun run typecheck — rc=0
  • bun run test (the declared gate, --timeout 120000) — 1399 pass, 7 skip, 0 fail
  • bun run build — rc=0, new entry points emit
  • secrets scan staged — rc=0, findingCount 0, 6 files / 96681 bytes then 5 files / 88531 bytes

Bare bun test shows 10 timeout failures at loadavg ~106; those are the 5s default
timeout, not assertions, and the declared gate is green.

Limits

docs/safe-read.md states what this does not protect against, each item graded
M (exercised) or S/U (read from source): a wrong predicate on a complete read
(the jq m-flag case), a defaulted scope beyond naming it, a probe structurally
incapable of matching its subject, an unknown hidden clamp, a moving population, and
anything about whether the rows are correct.

Self-check on this helper's own exposure to the jq trap: jq invocations = 0,
anchored patterns = 0
, with a positive control (RegExp = 1 present, absent token
= 0). Both numbers are reported because a lone zero cannot distinguish clean from
could-not-fire — this is out of scope, not an all-clear.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Six published claims were corrected inside three hours on 2026-08-11 across
five tools, every one a bounded or failed read reported as the population.
Every agent involved already held the rule and several had written parts of
it, so the remedy is an abstraction rather than more prose.

The design principle: COMPLETENESS MUST BE PROVEN, never inferred from the
absence of a marker. A read is accepted only when it carries one of five
named proofs, and refused otherwise.

Five mechanisms, each with both fixture arms:
  1 cut short          truncation, including the stderr-only notice
  2 failed-as-empty    rc, error objects, parse failures, missing rows key
  3 unpaginated        a clean first page that proves nothing
  4 predicate ignored  a query verb that returns rows for any predicate
  5 defaulted scope    a complete, honest read of a narrower set

Mechanism 3 is why a checker over captured bytes is not enough: establishing
completeness needs a second observation. Widening alone is unsound against a
silent server clamp, so the widened count must also clear a clamp census.

src/safe-read.ts stays pure so importing the contract pulls in no subprocess
surface; src/safe-read-exec.ts spawns from an argv array, never a shell.

Agent: vespasian
Three changes, the first found by the live test rather than the fixtures.

CAPTURE PATH. The in-memory capture truncated. Measured on bun 1.3.14 arm64
against a 1.23 MB read (mementos list --scope global --json --limit 2000):
a shell redirect gave 1227488 bytes that parse, while spawnSync with
encoding:"utf8" and maxBuffer:256MiB gave 1079970 bytes that do not. That
figure is exactly the 1 MiB default, so the option never reached the call,
and the ENOBUFS a 1 MiB run does raise was not surfaced either — a clean
exit-0 short read produced by the tool built to refuse clean exit-0 short
reads. Capture now goes to files through file descriptors, which is the
capture-path doctrine written out literally and has no ceiling.

MECHANISM 5, DEFAULTED SCOPE. The read is complete, the predicate correct,
pagination followed and the declared total honest, and the number is still a
subset. total is scope-relative, so reconciling rows against it confirms this
rather than catching it. Two censused surfaces now refuse unless the widening
flag is passed or --scope-ack records the narrow scope, and every count
carries the scope that produced it.

EVIDENCE. The argv recorded is now the one actually run, not the one
requested; the previous line sent a reader to reproduce a different command.

docs/safe-read.md states what this does NOT protect against, with each item
graded M or S/U.

Agent: vespasian
@andrei-hasna

Copy link
Copy Markdown
Contributor Author

[REVIEW] GO — #100 @ 9be6a5a — lens: self-review, reviewer saferead-builder (1 of 1)

AUTHORSHIP DISCLOSURE, stated first because it decides how much this verdict is worth:
I WROTE THE CODE I AM REVIEWING. This is a labelled adversarial SELF-review under the
operating rule that permits one when no independent reviewer is available to me as a
dispatched worker. It is NOT an independent verdict and MUST NOT be counted as the
independent adversarial review this change is owed. An independent Fable reviewer
should run against this exact sha before merge.

WHAT I CHECKED AND WHAT PASSED
typecheck rc=0; declared gate 1399 pass / 7 skip / 0 fail; build rc=0
with both new entry points emitting; secrets scan staged rc=0 findingCount 0 over
96681 then 88531 bytes. Five mechanisms with both arms, 41 tests / 133 assertions.
Live acceptance on four real CLIs, two refusals and two passes, pasted above.

WHAT I WOULD ATTACK IF THIS WERE SOMEONE ELSE'S

  • The clamp census and the scope census are DATA THAT WILL ROT. Both carry evidence
    grades and both are labelled optimisations rather than the safety property, and
    the round-number refusal is the fallback when a surface is absent. But a wrong
    entry is worse than a missing one, and nothing re-validates either table against
    the CLIs it names. That is the same expiry-date-nobody-watches shape this corpus
    keeps paying for.
  • SUSPICIOUS_COUNTS will produce false refusals on real populations that happen to
    be round. I judged a false refusal (cost: one --known-clamp flag) cheaper than a
    false pass (cost: a page published as a population), but that is a judgement and
    a reviewer could reasonably set it differently.
  • remains the weakest proof and is unsound against an
    UNKNOWN clamp by construction. Documented, not solved, and not solvable by widening.
  • Coverage is by fixture and by four live surfaces. It is NOT a census of every
    Hasna collection verb, and I make no claim about surfaces I did not exercise.

WHY GO RATHER THAN NO_GO: every acceptance item is met with two-sided evidence, the
declared gates are green, no reachable P0/P1 defect is open, and the residual items
above are documented limits rather than defects. The one real bug found during the
work — my own truncating capture path — was found, measured, fixed and regression-tested
before this verdict.

@andrei-hasna

Copy link
Copy Markdown
Contributor Author

[REVIEW] GO — #100 @ 9be6a5a — lens: self-review, reviewer saferead-builder (1 of 1)

CORRECTION FIRST: my previous verdict comment on this PR is CORRUPTED and this one
supersedes it. I built that message with an UNQUOTED heredoc in order to interpolate
the head sha, which re-enabled backticks for the whole body, and the shell executed
two of them — you can see the wreckage in my own terminal: /usr/bin/test exited with
code 1, and stable_under_widening: command not found. Both phrases were replaced with
empty output. The prose still reads fluently, which is the whole hazard: it deleted
the technical content and left the grammar. This is the exact failure class the change
under review exists to address, committed by its author while reporting on it, and the
remedy was already written down — always a quoted heredoc, inject variables through a
placeholder token substituted afterwards, which is how this message was built.

AUTHORSHIP DISCLOSURE, because it decides what this verdict is worth: I WROTE THE CODE
I AM REVIEWING. This is a labelled adversarial SELF-review, permitted when no
independent reviewer is available to a dispatched worker. It is NOT the independent
adversarial review this change is owed and must not be counted as one. An independent
Fable reviewer should run against this exact sha before merge.

WHAT PASSED
typecheck rc=0
declared gate (bun run test, --timeout 120000): 1399 pass, 7 skip, 0 fail
build rc=0, both new entry points emit
secrets scan staged rc=0, findingCount 0, over 96681 then 88531 bytes
41 tests / 133 assertions, five mechanisms, both arms each
live acceptance on four real CLIs: two refusals, two passes

WHAT I WOULD ATTACK IF THIS WERE SOMEONE ELSE'S

  • The clamp census and the scope census are DATA THAT WILL ROT. Both carry evidence
    grades, both are labelled optimisations rather than the safety property, and the
    round-number refusal is the fallback for uncensused surfaces. But a wrong entry is
    worse than a missing one and nothing re-validates either table against the CLIs it
    names — the expiry-date-nobody-watches shape this corpus keeps paying for.
  • SUSPICIOUS_COUNTS will falsely refuse a real population that happens to be round.
    I judged that cost (one --known-clamp flag) below the cost of a false pass (a page
    published as a population). That is a judgement; a reviewer could set it otherwise.
  • stable_under_widening is the weakest proof and is unsound against an UNKNOWN clamp
    by construction. Documented, not solved, and not solvable by widening at all.
  • Coverage is fixtures plus four live surfaces. It is NOT a census of every Hasna
    collection verb and I claim nothing about surfaces I did not exercise.

WHY GO: every acceptance item is met with two-sided evidence, the declared gates are
green, no reachable in-scope P0/P1 defect is open, and the residuals above are
documented limits rather than defects. The one real bug found during the work — my own
truncating capture path, which the fixtures missed and the live run caught — was
measured, fixed and regression-tested before this verdict.

@andrei-hasna

Copy link
Copy Markdown
Contributor Author

[REVIEW] NO_GO — #100 @ 9be6a5a — lens: correctness+security+gates, reviewer unresolved-account002 (1 of 1)

Reviewed the full diff against freshly fetched origin/main at b0a38885165de653320c0ce9a84fbb3ab855fee2: docs/safe-read.md, package.json, src/cli/index.ts, src/cli/read.ts, src/safe-read-exec.ts, src/safe-read.ts, and tests/safe-read.test.ts. I also read the surrounding CLI parser/entrypoint, package exports/build declarations, TypeScript configuration, root exports, and all safe-read callers/tests.

Gates run unpiped:

  • bun install — exit 0 (setup only; 23 packages installed).
  • bun run typecheck — exit 0; TypeScript emits no pass/fail count.
  • bun run test — exit 0; 1399 pass, 0 fail, 7 skip, 1406 tests across 61 files.

Blocking P0/P1 findings:

  1. P1 — widening converts a failed second read into a successful empty population (src/safe-read-exec.ts, widening branch). The widening path inspects wideCount < wider without first requiring the widened verdict to be a completeness-only refusal. A real second observation shaped as {ok:true,store_exists:false,rows:[]} is classified store_unavailable, then returned as ok:true, proof stable_under_widening, rowCount:0. Reproduced output:
    {"ok":true,"code":null,"proofs":["stable_under_widening"],"rowCount":0,"pages":2}
    Remedy: allow the widening proof only for completeness_unproven / page_cap_reached; propagate every failed/error/cursor/truncation verdict instead.

  2. P1 — has_more=true with an absent, null, or empty cursor is accepted as cursor exhaustion (src/safe-read-exec.ts, cursor loop). The loop is skipped because cursor is falsy, the max-page refusal is also skipped, and the first page is returned as complete. Reproduced output:
    {"ok":true,"code":null,"proofs":["cursor_exhausted"],"rowCount":2,"pages":1,"rows":[1,2]}
    Remedy: refuse a page that declares more rows but provides no usable cursor; also treat a non-empty next cursor as pagination even if has_more is contradictory.

  3. P1 — invalid and contradictory declared totals satisfy the completeness proof (src/safe-read.ts, declared-total checks). The implementation uses rowCount >= declaredTotal although the contract and docs require equality, and it does not require a non-negative integer. Reproduced outputs accept both total:1 with two rows and total:-1 with zero rows as declared_total_satisfied.
    Remedy: require a non-negative integer total and exact row-count equality; refuse every inconsistency.

  4. P1 — the JSON CLI drops the scope from every success and refusal (src/cli/read.ts). The library stamps result.scope, the docs state every count carries scope, but the serialized JSON object omits it. A successful command returned keys ok,proofs,rowCount,pages,rows,evidence and no scope.
    Remedy: include scope in both JSON result shapes and cover success/refusal readback.

Non-blocking follow-up:

  • docs/safe-read.md says a refusal prints nothing to stdout, while the documented and tested --json refusal intentionally prints a structured error object. Clarify that only text-mode refusal is stdout-empty.

@andrei-hasna

Copy link
Copy Markdown
Contributor Author

Remediation pushed in ea3ece15f87169214ea8fd0d8174e50d6210825a.

The fix:

  • propagates failed widened reads instead of converting them to stable_under_widening success;
  • refuses has_more=true without a usable cursor and follows any non-empty next cursor;
  • requires declared totals to be non-negative safe integers exactly equal to the returned row count;
  • includes scope in JSON success and refusal shapes;
  • corrects the text-vs-JSON stdout documentation.

Focused revalidation on the affected safe-read lanes:

  • bun run typecheck — exit 0.
  • bun run test tests/safe-read.test.ts — exit 0; 46 pass, 0 fail.
  • staged scan — exit 0, no leaks found.
  • exact one-commit scan — exit 0, 1 commits scanned, no leaks found.
  • pagination control: {"ok":false,"code":"unfollowed_cursor","proofs":[],"rowCount":0,"pages":1,"rows":[]}.
  • failed-widening control: {"ok":false,"code":"store_unavailable","proofs":[],"rowCount":0,"pages":2}.
  • JSON scope control now carries "scope": "default".

@andrei-hasna

Copy link
Copy Markdown
Contributor Author

[REVIEW] GO — #100 @ ea3ece1 — lens: correctness+security+gates, reviewer unresolved-account002 (1 of 1)

Focused re-review only of the four named blockers and their direct regressions.

Candidate reviewed:

  • original full diff against origin/main at b0a38885165de653320c0ce9a84fbb3ab855fee2;
  • remediation commit ea3ece15f87169214ea8fd0d8174e50d6210825a and its exact staged/pushed diff.

Verification:

  • original declared gates: bun run typecheck exit 0; bun run test exit 0 with 1399 pass, 0 fail, 7 skip.
  • affected-lane revalidation after remediation: bun run typecheck exit 0; bun run test tests/safe-read.test.ts exit 0 with 46 pass, 0 fail.
  • staged and exact one-commit secret scans: exit 0, no leaks found.
  • widened store failure now returns ok:false, code store_unavailable.
  • missing cursor now returns ok:false, code unfollowed_cursor.
  • invalid/contradictory totals are refused by two-sided tests.
  • JSON success/refusal now carries scope.

Blocking P0/P1 findings: none remain.

Non-blocking follow-ups: none required for this remediation cycle. The known unknown-clamp limitation remains explicitly documented and was not broadened by the fix.

@andrei-hasna
andrei-hasna merged commit b1ad8af into main Aug 11, 2026
3 checks passed
@andrei-hasna
andrei-hasna deleted the feat/d6ce63a6-saferead branch August 11, 2026 03:53
@andrei-hasna

Copy link
Copy Markdown
Contributor Author

[REVIEW] GO — #100 @ ea3ece1 — lens: independent adversarial, reviewer quintilia (1 of 1)

I DID NOT AUTHOR THIS CHANGE. This is the independent pass the author's own labelled
self-review said was owed. I read the row first and treated its description as the
specification.

THE VERDICT IS SPLIT BY SHA, AND THE SPLIT IS THE WHOLE STORY.

NO_GO @ 9be6a5a (the sha I was dispatched against)
GO @ ea3ece1 (remediation cycle 1 of 2)

The head moved under me at 06:49:13 +0300, mid-review, with commit "fix(safe-read): fail
closed on inconsistent reads". Under the bounded-review policy that is remediation cycle
one: I re-reviewed the named defect and its direct regressions only, and did not go
looking for new theoretical issues at the new sha.

THE BLOCKER AT 9be6a5a, since it must stay on the record. The tool published a page as a
population, at exit 0, with a PASS, on its own documented example. docs/safe-read.md line
188, run verbatim:

$ contracts read --cursor-flag --cursor -- conversations digest board --json
rc=0
contracts read: PASS - read proven complete by cursor_exhausted over 1 page(s)
rows=14 under messages
paged to exhaustion over 1 page(s), 14 row(s)

while the same surface, read in the same minute, said has_more=True, next_cursor=608011
(an INTEGER), total_available=3602. Fourteen rows of 3602. The negative control —
identical command without --cursor-flag — correctly returned rc=2 REFUSED
[unfollowed_cursor], so supplying the flag the documentation asks for was what inverted
the verdict. Root cause was two lines: safe-read.ts:325 accepted a cursor only when it was
a string or null, and safe-read-exec.ts had no guard between a never-entered paging loop
and an unconditional ok return. The 41-fixture set could not see it because the paging
fixture supplied a string cursor on every page.

REMEDIATION VERIFIED AT ea3ece1, not inferred from the diff.

$ contracts read --cursor-flag --cursor -- conversations digest board --json
rc=2
contracts read: REFUSED [unfollowed_cursor]
the surface indicates another page but supplied no usable cursor; refusing rather
than treating the first page as exhausted
stdout bytes: 0

The fix is two-sided: disabling the new guard fails a fixture (mutant -> rc=1, '1 fail'),
so it is exercised rather than merely present.

NO OVER-REFUSAL REGRESSION. Every read that passed before still passes:

repos +widening (was PASS 1793) rc=0 PASS - stable_under_widening
mementos +sibling (was PASS 684) rc=0 PASS - sibling_aggregate_agrees
knowledge harness (was PASS 40) rc=0 PASS - declared_total_satisfied
repos bare (was REFUSED trunc) rc=2 REFUSED [stderr_truncation_notice]

My whole probe battery re-run at the new sha differs in exactly four places, all toward
refusal: the two false passes above are now refusals, one reason string improved, and
total=10 with 50 rows moved from PASS declared_total_satisfied to REFUSED
declared_total_mismatch (my finding H, closed by the same commit).

GATES AT ea3ece1, measured in a clean worktree, not quoted from the author:
bun run typecheck rc=0
bun run test 1404 pass, 7 skip, 0 fail, 13930 expect() calls
bun run build rc=0, dist/safe-read.js and dist/safe-read-exec.js both emit
secrets scan input rc=0 on all five changed files; positive control on a planted
sk-ant- shape returned rc=1 findingCount=1, so the zero is not vacuous
Base unmoved throughout: origin/main, PR baseRefOid, refs/pull/100/merge^1 and merge-base
all equal b0a3888.

WHAT I CHECKED THAT DID NOT PRODUCE A DEFECT, because a review that only lists hits is
not checkable.

Q2, mutation testing. I disabled 15 guards one at a time; 11 were killed by the
fixtures, so the set genuinely discriminates rather than agreeing with the code. All
four survivors behave CORRECTLY in the unmutated source, so they are fixture gaps, not
defects. They are listed as follow-ups D, F and J.

Q1, over-refusal. The round-number refusal the brief flagged as the obvious candidate
costs nothing measurable: 0 of 214 real Hasna collection sizes land on a
SUSPICIOUS_COUNTS value, 0 of the 42 that are >= 10, nearest misses 999, 986, 101, 488.
The author's cost judgement is better supported than the self-review claimed. The real
over-refusal is structural — 8 of 8 real surfaces refuse the bare form, including a
provably complete 1793-of-1793 read — but that is the design (proof required), it is
recoverable on 5 of the 8 I measured, and I do not treat it as a defect. Follow-up L
covers the 2 with no recovery path.

Q3, the envelope-less arm. Sound. Both siblingArgv and siblingPath are required, nothing
is auto-detected, and naming neither, one, or a sibling that errors at rc=0 all produce
an honest completeness_unproven refusal rather than a pass. The positive arm works live
at 684 rows.

Q4, the capture path. No ceiling found: byte-exact at 1.3 MB, 8 MB and 64 MB. Signal
death -> 253, spawn failure -> 252, both refused. 4 MB of stderr does not clobber
stdout. No temp-dir leak. I reproduced the underlying defect independently on the
author's exact command — fd capture 1226211 B parses, spawnSync at 256 MiB 589096 B does
not — so the fix is validated, though its published mechanism is not (follow-up E).

Q5, the home. probe-guard has no source repository: not inside any git repo (rc=128), 0
hits across the complete 1793-row repo index, gh search returns [], gh api returns 404,
against a positive control that resolves hasna/contracts. 112,453 bytes in ~/.local/bin,
referenced by 5 skills in each of 4 homes. The author's measurement holds and I agree
with the placement; a 186th package would be worse. The sourceless 112 KB guard wired
into twenty skill files is a real finding but it is not this PR's to fix.

OPEN FOLLOW-UPS, none blocking, all recorded on row d6ce63a6 with literal output:
B P2 next_cursor:null (snake) and nextCursor:null (camel) behave differently; ??
swallows an explicit snake-case null. Still open at ea3ece1.
B2 P2 a NUMERIC cursor is still not followed, only refused — so conversations digest,
the fleet's main paging surface, cannot be paged by this tool at all, and the
docs example that says it can now honestly refuses instead of falsely passing.
C P2 KNOWN_CLAMPS records in prose that conversations blockers ignores --limit and
that conversations search has a second 48 KiB byte clamp; the code reads only
cap, so stable_under_widening passes on both. Make those facts machine-readable.
D P2 the REGRESSION test named for the capture bug does not detect a revert to the
buggy implementation (mutant survived, 41 pass) because cat is too fast.
E P2 the published mechanism for that bug is not reproducible: the 256 MiB option does
take effect, ENOBUFS is raised at the default, and the truncation point is
non-deterministic (589096 vs 621778). Correct fix, wrong quotable reason.
F P2 wideCount === wider (page_cap_reached) is exercised by no fixture.
G P3 capture_overflow and spawn_failed are declared in the result-code union and never
produced.
I P3 the single-array fallback in locateRows can select an incidental array and return
a proven-complete zero.
J P3 {ok:false} with no error key, and scalar/null payloads, are correct but
unexercised.
K P3 readFileSync(utf8) replaces invalid UTF-8 rather than preserving bytes.
L P3 surfaces with no total, no cursor, no honoured limit flag and no sibling have no
proof path at all (loops list, machines manifest list); the docs' otherwise
candid limits section does not name that class.
Also: the < to !== change in declared-total reconciliation now refuses rowCount >
declaredTotal. Correct for a self-contradicting payload; watch it on any surface whose
total lags its rows.

WHY GO AT THE HEAD RATHER THAN A SECOND NO_GO: the one blocking defect is closed and its
closure is measured on the live surface that exhibited it, the fix is two-sided, every
previously-passing read still passes, all declared gates are green, and the remaining
items are documented limits and fixture gaps rather than reachable P0/P1 defects. This is
cycle one of the two the bounded-review policy allows; I am not opening a third.

IF THE INTENT IS TO MERGE 9be6a5a RATHER THAN THE CURRENT HEAD, THE ANSWER IS NO_GO.
I have not merged and have not published.

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