feat(sources): capture aistudio-drive draft input evidence - #3415
Conversation
Problem: polylogue-o4j2 found the AI Studio chunkedPrompt.pendingInputs block unread at parse. It carries the operator's not-yet-submitted textbox content -- draft prompts that never became a chunk and are unrecoverable once Drive syncs over them. A live archive check (397 aistudio-drive raw sessions) found pendingInputs present on 396/397 records, 7 with non-blank draft text (one a substantial multi-paragraph prompt never sent). What changed: parse_chunked_prompt now emits a draft_input session_event per non-blank pendingInputs entry (text/role/token_count), skipping the near-universal blank-textbox case as non-evidence. Most of this bead's scope (runSettings -> ParsedSession.run_settings / sessions.run_settings_json) was already shipped by PR #3390 (polylogue-2qx.4/cgfy, index v46); this closes the one remaining unread field the bead named. Verification: devtools test tests/unit/sources/test_parsers_drive.py (31 passed); devtools verify --quick (all steps ok). Ref polylogue-o4j2
polylogue-j8yo records the live-browser investigation into whether an aistudio.google.com browser-capture adapter would be cheap given the Drive-sync-only hypothesis: SKIP, with CDP-Network evidence (undocumented application/json+protobuf internal RPC transport) and cross-reference to the pendingInputs/runSettings findings from o4j2. Ref polylogue-o4j2 Ref polylogue-j8yo
|
Warning Review limit reached
Next review available in: 36 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (13)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
o4j2's runSettings-storage scope predates this bead (PR #3390); the remaining pendingInputs gap is fixed on #3415. AC2 (query-DSL numeric predicates over run_settings) is genuinely deferred -- filed as polylogue-mgf6 with the two concrete blockers (grammar INT-only literals, SQL-builder plain-column assumption). Ref polylogue-o4j2 Ref polylogue-mgf6
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 81802b45c2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ParsedSessionEvent( | ||
| event_type="draft_input", | ||
| timestamp=timestamp, | ||
| payload=payload, |
There was a problem hiding this comment.
Exclude mutable drafts from append-only revision comparison
When a Drive document is retained twice and the operator edits or submits a nonblank draft, this emits only the current draft as an ordinary session event. session_revision_projection() folds event content into its identity, while _relation() requires the message and event axes to grow in the same direction; editing creates disjoint event identities, and submitting makes messages grow while the draft event disappears. classify_membership_revisions() therefore quarantines both direct-export raws with no accepted head, so a normal draft edit can make the session vanish on replay. Treat draft_input as mutable evidence excluded from append-only membership comparison, or give it revision-aware lifecycle semantics.
AGENTS.md reference: AGENTS.md:L112-L120
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 8c3eb9a: pendingInputs is no longer a session_event. It is now ParsedSession.pending_drafts, a plain session-row field (sessions.pending_drafts_json, index v47) stored outside every identity/hash computation in pipeline/ids.py — mirroring the existing run_settings precedent. Added a regression test for the exact retain -> edit draft -> retain -> submit sequence you traced (test_pending_draft_mutation_does_not_break_revision_containment), asserting _relation() and classify_membership_revisions() both stay correct at every step.
…nt axis Problem: a reviewer traced a P1 on the draft_input session_event this PR originally added -- drafts are MUTABLE (the operator edits the textbox, then eventually submits it), but session_events feed session_revision_projection's message/attachment/event comparison axes (polylogue-aggz Invariant 1), which assume every axis only ever grows between two acquisitions of the same session. Editing a draft created disjoint event identities (comparing as a fork); submitting made the event axis shrink while the message axis grew, and _relation() requires every non-equal axis to agree on direction -- both cases misclassified revision membership. This is the same defect class as polylogue-bu1i (acquisition state folded into identity) and polylogue-nuec (provider-remeasurement folded into identity), on a third axis: mutable session state. What changed: pendingInputs no longer becomes a ParsedSessionEvent. It is now ParsedSession.pending_drafts, a plain session-row field mirroring the existing run_settings precedent (stored verbatim, outside every hash/ identity computation in pipeline/ids.py). Plumbed end-to-end: index.db schema v47 (sessions.pending_drafts_json, additive JSON-array column, SEMANTIC_REPARSE per storage/sqlite/lifecycle.py since values depend on parser semantics), the real writer (archive_tiers/write.py, plain overwrite rather than run_settings' COALESCE -- a draft must actually clear when submitted, not persist stale), and the read mapper (mappers_archive.py + a new _json_object_list helper for the JSON-array shape run_settings' _json_object doesn't fit). Regression test added for the exact sequence the reviewer specified (tests/unit/sources/test_parsers_drive.py:: test_pending_draft_mutation_does_not_break_revision_containment): retain with a draft, retain again after editing it, retain again after submitting it -- asserts _relation() and classify_membership_revisions both stay correct across all three states. A storage-level round trip (tests/unit/storage/test_unread_wire_batch_v46.py) proves the new column survives the real writer/repository path, not just the parser. Verification: devtools test tests/unit/sources/test_parsers_drive.py tests/unit/storage/test_unread_wire_batch_v46.py tests/unit/archive/test_session_revision_membership.py -- 81 passed. devtools test tests/unit/storage tests/unit/pipeline tests/unit/sources -- 4624 passed, 1 failed (test_live_watcher.py:: test_end_to_end_hidden_root_file_creation_triggers_ingest, an async-timing watcher test unrelated to this change -- passes in isolation, confirmed pre-existing/flaky). mypy --strict polylogue -- clean. ruff format/check -- clean. devtools render all --check -- clean. devtools lab policy schema-versioning -- clean (v47 delta declared). Ref polylogue-o4j2
Follow-up to the previous commit: the shared chunks_before_submit list literal needed an explicit JSONValue annotation (list invariance means mypy's inferred list[dict[str, str]] doesn't structurally satisfy list[JSONValue] once reused across two payload dict literals). Verification: mypy --strict tests/unit/sources/test_parsers_drive.py -- clean. devtools test tests/unit/sources/test_parsers_drive.py -- 32 passed. devtools verify --quick -- 19/19 steps ok. Ref polylogue-o4j2
Summary
Parses AI Studio's
chunkedPrompt.pendingInputsblock intoParsedSession.pending_drafts— the operator's not-yet-submitted textboxcontent, which was previously discarded at parse. Also documents that this
bead's larger claim (runSettings entirely dropped) was already fixed by
prior work, and files a separate investigation bead for the related
browser-capture question.
Update: a reviewer found a P1 in the original design (drafts modeled as
a
draft_inputsession_event) and it has been fixed on this branch — see"P1 fix" below.
Problem
Ref polylogue-o4j2: a wire-evidence survey found the AI Studio Drive-synced
JSON's
runSettingsblock (temperature/topP/topK/maxOutputTokens/thinkingLevel/safetySettings/enable* flags) and
chunkedPrompt.pendingInputsboth unread at parse.
Re-verifying against current
master:runSettingsis already parsedand stored (
ParsedSession.run_settings→sessions.run_settings_json),shipped by PR #3390 (polylogue-2qx.4/cgfy, index v46) before this bead was
filed — so most of o4j2's scope was already closed without the bead knowing
it.
pendingInputswas genuinely still unread, confirmed against the livearchive: of 397
aistudio-driveraw sessions, 396 carry apendingInputsentry and 7 have non-blank draft text (one a full multi-paragraph prompt
that was never submitted) — content that is unrecoverable once Drive syncs
over it.
Solution
polylogue/sources/parsers/drive.py:parse_chunked_promptnow extractsnon-blank
pendingInputsentries (text/role/token_count) ontoParsedSession.pending_drafts. Blank-textbox entries (the ~99% commoncase) are skipped as non-evidence.
AC status (polylogue-o4j2):
predates this bead).
query DSL's numeric-field grammar only accepts integer literals
(
COUNT_FIELD COMP_OP INT) andNumericQueryFieldInfo.unit_columnsassumes a plain SQL column, not a JSON-extract expression — exposing
float-typed run_settings fields needs grammar + SQL-builder changes
out of this PR's scope. Filed as polylogue-mgf6.
runSettings (index v46); pendingInputs lands via the new index v47
SEMANTIC_REPARSE delta declared in this PR.
polylogue/sources/parsers/andpolylogue/sources/providers/forgenerationConfig/sampling_params/temperature/inference_config/model_settings; onlydrive.pymatches. No equivalent block foundelsewhere.
Also filed polylogue-j8yo: a live-browser (CDP Network) investigation into
whether an aistudio.google.com browser-capture adapter would be cheap,
given the hypothesis that the live app fetches the same JSON Drive sync
gets. Verdict: SKIP — the live app's data comes from an internal RPC
service (
google.internal.alkali.applications.makersuite.v1.MakerSuiteService)returning
application/json+protobuf(Google's undocumented positional-arrayRPC framing, not the clean object JSON polylogue parses), and the two
capabilities a live adapter was hoped to add — drafts and generation params
— are both already present in the Drive-synced file (the latter already
landed, the former lands in this PR).
P1 fix: drafts moved off the identity-bearing event axis
A reviewer traced a real defect in the original design (drafts as a
draft_inputParsedSessionEvent): drafts are mutable (the operatoredits the textbox, then eventually submits it), but
session_eventsfeedsession_revision_projection's message/attachment/event comparison axes(polylogue-aggz Invariant 1), which assume every axis only ever grows
between two acquisitions of the same session.
of the same conversation — comparing as a conflict/fork instead of the
same document.
pendingInputsentirely whilethe message axis grows —
_relation()requires every non-equal axis toagree on growth direction, so this also misclassified as a conflict.
This is the same defect class as polylogue-bu1i (acquisition state folded
into identity) and polylogue-nuec (provider-remeasurement folded into
identity), on a third axis: mutable session state.
Fix:
pendingInputsis no longer a session_event. It is nowParsedSession.pending_drafts, a plain session-row field mirroring theexisting
run_settingsprecedent — stored verbatim, outside every hash/identity computation in
pipeline/ids.py. Plumbed end-to-end:polylogue/storage/sqlite/archive_tiers/index.py: index schema v47,sessions.pending_drafts_json(additive JSON-array column).polylogue/storage/sqlite/lifecycle.py: v47 delta declaredSEMANTIC_REPARSE(values depend on parser semantics, same as v42/44/45/46).polylogue/storage/sqlite/archive_tiers/write.py: the real writer, plainoverwrite (not
run_settings'COALESCE) — a draft must actually clearwhen submitted, not persist stale.
polylogue/storage/sqlite/queries/mappers_archive.py+mappers_support.py: read mapper, new_json_object_listhelper for theJSON-array shape
run_settings'_json_objectdoesn't fit.polylogue/storage/runtime/archive/records.py:SessionRecord.pending_drafts.Regression test for the exact sequence the reviewer specified
(
tests/unit/sources/test_parsers_drive.py:: test_pending_draft_mutation_does_not_break_revision_containment): retainwith a draft, retain again after editing it, retain again after submitting
it — asserts
_relation()andclassify_membership_revisionsboth staycorrect across all three states. A storage-level round trip
(
tests/unit/storage/test_unread_wire_batch_v46.py) proves the new columnsurvives the real writer/repository path, not just the parser.
Verification
devtools test tests/unit/sources/test_parsers_drive.py tests/unit/storage/test_unread_wire_batch_v46.py tests/unit/archive/test_session_revision_membership.py— 81 passed.devtools test tests/unit/storage tests/unit/pipeline tests/unit/sources— 4624 passed, 1 failed
(
test_live_watcher.py::test_end_to_end_hidden_root_file_creation_triggers_ingest,an async-timing filesystem-watcher test unrelated to this change — passes
in isolation, confirmed pre-existing/flaky).
mypy --strict polylogueand the changed test files — clean.ruff format/check— clean.devtools render all --check— clean.devtools lab policy schema-versioning— clean (v47 delta declared).devtools verify --quick— 19/19 steps ok (twice, after the mypy fixup).Not run:
devtools verifyfull/testmon pass (not seeded in this worktree)and the heavy
testCI suite (per-PR CI skips it by repo convention; willrun post-merge on master).
Ref polylogue-o4j2
Ref polylogue-j8yo
Ref polylogue-mgf6