Skip to content

fix: recover original saved captions and comments - #227

Merged
devhims merged 8 commits into
mainfrom
fix/saved-evidence-verification
Oct 28, 2026
Merged

devhims merged 8 commits into
mainfrom
fix/saved-evidence-verification

Conversation

@devhims

@devhims devhims commented Oct 27, 2026 •

Copy link
Copy Markdown
Owner

Problem / Motivation

The agent sometimes claimed retained quotations or comments were unavailable because it answered from shortened evidence. This affected the Watercolor, Bicycle, JavaScript and Pothos sessions.

What changed

Add read_saved_evidence and a focused verification skill for bounded reads of session-owned originals. Results preserve words, authors, timestamps and immutable citations. Saved-only requests cannot fetch fresh data.

Research reads requested originals first. Overlapping reads share identical excerpts, preserving later quotations and source warnings. Saved verification receives a 250,000-character evidence budget. Admission and billing remain in place; recovery restores admitted passages without rereading or recharging.

Durable read limits survive retries and asset deletion. Finished-run bookkeeping is pruned, and account deletion clears it. Permanent read failures become explicit gaps; invalid requests and transient failures remain retryable. The production finalizer accepts an uncited gap response only when the same run's persisted state establishes terminal read outcomes or an exhausted read budget, with no admitted content. Other route substitutions remain rejected.

%%{init: {"themeVariables": {"sequenceNumberColor": "#ffffff", "actorBkg": "#eef2ff", "actorTextColor": "#111827", "signalColor": "#334155", "signalTextColor": "#334155"}}}%%
sequenceDiagram
    autonumber
    participant Agent
    participant Store as Saved sources
    participant Answer
    alt Before
        Store-->>Agent: Shortened evidence
        Agent-->>Answer: May report originals unavailable
    else After
        Agent->>Store: Reserve bounded original read
        Store-->>Agent: Exact passages or explicit gap
        Agent->>Answer: Cited answer or verified access limitation
    end
Loading

Original reads supplement existing source storage.

Formatting enforcement and numbered-point citation placement remain in draft #228. Prefix matching and reader consolidation remain deferred. Full replay fixtures preserve the original failure context.

Tests

  • Build, documentation checks, 2,153 unit tests and 462 Worker integration tests passed.
  • Five exact Fireworks replays passed, checking source recovery, citations and zero fresh retrieval.
  • Real DO finalization tests cover recovered gaps and rejected route substitutions.

Regression details and limitations. Presentation checks are diagnostic. Browser reload and Memory correction persistence remain unverified.

@vercel

vercel Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
video2ctx-web Ready Ready Preview Oct 27, 2026 6:47pm UTC

devhims commented Oct 27, 2026

Copy link
Copy Markdown
Owner Author

Review: changes needed

Reviewed exact head 5bd007f094b3505a57505a05f460aa82eda7f0de against 3d11c12a9bb32ed9d3131d7ce2cd0844147e2030 on 2026-10-27. Independent review plus a separate read-only Kiro review; no implementation changes.

The bounded, session-owned original-reader approach addresses the right problem. Ownership/deletion fences, immutable source identity, admission/billing and saved-only provider restrictions have meaningful coverage. However, the recovery path does not yet preserve the new work.

1. P2 — Restore delivered original pages when finalization resumes

research-agent.ts:723–725

The saved reader persists its page to session_packets; this callback admits/bills it and adds it to the in-memory evidence map. AgentRuntimeDO.deliverEvidence does not write that packet to agent_evidence_packets, but current-run recovery reads only that table (DO lines 683, 1448–1452). preparePriorEvidence restores delivered IDs from inherited candidates, not the newly created current-run session page. When a finalization deadline is already persisted, line 687 skips research and its read tools.

Independent reproduction: a real Worker/DO integration test executes sessionStore.searchTools -> read_saved_evidence -> deliverEvidence. The original page is saved, its ID is marked delivered, and a positive test evidence charge exists. Nevertheless, readEvidencePackets(runId) and reconstructed prior content are both empty. This tested persistence/reconstruction, not an actual process kill or a generated answer.

Impact: an interruption after successfully reading an original can leave resumed finalization without the quote/comment already read and charged for. Restore the admitted current-run original packets with their immutable references and existing billing marks; add a finalization-recovery regression. Do not fetch fresh data or charge again merely to recover them.

2. P2 — Persist the structured verification handoff

research-agent.ts:460–470

verificationHandoff is local state only. Runtime restoration supplies the finalization deadline/research context but not these requirements. The resume shortcut at line 687 therefore reaches finalization with no answerFormat, disabling the new validation/repair protections.

Independent two-path reproduction: identical current user text requests “Juniper,” exactly three numbered points, no extra prose, and fenced code. A mock finalizer returns one unnumbered “Cedar” block without fences. Normal execution records requirements, tries twice and rejects without persistence. Execution with finalizationDeadlineAt skips research and accepts/persists that same invalid answer on its first attempt. The prompt still reaches the model; the deterministic enforcement is what disappears.

Persist/recover the handoff before entering finalization, or reconstruct equivalent validated constraints on resume. Test resumed finalization, not only a timeout within the same invocation.

3. P2 — Regenerate the OpenAPI artifact and restore green checks

contracts.ts:27–28 and analogous route variants add savedOnly/skill, but docs/api-reference/openapi.json remains stale.

Independently reproduced with generate-docs.ts --check and test/openapi.test.ts:32. The Verify run also fails Documentation and Platform checks. Regenerate the artifact and rerun both checks before approval.

Nonblocking P3 — Citation insertion can violate “only numbered points”

Format validation precedes final citation rendering. A numbered block ending in a fenced code block with non-inline evidence gets a leading Sources: [cite:ref_1] from structured-answer.ts:273, producing extra unnumbered prose after validation passed. Independently reproduced and also reproduced by Kiro. This is a specific output-contract issue, not a wrong central factual answer. Track it separately or qualify the formatting guarantee; validate the final rendered answer in regression coverage.

Verification

  • Full platform unit suite: 2,144 passed, 1 failed, 59 skipped; failure is the OpenAPI mismatch.
  • Targeted session/replay Worker integration: 87 passed, 5 live-inference tests skipped.
  • Additional reviewer regressions: 2 handoff-path assertions and 1 real-DO persistence/reconstruction test passed, confirming the failures above.
  • TypeScript and generated-agent-skills check passed. Focused 174-test run also passed (overlaps full suite).
  • No production/browser QA or external inference was run. Author-reported live replays and store reconstruction do not establish runtime-restart correctness or broad live resolution. No product-credit usage was incurred by these local tests; local ledger charges are fixture data.

Verdict for this head: CHANGES NEEDED. Resolve recovery persistence and the failing checks, then independently re-review the updated head. No merge/deployment approval.

devhims commented Oct 27, 2026 •

Copy link
Copy Markdown
Owner Author

Re-review correction: CHANGES NEEDED

2026-10-27 15:13 UTC update for unchanged head 68fa61b5a595a94e8a55e964d46112c02871d703: the approval below is withdrawn. New review comments prompted additional independent reproductions that our earlier coverage missed. The previous recovery and OpenAPI fixes remain verified; they do not resolve these newly established cases.

P2 — Empty formatting constraints disable an otherwise available partial-answer fallback

research-agent.ts:874–883

answerFormat is required in the verification handoff, so checking its object truthiness suppresses fallback even for {name:null, numberedPoints:null, onlyNumberedPoints:false, fencedCode:false}. Two independent deterministic full-run tests use identical eligible analyzed-frame findings and two malformed finalizer responses: without a handoff the run persists an evidence partial; with this all-empty format it throws and never persists. This is conditional on finalization failure and eligible fallback evidence; raw original captions alone do not support the existing fallback. Gate rejection on actual constraints rather than mere object presence, and cover the unconstrained saved-verification path.

Confirmed validator edge requiring repair

verify-saved-evidence.ts:113–120

A valid three-block answer with a three-space-indented numbered sublist inside point 2 is rejected because nested markers are counted as additional top-level points. Independent unit reproduction passes, alongside controls for an ordinary three-point answer and Markdown > blockquoted numbering (both accepted). The general claim that all quoted numbering fails is therefore too broad. Runtime consequence and remaining comments are still being reconciled with Kiro; no production testing or implementation has occurred.

Current verdict: CHANGES NEEDED. Do not rely on the earlier ready-to-merge guidance. This updates the existing review rather than creating a duplicate comment for the same head.


Earlier 14:15 UTC review, retained for history; verdict superseded above

Re-review: APPROVE

Reviewed exact head 68fa61b5a595a94e8a55e964d46112c02871d703 against base 3d11c12a9bb32ed9d3131d7ce2cd0844147e2030 on 2026-10-27. Revisited the previous review, independently inspected the update and surrounding recovery/finalization paths, and consulted the existing read-only Kiro review session.

No material unresolved finding identified for this head. The three prior P2 findings are addressed:

  1. Admitted original-page recovery: agent-runtime-do.ts:1448–1468 restores saved-original packets through the current run's delivery ledger, deduplicates packet IDs and checks that referenced immutable versions still exist. The new Worker regression covers finalization-boundary reconstruction, unchanged billing, no reread, and exclusion of deleted/undelivered/other-run content. My separate reader→admission→recovery regression now also recovers the original quoted text.
  2. Checkpointed requirements: context-handoff.ts:19–37 validates and persists requirements before acknowledging them; research-agent.ts:460–471 restores them. My normal/resumed-path regressions both reject the same invalid name/numbering/code-fence answer after the repair allowance, without persisting it; resumed finalization skips research as intended.
  3. Generated OpenAPI: regenerated artifact matches the route schemas. The formerly failing OpenAPI/unit/docs checks now pass.

The prior nonblocking P3 numbered-code citation case is also addressed: structured-answer.ts:274–285 keeps remaining citations in the numbered point, and the finalizer validates rendered blocks.

Verification on this head

  • Independent full platform unit suite: 2,148 passed, 59 skipped.
  • Independent full Worker integration suite: 431 passed, 5 live-inference tests skipped.
  • OpenAPI generation check, generated-agent-skills check and TypeScript: passed.
  • Additional reviewer regressions: 2 resume-format tests + 1 real Worker saved-original reconstruction test passed. An initial inherited-content assertion in my adapted harness was incorrect: current-run recovered packets are already loaded and deliberately excluded from inherited content; corrected the harness, not product code.
  • Verify CI run: all eight jobs successful, including both dashboard E2E groups.
  • Kiro's separate read-only review independently approved this same SHA after 38 focused tests, the new DO recovery test and a citation-render check. Those overlap coverage above and are not added to my totals.

Limits: deterministic model fixtures and persisted-store reconstruction are not an actual process-kill test, production browser reload test or fresh external-inference replay. Author-reported earlier model replays are not my live verification. Live dashboard QA remains paused; this approves the code change, not broad live resolution of issue #1 or unrelated citation semantics/Memory correction issues. No product credits used by my local checks; ledger amounts are fixture data.

Verdict for 68fa61b: APPROVE — ready for the user's review/merge decision. No code changes, push, merge or deployment performed by the reviewer.

@devhims devhims left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review notes on the saved-evidence verification path. The first three can make saved-verification runs fail or stall; the rest are smaller correctness and cleanup items.

const failure = finalizationFailure(finalizationError, finalizationFailures);
// An unstructured evidence summary cannot satisfy a validated answer format.
// Keep the saved evidence reusable and fail clearly if the single repair failed.
if (verificationHandoff?.answerFormat) throw failure;

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partial-answer fallback never runs. answerFormat is required by savedVerificationHandoffSchema, so verificationHandoff?.answerFormat is truthy for every run that recorded requirements. Any saved-verification run whose finalizer fails twice now throws, even when the user set no name, count or code requirement and the admitted originals could have produced a partial answer.

Suggest gating on an actual constraint, e.g. answerFormat.name || answerFormat.numberedPoints !== null || answerFormat.fencedCode.

...(storyboardRetrievalBudget(options.researchDeadlineAt) < STORYBOARD_RETRIEVAL_MIN_MS ? ['get_video_storyboard'] : []),
...(!canAnalyzeStoryboard(options.researchDeadlineAt) ? ['analyze_video_storyboard'] : []),
...(needsVisualWork() ? [completionTool] : []),
...(unreadOriginals().length ? [completionTool] : []),

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Completion can stay locked for the whole run. unreadOriginals() only counts a source as read once an admitted packet with excerpts lands in evidence. If deliverEvidence withholds the packet (credit reserve exhausted), or every saved asset for the subject is empty (e.g. a comments page with zero comments), complete_research never becomes available. The model then burns reads until the step, tool-call or 120s limit forces finalization.

Consider treating a withheld or empty read of the asset as satisfying the requirement and passing it as a handoff gap instead.

});
if (format.name && !prose.join('\n').includes(format.name)) fail(`Include the current name ${JSON.stringify(format.name)} in the answer prose, inside a numbered point when only points are requested.`);
if (format.numberedPoints !== null) {
const numbers = [...prose.join('\n').matchAll(/^\s{0,3}(\d+)[.)]\s/gm)].map(match => Number(match[1]));

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nested numbered lines count as extra points. This regex matches every prose line starting with N. or N) across all blocks, so a short sub-list or a quoted line starting with a number inside a valid point fails validation (e.g. 3 points where point 2 contains 1. preheat / 2. mix gives [1,1,2,2,3]). Together with the rethrow at research-agent.ts:879, the run fails instead of returning a well-structured answer.

When onlyNumberedPoints is set, checking only the first line of each block (already done just below) seems sufficient; otherwise only count list markers at the start of each block.

}
return chunks;
});
const tokens = request.query?.toLowerCase().match(/[\p{L}\p{N}_]+/gu);

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tokenized query, substring match. Punctuation is stripped when splitting the query, but each token is then matched with includes against raw text. A search for don't becomes ["don", "t"]: t matches nearly every caption window and don matches London/done. That wastes the 16-read budget on huge result sets. Matching on word boundaries (tokenize the haystack the same way), or keeping intra-word apostrophes in tokens, would fix it.

: comments.flatMap(({ comment, position }) => {
// Split long comments without discarding their tail or changing their words.
const text = comment.text;
const suffix = `\nAuthor: ${comment.author.name.slice(0, 200)}\nComment ID: ${comment.id.slice(0, 200)}\nSaved position: ${position.map(index => index + 1).join('.')}`;

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Metadata labels are searchable. The search text includes Author:, Comment ID: and Saved position:, so query words like author, comment or position match every comment. Consider searching text plus the author name only, without the label words.

characters += size;
}
const nextOffset = request.offset + page.length < matching.length ? request.offset + page.length : undefined;
const sourceMeta = (value as Transcript | CommentsPage).meta;

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assumes newer blob shapes. This assumes every saved blob has meta.warnings, and flattenComments assumes every comment has a replies array. The existing savedComments helper in session-evidence.ts is more tolerant. An older saved page missing either field throws a TypeError, and the model may then report the saved comments as unavailable, which is the failure this PR is fixing. Suggest meta?.warnings ?? [] and comment.replies ?? [].

};
const applyConstraints = (decision: Record<string, unknown>) => {
const fill = fillInto(decision);
if (constraints.savedOnly) {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

savedOnly silently overrides a refresh request. In the last-resort merge, savedOnly from any candidate wins and deletes refreshEvidence/refreshDynamicData set by another candidate. A request like "get fresh comments and quote the saved transcript" can end up answered from stale comments with no note that the refresh was dropped. Conflicting candidates might be better resolved as a clarification, or at least surfaced as a warning.

inputSchema: readSavedEvidenceInputSchema,
execute: async input => {
signal.throwIfAborted();
if (++calls > SAVED_EVIDENCE_READ_LIMIT) throw new Error('Saved-evidence read limit reached. Answer from the passages already read and state remaining gaps.');

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Read cap is per tool instance. calls lives in the closure, so the 16-read limit resets whenever searchTools is rebuilt (recovery, a new phase), and the bound isn't enforced per run. It also counts calls that fail validation or hit a deleted asset. Consider persisting the count per run (or deriving it from delivered read_saved_evidence operations), and incrementing only after a successful read.

{ message: 'endMs must be at least startMs.', path: ['endMs'] });

/** Reads only session-owned originals. There is deliberately no provider dependency. */
export async function readSavedEvidence(store: SessionEvidenceStore, input: z.input<typeof readSavedEvidenceInputSchema>, signal?: AbortSignal) {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overlaps the existing reader. This mostly repeats SessionEvidenceStore.readEvidence / readCommentEvidence (paging, query filtering, packet building), with different query rules (all-words vs phrase), different excerpt IDs (saved: vs evidence:) and different reply handling. Fixes to one path won't reach the other, and the same passage gets two citation IDs. Worth sharing a single excerpt builder for both.

signal?.throwIfAborted();
const request = readSavedEvidenceInputSchema.parse(input);
const generation = store.generation();
const saved = await store.readAsset(request.assetVersion);

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repeated work per read. Each call does readAsset without a RunAssetCache, rebuilds the full excerpt array, and joins and lowercases a 3-segment window for every excerpt, up to 16 times per run. Passing the run's asset cache and precomputing lowercase segment text once per asset version would avoid it.

@devhims devhims changed the title fix: recover saved evidence and preserve follow-up formatting fix: recover original saved captions and comments Oct 27, 2026

devhims commented Oct 27, 2026

Copy link
Copy Markdown
Owner Author

Re-review: CHANGES NEEDED

Reviewed exact head ad251db51e8f2306ca74b411e24640019db21157 on 2026-10-27, including the new delta, prior findings, current comments, and a separate read-only Kiro review. The earlier approval of 68fa61b was withdrawn in its existing comment; this verdict applies to the new head.

P2 — Gap-only answers fail the real persistence route contract

research-agent.ts:940–945 switches a saved-original verification with no admitted excerpt IDs and a saved_original_not_read gap to context_answer. However, agent-runtime-do.ts:975–985 validates against the persisted executable route, and capability-router.ts:905–912 allows that route's own intent or clarification—not context_answer.

Independent reproduction: two real Worker/DO-boundary regressions persist an inspect_video saved-verification route, resume finalization with no recovered evidence and respectively an empty/withheld original-read state, and give the finalizer a valid gap-only answer. runResearchAgentWithModel calls the actual performFinalizeRun, which throws AGENT_INTENT_MISMATCH: Final intent context_answer does not match the persisted route inspect_video. The outer run fails and result_json remains null. The model and read-state fixtures are deterministic; the route check and persistence boundary are real. The corresponding topic-research incompatibility is code-inspected, not a separate runtime reproduction.

Impact: in this narrow no-admitted-evidence case, a user gets a failed answer instead of the honest empty/access-gap explanation the new branch intends. The earlier gate is unlocked, but completion still fails at persistence. Cases retaining admitted evidence are different and passed our tests. This is an incompletely repaired edge path, not evidence that every saved-source request fails or that production users encountered it.

Make the gap-only output and persisted-route contract consistent without fabricating citations or silently enabling fresh retrieval. Add a regression through the real finalization boundary, not only buildAgentTurnResult, for both empty and withheld originals.

Scoped improvements verified

  • Eligible analyzed-evidence fallback is restored after malformed finalization; the old all-empty format guard no longer suppresses it.
  • Empty/withheld read outcomes unlock completion and propagate gaps when other admitted evidence remains. An unsuccessful query is not treated as proof an original is empty.
  • Search now uses whole words/contractions and excludes generated metadata labels; missing metadata/replies are tolerated.
  • Conflicting saved-only/fresh classifier fallbacks now clarify.
  • Actual SQLite reservation tests across 20 concurrent tool instances allow 16 reads and reject four; exhaustion survives reopening, while a different run gets its own budget. Unknown/deleted assets and ineffective foreign outcomes are rejected/filtered.
  • Blocking formatting/checkpoint machinery was removed into draft PR228, not repaired here. Naming/formatting remains prompt guidance; this review does not approve PR228 or establish correction-memory guarantees.

Verification and limits

  • Independent platform units: 2,150 passed, 59 skipped.
  • Independent full Worker integration: 437 passed, 5 live-inference tests skipped.
  • Additional focused reviewer regressions: 16 passed covering fallback, nested output, reader/search/routing/bounds; the initial completion fixtures retained analyzed evidence and therefore did not test the zero-evidence defect above.
  • Two additional real-DO boundary regressions passed by asserting the defect (empty/withheld cases; 102 unrelated tests skipped). They are not successful product outcomes.
  • TypeScript, generated skills/docs checks and git diff --check: passed. The local tsx CLI hit an IPC permission error; equivalent node --import tsx checks passed.
  • Verify CI run40529117606: all eight jobs successful; Vercel status successful. These green suites do not cover the newly reproduced boundary mismatch.
  • Kiro independently flagged the same route mismatch and passed 19 focused unit/7 targeted integration tests; its first reproduction mirrored the predicate, so the real-DO confirmation above was performed separately.
  • No production/browser QA, real provider replay, process-kill test or production-credit measurement. Live QA remains paused. No product credits used by our local tests; ledger charges are fixtures. Sequential reread caching and reader duplication remain nonblocking optimization/maintenance concerns, not measured user-impact defects.

Verdict: CHANGES NEEDED for ad251db. Resolve the gap-only persistence mismatch and re-review the updated head. No reviewer implementation, push, merge or deployment.

@devhims devhims left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up review on ad251db. The split looks right, and 8 of the earlier 10 notes are addressed. Five new notes below; the first two are worth fixing before merge.

add(packet);
continue;
}
if (packet.savedEvidence) {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Later original reads can be dropped whole. Originals that don't fit the finalizer budget are omitted in read order, and overlapping reads repeat the same captions. A topic_research saved-verification run without comparisonVideoIds gets the 40,000-character budget (research-agent.ts:937), while 16 reads can return up to ~192,000 characters. The first broad search can fill the budget, so the narrowed read that actually holds the quote is the one cut, and the answer may still say the quote is unavailable.

Suggest deduplicating excerpts by ID across saved-evidence packets before packing, and giving saved-verification runs the inspection budget.

const result = await readSavedEvidence(store, input, signal);
signal.throwIfAborted();
const packets = admit(result.packets, 'read_saved_evidence') ?? result.packets;
const outcome = result.coverage.totalExcerpts === 0 ? 'empty' : result.coverage.returnedExcerpts > 0 && packets.length < result.packets.length ? 'withheld' : undefined;

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repeating read errors never become terminal. Only empty and withheld are recorded. A read that throws every time (e.g. a saved transcript over the duration limit raises VideoTooLongError from readAsset, or the blob is unreadable) records nothing, so pendingOriginals keeps complete_research locked until all 16 reserved reads are used.

Suggest recording an unavailable outcome when readSavedEvidence throws for a known asset version.

sql.exec(`CREATE TABLE IF NOT EXISTS session_memory_writes (id TEXT PRIMARY KEY, source_turn INTEGER NOT NULL)`);
sql.exec(`CREATE TABLE IF NOT EXISTS session_memory_state (id INTEGER PRIMARY KEY, version INTEGER NOT NULL)`);
sql.exec(`INSERT OR IGNORE INTO session_memory_state VALUES (1, 0)`);
sql.exec('CREATE TABLE IF NOT EXISTS session_original_reads (run_id TEXT PRIMARY KEY, attempts INTEGER NOT NULL)');

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

session_original_reads is never cleared. Asset deletion removes outcomes, but nothing deletes these rows, and account deletion (agent-runtime-do.ts:1420) wipes session_run_generations and other run-keyed tables but not this one. Run IDs survive account deletion, and the table grows by one row per run.

Suggest adding it to the account-deletion table list and to delete() when called without a version.

// Captions can split a phrase across consecutive segments. Comments never mix authors.
const text = asset.kind === 'comments' ? commentSearchTexts[index]!
: excerpts.slice(index, index + 3).map(item => item.text).join(' ').toLowerCase();
const words = tokens ? new Set(searchWords(text)) : undefined;

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whole-word matching misses inflections. This fixes the apostrophe problem, but mix no longer matches mixing and lube no longer matches lubed. The skill tells the model to search one distinctive word from the request, so a request about "mixing greens" searched as mix returns nothing and costs extra reads.

Prefix matching for tokens of 3+ characters would keep the precision fix and recover common inflections.

'Prefer current assets over superseded versions unless the user asks for a historical comparison. A failed refresh does not make an old snapshot fresh; retain its collection time and explain the failure.',
'Packets with savedEvidence contain selected original captions or comments, not analyst paraphrases. Use their exact text for requested quotations. Put supporting references only in each block\'s evidenceIds array; the application renders precise passage links from segment startMs. Do not put asset IDs, excerpt IDs, startMs/endMs fields, millisecond values or hand-written timestamp URLs into the prose. Preserve source identity. Do not claim timestamps or captions are absent when these packets supply them. A quote spanning several segments must cite those segments; do not guess corrected brand spellings from noisy captions. Coverage describes a selected page, not a complete transcript review.',
'The current user message can correct earlier memory. Prefer explicit current corrections over old context.',
'When continuing a named plan, use its latest user-specified name within the requested answer format. Preserve the latest formatting constraints from conversation history, including numbered-point counts and requests for no introduction or closing section.',

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Named-plan guidance applies to every finalization. This line isn't gated, so every answer on every route gets instructions about plan names and point counts. SAVED_EVIDENCE_ANSWER_SKILL already covers this for saved-verification runs. Suggest gating it on usesSavedEvidenceSkill or moving it to #228 with the rest of the formatting work.

devhims commented Oct 27, 2026

Copy link
Copy Markdown
Owner Author

Changes needed — a28edbd0ccc4e8e150d8a7e72c4437440ac8dfd1

Re-reviewed the new delta against ad251db, the surrounding production boundary, prior findings, and current review discussion. The earlier approval is not restored.

P2 — zero-evidence saved-source gaps still fail at the real finalization boundary

research-agent.ts:942–947 changes the final intent to context_answer when original reads leave no admitted passages. However, AgentRuntimeDO:978–987 still checks the persisted route, and finalIntentMatchesRoute:905–911 rejects context_answer for inspect_video.

Personally reproduced on this exact head using the actual Worker/SQLite AgentRuntimeDO.performFinalizeRun boundary, with deterministic model output:

  1. Seed a running run and persist an inspect_video / savedOnly: true / verify_saved_evidence decision.
  2. Recover with no admitted evidence and a saved-read outcome of empty, withheld, or the newly added unavailable.
  3. Generate an honest, uncited access-gap answer and pass it through the real DO finalizer.
  4. All three cases throw AGENT_INTENT_MISMATCH: Final intent context_answer does not match the persisted route inspect_video.; research rejects with “The final answer could not be completed” and persisted result_json remains null.

Expected: a completed, honest gap response, with no fabricated citation or fresh retrieval. Impact: users whose saved source is empty, withheld, corrupt/missing, or otherwise permanently unavailable can still lose the whole answer instead of receiving the intended limitation. This is the prior blocker, now also reproduced for the new unavailable path—not a claim that ordinary evidence-backed answers fail.

Please align the gap-answer contract with the real persisted-route boundary and add integration coverage that calls that boundary. Do not broadly allow arbitrary context_answer route substitutions. The current “research can complete with an explicit … gap” tests at session-evidence.integration.test.ts:1841–1854 replace ctx.finalize with buildAgentTurnResult, bypassing the rejecting check.

Scoped progress and independent checks

  • Exact-original deduplication preserves source/version/span identity and explicit budget gaps; the saved-verification budget increase, permanent-versus-transient read handling, terminal/account-deletion cleanup, and removal of global named-plan guidance are covered by the current deterministic suite. No additional material defect established in those delta paths.

  • Passed independently: 2,153 unit tests (59 skipped); 449 Worker integration tests (5 live-provider tests skipped); platform TypeScript check; generated skill and documentation checks.

  • Failure reproduced: 3 temporary reviewer regression cases above. These are defect-assertion tests: their passing asserts the undesired rejection, not successful product behavior. Review-only test additions were removed afterward.

  • CI Verify #583 is terminal cancelled, not fully green: Auth E2E and Library/skill-bundle jobs cancelled; the other six jobs succeeded. Required cancelled checks remain unverified.

  • No fresh live dashboard/provider QA, implementation, push, merge, or deployment. Author-reported Fireworks replays are not counted as my independent live verification. Browser reload and Memory persistence remain unverified. Draft draft: checkpoint and validate saved answer formatting #228 remains separate; deferred prefix matching is not being expanded into this PR’s scope.

  • Kiro's existing PR227 session accepted a read-only latest-head review request, then signed out before its result could be read. The verified organization route now requires secure sign-in. Writer/reviewer alignment and relay of the confirmed boundary reproduction are blocked, not completed; no implementation was requested.

Verdict: changes needed. Resolve the real-boundary regression and obtain completed required checks before approval.

devhims commented Oct 27, 2026

Copy link
Copy Markdown
Owner Author

Changes needed / not ready — 49518044d8e48db9ee27c149505059a03291cc89

The previous P2 code blocker is resolved in the tested scope. No new material code defect found in this delta. Approval remains withheld only because the required Kiro writer/reviewer alignment is blocked by sign-in; this is a workflow blocker, not a claim that the old failure persists.

Scoped fix verification

  • AgentRuntimeDO:954–967, 997–998 now permits the uncited limitation answer only for persisted saved-verification routes with zero admitted excerpts and no pending original reads. Ordinary route matching remains unchanged.
  • Independently reran the committed real Worker/DO boundary regressions: empty, withheld, unavailable and exhausted-read recovery complete for inspection and comparison, with persisted completed result rather than AGENT_INTENT_MISMATCH.
  • Added temporary reviewer variants: comments-only originals across both routes/four terminal states; all nine rejection cases on topic_research (ordinary, pending, foreign-run, deleted, another pending original, inline citation, source artifact, claimed coverage, admitted content). 13 test cases passed, exercising 17 DO scenarios. No provider fetch occurred in the positive cases. These are deterministic model outputs through actual SQLite/DO finalization, not a mocked finalize callback. Temporary test additions were removed; checkout is clean.
  • This prevents the previously reproduced whole-answer failure when no saved original can be admitted. It does not prove arbitrary answer prose is true: honest gap wording remains model/prompt behavior, while this guard validates durable evidence/read state and citation/artifact constraints.
  • The numbered-point citation placement change at structured-answer.ts:273 is deliberately deferred to draft draft: checkpoint and validate saved answer formatting #228. No formatting expansion requested here.

Independent validation

  • Unit suite: 2,153 passed; 59 skipped.
  • Worker integration suite: 462 passed; 5 opt-in live-provider tests skipped.
  • Additional reviewer boundary suite: 13 passed. An initial reviewer comparison fixture used one ID and was correctly schema-rejected; corrected to two IDs before the result above. That was a test-fixture error, not an application defect.
  • Platform TypeScript, generated agent skills and generated documentation checks passed.
  • CI Verify #584: terminal success, all eight jobs successful, replacing the prior cancelled-check limitation.

Remaining blocker / limits

Kiro's existing PR227 session remains signed out, personally rechecked 2026-10-27 16:49 UTC. The existing secure organization sign-in is pending. The previously accepted review's result remains uncertain; latest-head feedback has not been delivered to or reconciled with Kiro. Required writer/reviewer alignment is therefore not verified. Complete that existing secure sign-in, then reconcile the same PR/session and this exact head; no new product decision or code change is requested by this review.

No fresh live dashboard/provider QA was run (testing remains paused). Author-reported Fireworks replays are not counted as my live verification; production behavior, browser reload and Memory persistence remain unverified. No implementation, push, merge or deployment.

Verdict: changes needed / not ready solely pending required Kiro alignment. The prior P2 boundary defect and cancelled CI checks are no longer the blockers on this head.

Move the opt-in live Fireworks replays out of this PR, since they also
score naming and formatting (draft #228). Fold the deterministic Bicycle
and JavaScript replays into session-evidence.integration.test.ts, merge
the saved-evidence unit tests, and combine fixtures into two files. The
vitest configs return to main.

devhims commented Oct 27, 2026 •

Copy link
Copy Markdown
Owner Author

Independent code review: approve / good to merge — 1c069854bb9b7f755754058cc13ba736fffe3f3e

No new material code defect found. This head changes tests, fixtures, test configuration and documentation only; production code is identical to the previously reviewed 4951804. The earlier saved-source gap finalization fix is not regressed in the independently rerun deterministic coverage. The old P2 failure is no longer present in that coverage. Based on the independent code review and validation below, this exact head is good to merge. The unfinished Kiro cross-check is not a code blocker.

Delta review and validation

  • Verified that the five relocated verification unit tests, three deterministic Bicycle/JavaScript replay tests, consolidated saved-source fixtures and full Watercolor caption arrays retain their assertions/data. No stale deleted-module imports were found.
  • Passed independently on this exact head: 2,152 unit tests (59 skipped); 462 Worker integration tests, including the actual SQLite/DO finalization regressions; platform TypeScript; generated agent skills; generated documentation checks. Checkout remains clean.
  • The unit count falls by one because the quote-parsing helper and its test were removed together. The five opt-in real-model replays are removed, not passed or skipped on this head.
  • Initial local checks encountered missing dependency links in the new worktree and a sandbox restriction on the tsx CLI IPC socket. Restoring existing dependency links and using node --import tsx resolved those harness issues; the results above are the successful reruns, not product failures.
  • CI Verify #585 is terminal success, with all eight jobs successful.

Coverage and documentation precision

SESSION_EVIDENCE.md:164 describes the removed replays as scoring naming/formatting. In the prior head those presentation checks were diagnostic-only; the blocking assertions exercised source fidelity and citations. This is a non-blocking documentation clarification, not a production defect: the important coverage distinction is that those real-model end-to-end assertions no longer run in this PR. They remain inspectable at 4951804; prior author-reported passes are not my independent live verification.

Incomplete Kiro cross-check — not a code blocker

Personally rechecked Kiro on 2026-10-27 at 18:51 UTC. The existing PR227 session's organization sign-in reaches the password step for the existing account; it is not authenticated. I did not duplicate the already-pending secure credential request. The previous accepted review result still cannot be reconciled, and latest-head feedback cannot yet be delivered to that session. Writer/reviewer alignment is not verified; no alignment or formal ready-for-review status is claimed. Completing the existing secure sign-in would allow reconciliation of that same session and exact head, but the authentication issue does not negate the independent code approval above. No implementation or new product decision is requested.

No fresh live dashboard/provider QA was run; testing remains paused. Browser reload, production behavior and separate Memory persistence remain unverified. No code implementation, push, merge or deployment.

Verdict: approve / good to merge at 1c069854bb9b7f755754058cc13ba736fffe3f3e based on independent code review. No remaining material code issue was found; tests and CI pass. Kiro alignment remains incomplete and production verification remains outstanding. This supersedes the earlier changes-needed label that was based solely on Kiro authentication, not a code defect. No formal PR ready/draft status is changed.

@devhims
devhims merged commit 2749fc4 into main Oct 28, 2026
10 checks passed
@devhims
devhims deleted the fix/saved-evidence-verification branch October 28, 2026 10:27

This branch was successfully deployed

1 active deployment
Preview — 1c069854 Deployed Oct 27, 2026 by vercel[bot]
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