Skip to content

Follow-up: carry PR #701's stranded review fixes (skip the wasted resettle scan, MaintenanceReport symmetry, span visibility, LLP gloss) - #706

Open
philcunliffe wants to merge 3 commits into
masterfrom
fix/issue-700-followup
Open

Follow-up: carry PR #701's stranded review fixes (skip the wasted resettle scan, MaintenanceReport symmetry, span visibility, LLP gloss)#706
philcunliffe wants to merge 3 commits into
masterfrom
fix/issue-700-followup

Conversation

@philcunliffe

@philcunliffe philcunliffe commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Why this exists

PR #701 went through review, a fix worker applied four round-1 items and pushed
cc82d6ffd96e2216f4c219f349c107d06a046ee2, and then #701 was squash-merged at
head 287b67bb - before cc82d6f landed. #701 is now MERGED and closed,
so its branch is invisible to the reconciler and the four verified fixes would
be silently lost.

Evidence that they never reached master:

  • git merge-base --is-ancestor cc82d6f origin/master -> false
  • origin/master:src/core/cache/maintenance.js contains zero occurrences of compactionDue
  • origin/master:src/core/cache/maintenance.js contains zero occurrences of setAttribute('rebaselined'
  • origin/master:src/core/cache/types.d.ts contains zero occurrences of totalRebaselined

All four files cc82d6f touches are byte-identical between 287b67b and
current origin/master, so each fix is carried over unchanged in intent.

The four fixes

1. Skip the wasted re-settle scan (src/core/cache/maintenance.js)

Hoist a cheap const compactionDue = opts.force || (grewSinceCompaction && needsCompaction(liveDir, cfg))
above the hasResettleCandidate row scan, gate the scan on !compactionDue && settle,
and set shouldCompact = compactionDue || hasResettle.

Recognition of a foreign sorted replace outranks the re-settle check, so on
the first tick after each foreign replace a partition with no fallback row paid
a complete single-column scan of the day purely to discard the answer. The
boolean is unchanged (compactionDue || hasResettle is the same disjunction as
before); the row scan is skipped when its answer cannot matter. In exchange,
the cheap needsCompaction stat-walk is now evaluated in the re-settle path
where the old || short-circuited past it: needsCompaction only reads
(readdirSync/statSync), so this trades a warm stat-walk for a full
single-column row scan with no semantic change.

Empirical verification (instrumented hasResettleCandidate with a call
counter, then reverted before committing):

suite scans before scans after tests
test/core/cache-retention-maintenance.test.js 1 0 36 pass
test/core/cache-resettle-sweep.test.js 7 4 5 pass

The 4 remaining calls are exactly the cases where the fallback-row scan is the
only possible trigger for compaction, so the sweep keeps working; the 4
eliminated calls are scans whose answer was discarded.

2. LLP 0199 Extended-by gloss (llp/0199-maintenance-compaction-convergence.decision.md)

Give the bare **Extended-by:** LLP 0207 the corpus's usual linked + glossed
form, matching llp/0106:9, llp/0129:9, llp/0158:9, llp/0180:9,
llp/0182:9, llp/0188:9, llp/0190:9, llp/0191:9.

3. MaintenanceReport symmetry (types.d.ts, maintenance.js, commands/query.js)

Add totalRebaselined: number to MaintenanceReport and increment it beside
totalCompacted, so query.js reads the total the report already knows instead
of re-deriving it by filtering report.partitions. maintainCache is the only
producer of a MaintenanceReport, so the new required field has one writer.

4. rebaselined span attribute (src/core/cache/maintenance.js)

The hyp_rebaselines counter was the only in-daemon signal that a re-baseline
happened, and it carries only the dataset, not the partition. Tag the enclosing
maintenance.partition span so a trace query can find which day re-baselined.

Note on PR #698

Open PR #698 (fix/issue-697) rewrites this file heavily (streaming row groups,
descriptor parking) and adds its own span.setAttribute block in
maintainCache, converting the withSpan callback to async (span) =>. To
avoid a needless conflict, item 4 here is deliberately not the original
cc82d6f shape (which restructured that same callback into
const result = ... ; span.setAttribute(...) ; return result). Instead it is a
single line at the re-baseline site itself, using the repo's existing
getActiveSpan() helper (already used in src/core/commands/policy.js):

getActiveSpan()?.setAttribute('rebaselined', true)

withSpan runs its callback under tracer.startActiveSpan, so the enclosing
maintenance.partition span is the active span there; instrumentation confirmed
getActiveSpan() resolves to a real span in all 4 re-baseline paths exercised by
the suite. This leaves the exact block #698 rewrites untouched.

Verified: git merge origin/fix/issue-697 into this branch auto-merges
src/core/cache/maintenance.js, src/core/cache/types.d.ts and
src/core/commands/query.js cleanly. The only conflict is the one-line
**Extended-by:** metadata field in llp/0199, which #698 also rewrites (it
adds a gloss for 0207 and 0209 but no link); resolving it is a union of the two
glosses.

Gate

  • npm test: 3901 pass, 0 fail, 6 skipped
  • npm run typecheck: clean
  • test/core/llp-ref-hygiene.test.js: 11 pass, 0 fail
  • hyp smoke cache_lifecycle_maintenance: ok
  • hyp smoke incremental_sink_compaction: ok
  • hyp smoke cache_roundtrip: ok

Issue #700 was already closed by #701; this PR deliberately carries no
Fixes #... trailer.

🤖 Generated with Claude Code

… MaintenanceReport symmetry, span visibility, LLP gloss

PR #701 was squash-merged at head 287b67b, before the round-2 review fixes
in cc82d6f were pushed, so four verified fixes never reached master.

- Hoist a cheap `compactionDue` check above the `hasResettleCandidate` row
  scan and gate the scan on `!compactionDue`. Recognition of a foreign
  sorted `replace` outranks the resettle check, so the first tick after
  each foreign replace paid a complete single-column scan of the day
  purely to discard the answer.
- Add `totalRebaselined` to `MaintenanceReport` beside `totalCompacted`,
  and let `query.js` read it instead of re-deriving the count.
- Tag the enclosing `maintenance.partition` span with `rebaselined`; the
  `hyp_rebaselines` counter carries only the dataset, not the partition.
- Give LLP 0199's bare `Extended-by: LLP 0207` the corpus's linked and
  glossed form.

Co-Authored-By: Claude <noreply@anthropic.com>
Round-1 review of #706 approved the carry but flagged that none of the
four stranded fixes was pinned by a committed test, plus two @ref nits.

- Add three tests to test/core/cache-retention-maintenance.test.js, in
  the foreign-sorted-replace block:
  - `totalRebaselined === 1` on a re-baselining run, and `=== 0` once
    converged (pins the MaintenanceReport symmetry fix).
  - a capturing TracerProvider asserting the maintenance.partition span
    carries `rebaselined: true` (pins the span-attribute fix).
  - a partition already due for compaction: assert the resettle
    candidate's data file is read once, not twice, by spying on
    `fs.readFileSync` (pins the scan-skip fix). hasResettleCandidate's
    return value is otherwise unobservable once compactionDue is true
    (it's discarded via `||`), so this is the cheapest honest signal
    available; each new test was verified to fail when its
    corresponding fix is reverted.
- Retarget the scan-skip gate's @ref from LLP 0207#foreign-replace to
  #outranks-resettle: the gloss ("recognition ... still outranks it")
  is that anchor's actual subject, not the recognition test's.
- Drop the @ref on the span-attribute comment: #re-baseline settles the
  cursor-write shape, not telemetry, so citing it was close to
  mechanical. The prose rationale stays; it's the useful part.
- Amend the PR body's claim about item 1: `needsCompaction` (pure,
  read-only) is now evaluated unconditionally in the re-settle path
  where the old `||` short-circuited past it, so more moved than "only
  the scan's side effect is skipped."

Co-Authored-By: Claude <noreply@anthropic.com>
@philcunliffe

Copy link
Copy Markdown
Contributor Author

Review round 1 - head dcae50b

Verdict: approve. No blockers, no majors. Four items raised, all four fixed in abdb17d.

The premise verified first, since it is this PR's whole justification

  • git merge-base --is-ancestor cc82d6f origin/master -> false.
  • origin/master has 0 occurrences of compactionDue, setAttribute('rebaselined', or totalRebaselined.
  • All four files are byte-identical (same blob SHAs) between 287b67b and origin/master, so nothing landed by another route.

The four fixes really were stranded when PR #701 was squash-merged at 287b67b, before cc82d6f was pushed. Not redundant.

Item-by-item verification

Item 1 (scan skip) is boolean-neutral. With D = force || (grew && needsCompaction) and H = settle && grew && candidate: old force || H || (grew && needsCompaction) = D || H; new hasResettle = !D && H, shouldCompact = D || (!D && H) = D || H. Identical across all 8 assignments. hasResettle has no other reader, so redefining its value is unobservable. Reproduced the instrumentation table: retention/maintenance suite 1 scan -> 0, resettle sweep 7 -> 4.

Item 4 (span attribute) lands on the right span. withSpan -> startActiveSpan -> activeContext.run({span}, ...) (AsyncLocalStorage), and getActiveSpan() reads that store. With a capturing TracerProvider the exported span was maintenance.partition {... "status":"ok","rebaselined":true}. The ?. no-op is safe, and the unprefixed key matches its siblings partition/status.

Item 3 (totalRebaselined) has a single producer - only maintainCache returns a MaintenanceReport; format-iceberg's ExportMaintenanceReport is a separate interface, and no test constructs a literal. Value-identical refactor, dry-run behaviour unchanged.

Cross-PR merge claim verified exactly: merging origin/fix/issue-697 (#698) auto-merges maintenance.js and types.d.ts; the only conflict is the one-line **Extended-by:** field in llp/0199, resolvable as a union of the two glosses. The merged maintenance.js was inspected: compactionDue/hasResettle/shouldCompact, getActiveSpan()?.setAttribute and totalRebaselined all survive coherently, so the auto-merge is semantically sound and not merely textually clean.

Findings (all fixed in abdb17d)

1. minor - none of the four fixes was pinned by a test. grep for totalRebaselined|rebaselineNote|'rebaselined' across test/ and the smokes returned nothing. Reverting the scan-skip changed only cost and broke no test; reverting the span attribute was invisible. The scan-skip had been proven by temporary instrumentation, so it would have silently rotted.
Fixed with three tests, each proven to fail on revert: totalRebaselined === 1 on a re-baselining run and === 0 once converged (undefined !== 1 on revert); a capturing TracerProvider asserting the maintenance.partition span carries rebaselined: true (+ undefined - true on revert); and, for the scan-skip, an observation that the resettle scan and the compaction rewrite both read the same data file, so an unskipped scan reads it twice - spying fs.readFileSync and asserting exactly one .parquet read (2 !== 1 on revert). Only those three failed on their respective reverts.

2. nit - the @ref was less precise than it could be: [constrained-by] LLP 0207#foreign-replace pointed at the recognition test while its gloss described what #outranks-resettle actually settles. Fixed by retargeting, with the anchor confirmed to exist.

3. nit - @ref LLP 0207#re-baseline on the span attribute cited a section that says nothing about telemetry. #re-baseline settles the cursor-write shape, not observability, so the ref was close to mechanical by CLAUDE.md's own bar. Fixed by dropping the @ref and keeping the prose comment, which was the genuinely useful part. (The #re-baseline ref on rebaselineCursor itself is untouched and remains a correct [implements].)

4. nit - the PR body slightly overstated item 1. "Only the scan's side effect is skipped" omits that the old || short-circuited past needsCompaction whenever hasResettle was true, whereas compactionDue now evaluates it unconditionally when !force. needsCompaction is pure (readdirSync/statSync), so there is no semantic change and trading a warm stat-walk for a full single-column row scan is a clear win - but the claim implied nothing else moved. Fixed in the body.

Clean

Gates green (npm test 3903 pass / 0 fail, typecheck clean, three cache smokes ok, llp-ref-hygiene 11/11). Both @ref anchors resolve. No em dashes, no semicolons. No Fixes # trailer, deliberately - issue #700 is already closed by #701 and must not be reopened. The llp/0199 edit is a forward-ref field only, a permitted mechanical edit on an Accepted doc.

The head moved to abdb17d, so the next tick reviews it as round 2.

- test/core/cache-retention-maintenance.test.js: the resettle-scan-skip
  test asserted a global .parquet readFileSync count of 1 for the whole
  maintainCache tick, not just reads the gate governs. Any future second
  legitimate read elsewhere in the tick would break it with a message
  that misdirects the next reader. Switch to capturing a stack trace per
  .parquet read and asserting none pass through hasResettleCandidate,
  attributing each read to its caller instead of counting tick-wide.
  Verified both directions: passes at this head (3x, no flake), and
  fails with the expected message when the !compactionDue && gate in
  src/core/cache/maintenance.js is reverted, showing hasResettleCandidate
  in the offending stack.
- PR body: item 2's exemplar list cited llp/0012:9, 0017:9, 0036:9,
  0041:8, 0191:9 as using the linked-and-glossed Extended-by form,
  but only 0191 actually carries a link; the other four are gloss-only.
  Replaced with docs confirmed to use the linked form: llp/0106:9,
  0129:9, 0158:9, 0180:9, 0182:9, 0188:9, 0190:9, 0191:9. (0201, also
  suggested, only has a backward "Extends" pointer, not "Extended-by",
  so it was excluded.)

Co-Authored-By: Claude <noreply@anthropic.com>
@philcunliffe

Copy link
Copy Markdown
Contributor Author

Review round 2 - head abdb17d

Verdict: approve. All four round-1 findings are genuinely fixed, each reproduced independently rather than taken on trust. Two nits, both optional, both taken in 47fd4a4.

Round-1 items, verified by individual revert

Each fix was reverted in a separate worktree and the file re-run, so each test is proven to pin its own change for the stated reason:

  • revert the hasResettle/shouldCompact gate: only the scan-skip test fails, expected: 1 / actual: 2.
  • delete getActiveSpan()?.setAttribute('rebaselined', true): only the span test fails (expected: true, actual undefined).
  • delete totalRebaselined at all three sites plus the types.d.ts field: only the re-baseline test fails.

The scan-skip inference was confirmed by attribution, not just by count. Instrumenting readFileSync with full stacks shows exactly what the test claims: with the fix the single .parquet read comes from scanRowsFromTable under compactGeneration; with the gate reverted, read #1 comes from hasResettleCandidate under maintainGeneration:302 and read #2 is the rewrite. resolver.js:26 reads the whole file with one readFileSync per open on the mutable default fs object, so the mock really intercepts it and no byte cache could hide an unskipped scan. Three consecutive runs, 38/38 each, no flake.

The TracerProvider test does not leak. shutdown() ends with if (globalTracerProvider === this) globalTracerProvider = null, the test calls it in finally, and nothing else in the file registers a provider. scripts/run-tests.js shells to node --test <files>, isolating each file in its own process. Verified both alone and inside the full run.

Items 2-4 all check out: the @ref targets LLP 0207#outranks-resettle (anchor exists, and that section is what actually licenses the skip); the mechanical #re-baseline ref is gone from the span site while the correct [implements] one on rebaselineCursor is untouched; and the body's needsCompaction trade-off is the only behavioural delta, introducing no new throw path (wherever the old code skipped needsCompaction, liveDir provably holds a table, since hasResettleCandidate returning true requires tableExists).

Cross-PR merge re-verified. Merging #698 (05439e6d) into this head: maintenance.js, types.d.ts and the test file all auto-merge; the only conflict is the one-line **Extended-by:** field in llp/0199. Resolving it with #698's side and running the test file on the merged tree gives 38/38, so #698's streaming-row-group rewrite does not disturb the readFileSync accounting the new test depends on.

Item 2 stays unpinned, and that is fine - no hygiene check validates the Extended-by field's form, and a doc-formatting convention is not worth a test.

Nits (both taken)

1. nit - the scan-skip assertion was an exact global count where a targeted attribution is strictly better. dataFileReads === 1 counted every .parquet read in the whole tick, not just the ones the gate governs. Correct today, but any future second legitimate read (a footer-stats probe, a two-pass rewrite) would break it with a message that misdirects the next reader.
Fixed: the test now captures a stack per .parquet read and asserts no stack carries the hasResettleCandidate frame, plus a sanity check that a read happened at all. Verified in both directions - passes at head (3 isolated runs and 3 full-file runs, no flake), and on the reverted gate fails on the intended assertion with the hasResettleCandidate frame visible in the diff output. Immune to unrelated added reads, and it names the culprit. The mock-based technique was already right, since hasResettleCandidate is module-private and scanRowsFromTable is an unpatchable ESM named import.

2. nit - the body's exemplar list for the Extended-by form was mis-cited. Of the five docs cited, only 0191:9 actually carries a link; the other four are gloss-only.
Fixed, and the fix worker went one better than the correction: it verified each candidate itself and excluded llp/0201, which the review had suggested, because 0201 carries a backward Extends rather than an Extended-by. The body now cites eight confirmed docs.

Gate

At 47fd4a4: npm test 3903 pass / 0 fail / 6 skipped, typecheck clean, llp-ref-hygiene 11/11, three cache smokes ok. No em dashes, no added semicolons, no inline import('...') types (the await import(...) hits in query.js are pre-existing dynamic runtime imports, not types), and the added @import { Span } is root-anchored.

The head moved to 47fd4a4, so this PR has used its two review rounds and the next tick triages it. With no residual open findings, that triage should find nothing blocking.

@philcunliffe

Copy link
Copy Markdown
Contributor Author

Neutral triage at head 47fd4a4

Independently re-verified the two review rounds rather than trusting them. Result: no true blockers; one non-blocking test-hardening finding deferred to #707.

Reverts re-run (each in isolation, file re-run after each):

  • !compactionDue && gate reverted: exactly one failure, the scan-skip test, failing on the attribution assertion ("the resettle scan must not read the data file") with at async hasResettleCandidate present in the captured stack. Not a count failure.
  • getActiveSpan()?.setAttribute('rebaselined', true) deleted: exactly one failure, the span test, on "the span, not just the hyp_rebaselines counter, must name which partition re-baselined".
  • totalRebaselined increment removed: exactly one failure, the foreign-replace test, on "the report-level rebaseline count mirrors totalCompacted".

Attribution test stress: 8 targeted runs plus 3 full-file runs, all green. node --test isolates files per process and nothing in the repo or icebird mutates Error.stackTraceLimit. Verified the passing-direction stack attributes the single read to at async compactGeneration and confirmed TracerProvider.shutdown() clears the global provider (no leak). One latent hazard found and deferred to #707: the discriminating frame sits at depth 8 of the default 10-frame stack limit, so a deeper dependency call chain could silently blind the test; the stacks.length sanity guard does not cover that.

Commit 47fd4a4: touches only test/core/cache-retention-maintenance.test.js in the tree; the rest of that commit was the PR body citation fix. No behavioural edit.

Cross-PR merge with #698 (05439e6d): re-merged in a scratch worktree. Only conflict is the one-line **Extended-by:** field in llp/0199, resolved as the union of the two glosses; on the merged tree the maintenance test file (38/38), llp-ref-hygiene (11/11), and typecheck are all green.

Gate at head: npm test 3903 pass / 0 fail, typecheck clean, smokes cache_lifecycle_maintenance, incremental_sink_compaction, cache_roundtrip all ok.

@philcunliffe
philcunliffe marked this pull request as ready for review August 11, 2026 02:20
@philcunliffe philcunliffe added the neutral:approved neutral reviewed this and holds it for a maintainer merge (own or adopted PR; LLP 0025/0030) label Aug 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

neutral:approved neutral reviewed this and holds it for a maintainer merge (own or adopted PR; LLP 0025/0030)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant