Skip to content

feat(sources): capture aistudio-drive draft input evidence - #3415

Merged
Sinity merged 5 commits into
masterfrom
feature/sources/aistudio-drive-pending-inputs
Jul 31, 2026
Merged

feat(sources): capture aistudio-drive draft input evidence#3415
Sinity merged 5 commits into
masterfrom
feature/sources/aistudio-drive-pending-inputs

Conversation

@Sinity

@Sinity Sinity commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Summary

Parses AI Studio's chunkedPrompt.pendingInputs block into
ParsedSession.pending_drafts — the operator's not-yet-submitted textbox
content, 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_input session_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 runSettings block (temperature/topP/topK/maxOutputTokens/
thinkingLevel/safetySettings/enable* flags) and chunkedPrompt.pendingInputs
both unread at parse.

Re-verifying against current master: runSettings is already parsed
and stored (ParsedSession.run_settingssessions.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. pendingInputs was genuinely still unread, confirmed against the live
archive: of 397 aistudio-drive raw sessions, 396 carry a pendingInputs
entry 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_prompt now extracts
non-blank pendingInputs entries (text/role/token_count) onto
ParsedSession.pending_drafts. Blank-textbox entries (the ~99% common
case) are skipped as non-evidence.

AC status (polylogue-o4j2):

  1. runSettings parsed into typed evidence — already satisfied (PR feat(archive): index v46 wire-evidence batch, free-threaded-only runtime, parse-failure recovery #3390,
    predates this bead).
  2. Queryable ("sessions where temperature > X") — not satisfied. The
    query DSL's numeric-field grammar only accepts integer literals
    (COUNT_FIELD COMP_OP INT) and NumericQueryFieldInfo.unit_columns
    assumes 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.
  3. Existing sessions acquire it via reprocess — already satisfied for
    runSettings (index v46); pendingInputs lands via the new index v47
    SEMANTIC_REPARSE delta declared in this PR.
  4. Other origins checked for an equivalent settings block — grepped
    polylogue/sources/parsers/ and polylogue/sources/providers/ for
    generationConfig/sampling_params/temperature/inference_config/
    model_settings; only drive.py matches. No equivalent block found
    elsewhere.

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-array
RPC 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_input ParsedSessionEvent): 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 between two retentions
    of the same conversation — comparing as a conflict/fork instead of the
    same document.
  • Submitting a draft makes it disappear from pendingInputs entirely while
    the message axis grows — _relation() requires every non-equal axis to
    agree 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: pendingInputs is no longer a session_event. 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:

  • 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 declared
    SEMANTIC_REPARSE (values depend on parser semantics, same as v42/44/45/46).
  • polylogue/storage/sqlite/archive_tiers/write.py: the real writer, plain
    overwrite (not run_settings' COALESCE) — a draft must actually clear
    when submitted, not persist stale.
  • polylogue/storage/sqlite/queries/mappers_archive.py +
    mappers_support.py: read mapper, new _json_object_list helper for the
    JSON-array shape run_settings' _json_object doesn'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): 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 filesystem-watcher test unrelated to this change — passes
    in isolation, confirmed pre-existing/flaky).
  • mypy --strict polylogue and 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 verify full/testmon pass (not seeded in this worktree)
and the heavy test CI suite (per-PR CI skips it by repo convention; will
run post-merge on master).

Ref polylogue-o4j2
Ref polylogue-j8yo
Ref polylogue-mgf6

Sinity added 2 commits July 31, 2026 05:54
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
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@Sinity, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 36 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 750871a9-837a-4715-b23a-e0a960f5fc37

📥 Commits

Reviewing files that changed from the base of the PR and between 1140338 and f92b6be.

📒 Files selected for processing (13)
  • .beads/issues.jsonl
  • polylogue/sources/origin_specs.py
  • polylogue/sources/parsers/base_models.py
  • polylogue/sources/parsers/drive.py
  • polylogue/storage/runtime/archive/records.py
  • polylogue/storage/sqlite/archive_tiers/index.py
  • polylogue/storage/sqlite/archive_tiers/write.py
  • polylogue/storage/sqlite/lifecycle.py
  • polylogue/storage/sqlite/queries/mappers_archive.py
  • polylogue/storage/sqlite/queries/mappers_support.py
  • polylogue/storage/sqlite/queries/sessions_reads.py
  • tests/unit/sources/test_parsers_drive.py
  • tests/unit/storage/test_unread_wire_batch_v46.py

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 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".

Comment thread polylogue/sources/parsers/drive.py Outdated
Comment on lines +244 to +247
ParsedSessionEvent(
event_type="draft_input",
timestamp=timestamp,
payload=payload,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge 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 👍 / 👎.

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.

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.

Sinity added 2 commits July 31, 2026 06:37
…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
@Sinity
Sinity merged commit 7373fc2 into master Jul 31, 2026
3 checks passed
@Sinity
Sinity deleted the feature/sources/aistudio-drive-pending-inputs branch July 31, 2026 04:47
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