Repository navigation
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Merge Risk: ⚪ Minimal · up to
|
f08be2b to
4d56f74
Compare
barbatdev
left a comment
There was a problem hiding this comment.
Good seam, and the corroboration design is exactly the fail-closed shape we agreed on: deep-equal over a detached JSON snapshot, failed ids never admitted even when present on disk, all twelve reasons reachable without leaking contents, and an unreadable file never restores a memory-only profile. An independent adversarial QA sweep (33 probes: 100k-entry files, deep nesting, duplicate ids across lineages, source-changed windows, unserializable sources) found zero deviations, linear performance, and no escaping throws. That part I fully endorse.
Three things block this one for me:
- The scan aborts on the first malformed line, but Pi's own parser skips malformed lines. A file Pi opens fine can end up permanently indeterminate here, and that blocks append-controller recovery in #1948, which is the whole point of this slice. Skipping malformed lines as session-manager does, with a test for that exact scenario, would close it.
- The carried test debt from #1918/#1919 is still open on the new seam. Non-string customType, non-string kind and empty agent name remain untested anywhere, and
reasonis typed as a bare string, so #1948 cannot exhaustively match on it. Type it as the twelve-literal union and add the missing branches while you are in there. - The family prefix is re-derived with slice(lastIndexOf) instead of an exported constant. If SESSION_PROFILE_CUSTOM_TYPE ever loses its slash, every custom entry becomes a candidate. Export the constant and share it.
Two doc-level notes from the same sweep, non-blocking: BOM-prefixed files fail closed as invalid-json (fine if Pi never emits one, worth a line in the format doc), and missing-header is only reachable on an empty or all-blank file, which is internally consistent but surprising.
None of the three touch the design; they are execution. Happy to review the follow-up push quickly.
|
Checking in on this one, since it now gates the whole 3b-i stack: #1918 and #1919 landed on the 12th, and #1948 → #1993 all wait behind it. The three blockers from the last review still stand:
Now that #1919 is in, a rebase should also shrink the Files tab to just this slice. Are you still on this? If your time is tight right now, say so and we can pick up these three fixes on the branch ourselves so the stack keeps moving. Either way works, we just want to keep 3b-i from stalling. |
|
Yes, still on it, thanks for the nudge. Working on the three blockers now (malformed-line skipping with a test, the missing type/empty-name cases plus the twelve-literal |
Part of Gentleman-Programming#1064 (slice 3b-i, disk reader). readSessionProfileDisk reads the session's public active branch and JSONL file, selects the newest profile-family entry (excluding known failed append IDs) and admits it only when the record on disk is byte-identical; otherwise it returns one indeterminate reason without record contents. No writes, fallback, cache, ancestry repair or fsync; a missing file never restores a memory-only profile. Verified against a real SessionManager session file. Chain: main -> decoder (Gentleman-Programming#1918) -> encoder + replay (Gentleman-Programming#1919) -> [this]. Depends on: Gentleman-Programming#1919. Out of scope: append controller, authority publication, Enter wiring.
…entleman-Programming#1064) - Skip malformed JSONL lines like Pi's session parser, so a file Pi opens never becomes permanently indeterminate; drop the unreachable invalid-json reason. - Export DiskProfileIndeterminateReason as a literal union for exhaustive matching. - Share the exported SESSION_PROFILE_FAMILY instead of re-deriving the prefix. - Cover non-string customType, non-string kind and the empty agent name.
4d56f74 to
339ca4a
Compare
|
@barbatdev pushed, rebased onto main. The Files tab is now just this slice (5 files), and the fixes are a separate commit on top (339ca4a) so they're easy to review.
Docs: Checks: the session-profile tests (98), typecheck, provider-contract and runtime-harness pass. Unit-test failures are compared against main: no new ones. The binary-digest reuse test flakes on main too (2 of 4 runs). |
barbatdev
left a comment
There was a problem hiding this comment.
Thanks for the quick turnaround here, the fixes land all three blockers cleanly. On the malformed lines, matching Pi's session parser with physical line numbers preserved is the shape I wanted, and testing it through a real SessionManager.open on a file with a torn trailing line is the strongest possible evidence. Corroborating both before and after a new append in that same test covers the exact scenario I was worried about.
The typed reason union is better than what I asked for. The satisfies Record<…, true> guard means typecheck rejects any drift in either direction, so the union can't rot. On the count, dropping invalid-json is the right call. It was only reachable from the parse catch and skipping makes that path unreachable, so shipping a literal #1948 could never hit would be worse than an eleven literal union. The fail-closed outcomes through selected-record-missing and missing-header preserve the contract, and both the docs and your comment here make the deviation traceable. Consider that blocker closed.
The shared SESSION_PROFILE_FAMILY constant closes the last one, no more re-deriving the family with a slice. The BOM and missing-header doc notes also cover both of my non-blocking items from the sweep. CI is green and nothing in this round touches the fail-closed design I endorsed on the 12th, so I am approving. This unblocks the 3b-i stack, and when you rebase #1948 the Enter-before-flush and append-failure boundaries we confirmed on the issue this week apply directly to your persistence-outcome handling in runProfilesPanelAction.
Summary
Part of #1064, slice 3b-i (persistence). Supplies the corroboration that
replaySessionProfileBranch(#1919) requires but cannot establish: whether the selected record on the active branch is actually on disk.readSessionProfileDisk(source, options): stateless, synchronous. Takes the session id, session file path and public active branch; selects the newest profile-family entry on the branch, excluding caller-suppliedknownFailedEntryIds; parses the JSONL file; admits the entry only when the disk record is identical (isDeepStrictEqualover a JSON snapshot, so getters and mutable references cannot leak in).entryIndexandlineNumber, orindeterminatewith one closed reason (missing-source,invalid-candidate-metadata,unreadable-file,source-changed,invalid-json,invalid-record,session-header-mismatch,duplicate-id,missing-header,selected-record-missing,selected-record-mismatch,unserializable-source). Reasons never carry record contents or exception messages.source-changed.This is PR 3 of the chain.
Issue
Part of #1064
PR type
type:feature)Changes
4d56f74b5lib/session-profile-disk-reader.ts,tests/session-profile-disk-reader.test.ts,tests/session-profile-disk-reader-pi.test.ts, Disk corroboration section indocs/session-profile-format.md.Test plan
Verified on top of #1919,
main9782d26df, Node 25.2.1:tests/session-profile-disk-reader.test.ts: 29 pass, 0 fail (every indeterminate reason, failed-ID exclusion, source-changed detection, hostile getters, duplicate and header checks).tests/session-profile-disk-reader-pi.test.ts: 4 pass, 0 fail, against a realSessionManagerfrom@earendil-works/pi-coding-agentwriting to a temp directory.tests/session-profile-persistence.test.ts+agent-profiles+model-routing-authority: 77 pass.node scripts/check-types.mjs: 186 recorded diagnostics, no regressions.Review follow-ups (non-blocking)
replaySessionProfileBranch(feat(profiles): add session profile encoder and active-branch replay #1919), because it needs the raw branch index and the failed-ID exclusion before decoding. The "newest family entry is terminal" rule therefore lives in two places; happy to fold one into the other if you'd rather.Chain Context
main(fork PRs cannot stack bases; rebased as #1918/#1919 land)Summary by CodeRabbit
New Features
Documentation