feat(provider): gemini-cli adapter — third AgentProvider, spec pinned to 0.42.0 (grilled 2026-07-11) #126
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/coding-lab#126
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Third
AgentProvider: Gemini CLI, at the Codex bar (ADR-0036 DoD — Tier-1 conformance green + four Tier-2 live spikes evidenced by a committedinternal/compat/gemini/compat.md). Spec fully grilled 2026-07-11 with the maintainer; decisions below are settled — don't re-litigate.Pinned decisions (grill 2026-07-11)
/nix/store/7aw1jsvlxmj5p6dv3yr5sz3sd868piny-gemini-cli-0.42.0/bin/gemini). compat.md pins to 0.42.0. Host wiring stays generic (-provider-bin gemini=…/LAB_PROVIDER_BIN_GEMINI, ADR-0034); the deploy module needs itsagentPackages.geminiparameter wired so the bin lands on PATH (ADR-0033 baseline).~/.gemini/oauth_creds.json(codex auth.json precedent).AuthFlowkindoauth-code:NO_BROWSER=trueprints an auth URL and takes a pasted authorization code on stdin →LoginStartscrapes the URL from thelab-login-geminisession,LoginSubmitCodedelivers the code (unlike codex, this provider implements it). Free tier accepted for v1 (~60 req/min, 1000/day, shared across runs); quota-exhaustion behavior must be recorded, not worked around.gemini --approval-mode yolo --skip-trust --session-id <run-uuid> [-m <model>] [prompt]. Never-ask posture (codex ADR-0037 parity);--skip-trust= per-session trust, zero global mutation;--session-idis lab-generated per run → deterministic transcript identity. InitialPrompt rides the trailing positional (0.42.0 help: positionalquerystarts interactive mode).~/.gemini/tmp/<project-id>/chats/session-<sessionId>.jsonl. Verified in the 0.42.0 bundle: JSONL, born eagerly atChatRecordingService.initialize()(metadata record before any turn — no codex-style lazy-birth deadlock, #96 class). Grammar: metadata,user,gemini(thoughts/tokens/model/embeddedtoolCalls),$set,$rewindTo; updates re-append with the same messageid→ fold is last-writer-wins by id, not pure append of new items.LocateTranscriptresolves via the spawned session-id (filename issession-<sessionId>.jsonl), no cwd-match heuristic.GEMINI_TELEMETRY_ENABLED/TARGET/OUTFILE/LOG_PROMPTS/…env vars and emitsgemini_cli.user_prompt,gemini_cli.tool_call, conversation-finished events. Plan: per-run outfile in the runtime dir;ReadChatfolds transcript for content + telemetry events for working/ended edges (claude's #62 hook-edge pattern with a native feed).SeedMeta.ContextFileName = "GEMINI.local.md"(ADR-0035.localrule), wired by a marker-guarded merge into user-level~/.gemini/settings.jsonsetting the context filename list to["GEMINI.local.md", "GEMINI.md", "AGENTS.md"]— lab context always read, repo-tracked files keep working, no per-worktree tracked-file collision. (Codex global-config-append precedent.)NativeSkillDiscovery: true,SkillsDir: ".gemini/skills"— 0.42.0 has native Agent-Skills support (gemini skillssubcommand). First non-claude provider with native discovery; no generated index section.-m.Efforts()= single sentinel (default) mapped to no flag (conformance requires non-empty catalogs; gemini has no effort knob).git commititself). Patterns in the BRE∩RE2 dialect + fixture samples, codex §9 precedent.general.sessionRetention.enabledgatescleanupExpiredSessions; disabled by default) — durable-transcript requirement satisfied; the settings merge must never enable it. Record in compat.md.Tier-2 spike briefs (answer live on 0.42.0, post as an amendment comment BEFORE adapter code — codex #87 Amendment-2 pattern)
--session-idflows into the chats/ filename (+safeSessionIdsanitization);<project-id>dirname scheme for a given worktree; eager birth confirmed live;/clear— does it rotate the file or$rewindTo? epoch/identity obligation on context-clear;/resume/session-browser hazards; interactive vs-precording parity.ENABLED+TARGET+OUTFILE;USE_COLLECTORoff) with no GCP export; event set actually written in 0.42.0; does a turn-end event fire reliably after every turn (incl. tool-heavy turns)? prompts not logged (LOG_PROMPTS=false); file growth/rotation behavior.--approval-mode yolo --skip-trustwith seeded auth (expected: none →AnswerDialog=ErrDialogNotAnswerable).context.fileNamevs legacy flatcontextFileName); list semantics (all matching files loaded vs first match); interplay with global~/.gemini/GEMINI.md;.gitmemory boundary;/memory showas the verification probe.NO_BROWSER=truepaste-code flow live (scrape regexes for URL + code prompt, completion detection);AuthStatussurface — 0.42.0 has no auth/login subcommand, so status likely = creds-file presence/expiry (oauth_creds.json) +google_accounts.jsonemail, force-refresh via a cheap probe;Logout= creds-file removal (codex escalation precedent); creds-refresh behavior when the seeded token expires./modelinventory under the free-tier account; one real turn per candidate; the quota-exhaustion failure mode (429 vs silent fallback model) and what it looks like in transcript + telemetry (State must stay honest).scrub-markerssubtest.Implementation checklist (docs/agents/provider-authoring.md, ADR-0036)
internal/provider/gemini/mirroring codex file-for-file → SeedMeta (context file, skills dir, excludes, seeded-path patterns, scrub patterns) → generic host config only (no provider-named config fields) → registration row ininternal/provider/conformance_test.go+provider.NewRegistry(...)incmd/lab/main.go→ spikes →internal/compat/gemini/compat.md→ neutrality tests stay green.Open implementation point (recommendation, implementing agent verifies)
Per-run telemetry env needs spawn-time env plumbing: 0.42.0 has no
--telemetry*CLI flags (bundle-verified), only env vars/settings.LiveSignals.Setup(runID, dir)already hands the provider per-run runtime wiring but returns extra args, and core'sspawnEnv(internal/instance/start.go) is core-owned. Recommended: extend the LiveSignals capability so Setup can also contribute env (claude returns none), or set the vars via tmux session environment before spawn. Coordinate with #124, which touches the same spawnEnv seam.DoD
providertest.Conformancegreen in CI for the gemini adapter.internal/compat/gemini/compat.md(provenance-tagged like codex).Amendment 1 — Tier-2 spike sweep (2026-07-11, gemini-cli 0.42.0)
Environment: nix-store
gemini-cli 0.42.0on the lab host (/nix/store/7aw1jsvlxmj5p6dv3yr5sz3sd868piny-gemini-cli-0.42.0/bin/gemini,--version→0.42.0).Provenance legend:
live= driven against the real binary in tmux on the lab host today;bundle= extracted from the shipped 0.42.0 JS bundle (esbuild output retains identifiers; every claim traced to its source chunk);cli=--help/subcommand extraction. Creds caveat:~/.gemini/oauth_creds.jsonis NOT seeded on the host (decision 3 expected a maintainer copy; codex'sauth.jsonis present, gemini's creds are not — maintainer push-notified 2026-07-11). Everything that needs an authenticated turn is answered from the bundle and flagged residual-live below; a second amendment upgrades provenance when creds land.S1 transcript
~/.gemini/tmp/<project-id>/chats/session-<yyyy-MM-ddThh-mm>-<sid8>.jsonlwhere<sid8>= first 8 chars ofsanitizeFilenamePart(sessionId)([^a-zA-Z0-9_-]→_). live + bundle (ChatRecordingService, SESSION_FILE_PREFIX).<project-id>is NOT a hash: slugifiedbasename(resolve(cwd))(lowercase, non-alnum →-), deduped-1,-2,…via a registry in~/.gemini/projects.json+ a.project_rootownership marker inside the project tmp dir. TheprojectHashfield inside the metadata record issha256(cwd)— legacy-migration only, never the dirname. live + bundle.{sessionId, projectHash, startTime, lastUpdated, kind:"main"}) was on disk before any turn and before auth completed (TUI path). Headless-precords identically (Config.initialize()→ recording init is unconditional; only ENOSPC disables it). bundle. One nuance (live): when auth runs on the pre-TUI stdin path (selectedType set, creds absent), the file is born only when the TUI starts after auth.--session-idcollision: relaunching with an already-used id hard-errors, exit 42 (FATAL_INPUT_ERROR):Error starting session: Session ID "…" already exists. Use --resume to resume it, or provide a different ID.live. Accepted charset^[a-zA-Z0-9-_]+$(yargs coerce; not UUID-validated). bundle. ⇒ lab mints a fresh id per spawn./clear(alias/new): rotates — CLI generates a freshrandomUUID(),resetChat()starts a new file; the old file is left untouched (no$rewindTo, no truncation). bundle (clearCommand → resetNewSessionState). ⇒ epoch obligation: after/clearthe lab-minted session id no longer names the live file;LocateTranscriptresolves the project dir viaprojects.json(worktree path → slug) and prefers the exact<sid8>filename match, falling back to the newestsession-*.jsonlin that dir (each run has its own worktree ⇒ its own project dir, so newest-in-dir is the run's current epoch)./resume/session-browser: resumes into the OLD file; the in-memory session id reverts to the old id and a$setis appended. bundle. Hazard recorded (chat-unsafe command, curated out).{id, timestamp, type: user|gemini|info|warning|error, content, displayContent?, thoughts?, tokens?, model?, toolCalls?};{$set:{…partial metadata}};{$rewindTo: messageId}. Streaming/tool-status updates re-append a full record with the SAMEid⇒ fold is last-writer-wins by id;$rewindTodrops the matched id and everything after (the CLI's own loader does exactly this). bundle.S2 telemetry edges
argv ?? env ?? settingsprecedence):GEMINI_TELEMETRY_ENABLED,_TRACES_ENABLED,_TARGET(local|gcp, else FatalConfigError),_OTLP_ENDPOINT,_OTLP_PROTOCOL,_OUTFILE,_LOG_PROMPTS,_USE_COLLECTOR,_USE_CLI_AUTH. bundle.ENABLED=true+TARGET=local+OUTFILE=<path>(+LOG_PROMPTS=false). OUTFILE forces File{Span,Log,Metric}Exporters;USE_COLLECTORis irrelevant attarget=local; zero OTEL network. BUT Clearcut usage-stats is a separate pipeline, ON by default (privacy.usageStatisticsEnableddefaulttrue, no env/flag) — the lab settings merge sets itfalsefor true zero-export. bundle.JSON.stringify(x, null, 2)+\n, appended (flags:"a", no rotation/truncate ever) ⇒ per-run outfile under the runtime dir, folded with a brace-balanced reader, GC'd bySweepSpools. bundle.gemini_cli.user_prompt(on submit, before send),tool_call(per completed call),api_request/api_response(per model round-trip — several per tool-heavy turn),flash_fallback(silent quota fallback),next_speaker_check(disabled by default:model.skipNextSpeakerCheckdefaultstrue),conversation_finished— single call site, fires only in the interactive TUI under--approval-mode yoloon the responding→idle transition with turnCount>0. That is exactly lab's spawn posture ⇒ working edge =user_prompt, idle edge =conversation_finished; no scraping, native feed (#62 hard-evidence rule satisfied). bundle. residual-live: confirmconversation_finishedlands in the outfile after every turn incl. tool-heavy ones.LOG_PROMPTS=falsestrips prompt/response/function_args text from events. bundle.S3 reply / interrupt / dialogs
/rewind(writes$rewindTo— epoch hazard). Ctrl-C BANNED: first press cancels + warns, second press within the window runs/quitregardless of input-buffer state (kills the session — stricter hazard than codex). bundle + docs; Ctrl-C-at-login-prompt no-exit also observed live.--approval-mode yolo --skip-trust+ seeded auth + lab settings: trust dialog suppressed (--skip-trust⇒GEMINI_CLI_TRUST_WORKSPACE=trueenv, per-invocation, never persisted); tool/shell approvals suppressed (yolo policy); theme picker only on an invalidui.theme(absent key does NOT open it); auth dialog only whensecurity.auth.selectedTypeis absent (creds presence is irrelevant — the merge sets it); update nag default-off. Reachable residuals:ProQuotaDialogon daily quota exhaustion (see S6) and the auth dialog after a failed token refresh.AnswerDialogstaysErrDialogNotAnswerablein v1 (codex precedent). bundle; auth-choice dialog itself captured live pre-auth.S4 context-file discovery
context.fileName(string OR array). The v2 nested settings shape confirmed live — 0.42.0 itself wrote{"security":{"auth":{"selectedType":"oauth-personal"}}}on auth selection. Default context nameGEMINI.md.--- Context from: <path> ---blocks) — not first-match-wins. ⇒["GEMINI.local.md","GEMINI.md","AGENTS.md"]gives lab context always + repo-tracked files keep working. bundle.~/.gemini/<configured names>(the configured list applies there too — no hardcoded GEMINI.md fallback); upward walk stops one directory ABOVE the.gitproject root (quirk recorded — it readsdirname(projectRoot)too); downward BFS capped bycontext.discoveryMaxDirs(default 200). Workspace.gemini/settings.jsonoverrides user settings when trusted (and--skip-trustmakes it trusted) ⇒ a repo-committed settings file could overridecontext.fileName— recorded as a hazard, accepted for v1. Unknown/invalid settings keys are warn-only, never fatal ⇒ the user-level merge is safe. bundle.~/.gemini/settings.jsonsettingcontext.fileName=["GEMINI.local.md","GEMINI.md","AGENTS.md"],security.auth.selectedType="oauth-personal"(auth dialog gates on this setting),privacy.usageStatisticsEnabled=false(Clearcut kill, S2). It must NEVER touchgeneral.sessionRetention(cleanup stays opt-in-disabled ⇒ durable transcripts, decision 11). residual-live:/memory showprobe under a real turn.S5 auth
NO_BROWSER=<truthy>env; also auto-suppressed when linux has noDISPLAY/WAYLAND_DISPLAY/MIR_SOCKET, orCI=true, orDEBIAN_FRONTEND=noninteractive, or ssh on non-linux. (Lab sandbox has no DISPLAY ⇒ suppressed regardless; the adapter still setsNO_BROWSER=truefor determinism.) bundle.selectedTypeset and no creds, a pre-TUI plain-stdio flow printsPlease visit the following URL to authorize the application:+ the Google URL (PKCE S256,state, redirecthttps://codeassist.google.com/authcode— the code is shown to the user on a Google page, no localhost listener), then promptsEnter the authorization code:(readline on stdin ⇒ tmux paste + Enter works). Invalid code (live):Failed to authenticate with authorization code:invalid_grant+Failed to authenticate with user code. Retrying...+ a fresh URL (new PKCE challenge/state) ⇒ the scraper must always take the LAST URL in the pane. Two retries, thenFatalAuthenticationError. No success banner in this path ⇒ completion detection =oauth_creds.jsonbirth (the completion poller watches the file) and/or the TUI rendering. Ctrl-C at the code prompt does not exit (live) ⇒ teardown = kill the login session.LoginStartdesign: ensure the user-settings merge (soselectedTypeforces the pre-TUI path), spawn baregeminiwithNO_BROWSER=truein thelab-login-geminisession, scrape the URL;LoginSubmitCodepastes the code (gemini, unlike codex, implements it — flow kindoauth-code); completion poller = creds-file watch.~/.gemini/oauth_creds.json(google-auth-library Credentials:access_token,refresh_token,scope,token_type,expiry_datems-epoch, optionalid_token; 0600; rewritten on every silent refresh) and~/.gemini/google_accounts.json({active: <email>, old: []}). Silent refresh: expired access token + valid refresh token refreshes with no interaction; a FAILED refresh only debug-logs and falls through to the login flow (TUI: auth dialog / "(OAuth expired)"). bundle.AuthStatus= creds-file presence +expiry_date/refresh_tokeninspection + email fromgoogle_accounts.json(0.42.0 has no auth/login/logout CLI subcommand — confirmed, top-level commands are onlymcp|extensions|skills|hooks|gemma).Logout= removeoauth_creds.json+ clear the active account (the/auth signoutslash command does exactly this internally; file removal is the codex rm-escalation precedent). bundle + cli. residual-live: completed code-paste round-trip and refresh-after-expiry (needs the maintainer's Google account once).S6 models + quota
-m⇒auto-gemini-3("Auto (Gemini 3)"), resolved at request time togemini-3-pro-preview/gemini-3-flash-preview, auto-downgrading toauto-gemini-2.5when the account lacks preview access./modellist is hardcoded constants gated by server-fetched entitlements:gemini-2.5-pro,gemini-2.5-flash,gemini-2.5-flash-lite,gemini-3-pro-preview,gemini-3.1-pro-preview,gemini-3.1-flash-lite-preview,gemini-3-flash-preview(+ experimental gemma). bundle./model+one-turn sweep when creds land): Default = omit-m(labelled "Auto"); explicitgemini-3-pro-preview,gemini-3-flash-preview,gemini-2.5-pro,gemini-2.5-flash.Efforts()= singledefaultsentinel → no flag (gemini has no effort knob; conformance needs a non-empty catalog).activateFallbackMode) — observable as thegemini_cli.flash_fallbacktelemetry event + themodelfield on subsequent transcript records; hard daily exhaustion (TerminalQuotaError, the free-tier ceiling) ⇒ blockingProQuotaDialog: "Usage limit reached for . Access resets atS7 attribution ground truth
Co-authored-by/Generated withstrings anywhere, nogit committemplating of any kind (the CLI has no built-in commit tool; commits only happen if the model runs git in the shell tool). bundle. ⇒ScrubPatternsship defensive-only (BRE∩RE2: co-authored-by-Gemini + Generated-with-Gemini shapes) with synthetic fixture samples, codex §9 precedent. residual-live (incogni): capture commits/PR bodies from a real gemini run before flipping the samples to captured ground truth.Cross-cutting implementation pins
gemini --approval-mode yolo --skip-trust --session-id <lab-uuid> [-m <model>] [prompt]. Positional query starts INTERACTIVE mode with the prompt as first turn when stdin is a TTY and not CI (isHeadlessModestage-1/2 logic; tmux = TTY; lab must never setCIin spawn env) — 0.42.0 prints a startup notice about this default. cli + bundle.--approval-modechoices aredefault|auto_edit|yolo|plan.Setupcan contribute spawn env (claude returns nil) — landed as commit20f1c53onafk/126(coordinates with #124's SetupOpts; settings-file arming unchanged).general.sessionRetentionabsent ⇒cleanupExpiredSessionsreturns disabled — opt-in confirmed at the schema level (default: void 0); the lab merge never sets it. bundle.gemini skills list|enable|disable|install|link|uninstall(cli); discovery at workspace.gemini/skills(trusted ⇒--skip-trustsuffices) + user~/.gemini/skills(+.agents/skillsvariants);skills.enableddefaults true. bundle. ⇒NativeSkillDiscovery: true,SkillsDir: ".gemini/skills".Creds-gated residual list (second amendment when
~/.gemini/oauth_creds.jsonis seeded)geminirecords incl. toolCalls/thoughts) andconversation_finished-in-outfile confirmation (S1/S2)./memory showcontext-discovery probe (S4)./modelinventory + one turn per candidate + real quota-exhaustion shape (S6).codex debug models+ per-model efforts seam #156/land-pr audit — PR #129: FAIL (blocking correctness bug)
Verification signal: Forgejo Actions CI. On the pre-merge head both gates were green (
ci / native,ci-nix / flake-check— the nix gate did exercise this diff, which matters since it touchesagentPackages). After the conflict resolution below I re-ran the project's own gates locally on the resolved tree:go build ./...✅, fullgo test ./...✅,golangci-lint 2.12.20 issues ✅. PR touches noweb/files, so the vitest surface is untouched.Merge conflict — resolved and pushed to
afk/126The branch was behind
main(#161/#158/#155/#153 landed since). Two resolutions, both pushed:cmd/lab/main.go— import union (8609bfe).mainaddedinternal/pull, the PR addedinternal/provider/gemini, adjacent lines. Deterministic, behaviour-preserving.internal/provider/gemini— semantic break git merged textually (45e3e48). #158 changedAgentProvider.Models()to return[]provider.ModelOption(per-model efforts + reported default). The gemini adapter still implemented the pre-#156[]provider.Optionsignature, so the merged tree did not compile. Ported to claude-code's shape: gemini has no reasoning-effort knob at all (singledefaultsentinel, no flag emitted inSpawnArgv), so every model carries the same shared efforts list and noDefaultEffort. Values, order and the spawn default unchanged; the compat §10 pin restated in the enriched shape.🔴 Blocker — deterministic
--session-idcollides on every re-run of an issueinternal/provider/gemini/gemini.go:129derives the session id asUUIDv5(ns, tmuxSessionName), and bothSpawnArgvandLocateTranscriptrecompute it. ADR-0041 justifies this ("Considered options" → Persisting a per-run random--session-id) with "the collision error is avoided because tmux session names are unique per run". That premise is false:afk.Label(n, auto)=afk-<N>(internal/afk/decide.go:179) → session<repo>~afk-<N>(internal/afk/launch.go:223), worktree<repo>-<N>(internal/instance/instance.go:212). Both are pure functions of the issue number and repeat verbatim on every re-claim/re-run of the same issue.gitx.UniqueManualLabelde-dupes only against live tmux sessions (internal/instance/start.go:80-83), so a stop→start of the same label reproduces the name.Gemini 0.42.0 hard-errors
exit 42 FATAL_INPUT_ERRORwhen the id already exists — and the store it checks is~/.gemini/tmp/<project-slug>/chats/(compat.md §185), keyed by the worktree path through~/.gemini/projects.json. That lives in HOME, not in the worktree, so it survives worktree deletion, and session files are never cleaned (decision 11 deliberately leavessessionRetentionalone).Consequence: the second AFK run on any given issue spawns gemini → exit 42 → tmux session dies instantly → dead run, no transcript. Re-running a failed issue is the normal path, so this fires in ordinary use. The PR's own
TestCompat_Live_eagerBirthAndSessionIDCollisionpins exactly this behaviour. Secondary symptom from the same root cause:LocateTranscript(chat.go:68-73) prefers the exact<sid8>match, so a re-run in a reused worktree would bind to the previous run's transcript.Fix direction: mint a per-run unique id (persist it on the run row, or fold the run id / a nonce into the UUIDv5 input) rather than deriving it from the session name alone.
🟠 Serious — telemetry liveness edges arrive up to ~5 s late
Gemini builds
BatchLogRecordProcessorunconfigured, so OTEL's default 5 sscheduledDelayMillisapplies. The transcript'suserrecord is written synchronously at submit, butgemini_cli.user_promptonly reaches the outfile at the next flush. In that windowlivenessState(livesignals.go:169) still sees the previousconversation_finishedand reportsStateNeedsInputwhile the agent is working — andinternal/chat/notify.go:35debounces for only 2 s, so a seeded run can fire a spurious "needs you" push seconds after spawn. Cheap fix: setOTEL_BLRP_SCHEDULE_DELAY/OTEL_BSP_SCHEDULE_DELAYinSetup's env (the bundled SDK honours both). At minimum, compat.md §telemetry should pin the batch delay — it currently reads as if the edges are immediate.🟡 Note — decision 7 divergence
Decision 7 asked for a marker-guarded merge into
~/.gemini/settings.json;seed.go:72 EnsureUserSettingsimplements a semantic-guard merge (re-enforcescontext.fileNameto lab's list on every run). Defensible for JSON and documented in ADR-0041, but it silently overwrites an operator's customcontext.fileName— worth a maintainer nod.Verified clean (explicitly refuted as findings)
LiveSignals.Setup's new env return leaves claude-code byte-identical (nil env, arming payload untouched, sole call site updated, no slice aliasing); the transcript fold (last-writer-wins by id +$rewindTo, subagent-file filtering, reverse-time sort) matches the CLI's own loader; the telemetry byte-scan can't be poisoned (no other 0.42.0 record carries the event literals,LOG_PROMPTS=false, Clearcut killed); no nil derefs, goroutine leaks, or resource leaks.Verdict: FAIL on the session-id collision. The adapter otherwise genuinely mirrors codex file-for-file and every fragile 0.42.0 coupling I spot-checked against the bundle holds. Not merged.
Spec-version drift check, 2026-08-02 (Daily agent update check) — the 0.42.0 pin is 11 stable releases behind, and at least two pinned decisions look inverted
This issue is
ready-for-agentwith a spec pinned to gemini-cli 0.42.0 and an Amendment-1 spike sweep whose evidence is all 0.42.0. Upstream is now at 0.53.1. This comment is not a request to re-grill the whole spec — most of it still holds — but the drift is large enough that an agent starting implementation today would build against evidence that has demonstrably moved, so the deltas are recorded here before that happens.Everything below is a hypothesis derived from upstream release notes and the v0.53.1 docs tree. Nothing was driven against a live binary. Treat each item as "verify first", the same way #249 handles the codex bump.
Upstream releases since the pin
(no
v0.48.xwas ever cut)Which version to actually re-pin to — probably 0.47.0, not 0.53.1
Decision 2 pins a nix-store binary (
/nix/store/…-gemini-cli-0.42.0/bin/gemini) and the deploy path resolvesagentPackages.geminifrom nixpkgs (ADR-0033). nixpkgs does not have 0.53.1:So the realistic re-pin is 0.47.0 — that is what a lab host can actually get on PATH today — unless the spike is deliberately driven against a newer npm-installed CLI and the host wiring is solved separately. Decide this before spiking, because it decides which release notes below are in scope: 0.43.0–0.47.0 (nixpkgs-reachable) or all of them.
The two items that look like inversions of a pinned decision — check these first
1.
--session-idis not in the 0.53.1 CLI reference. Decisions 4 and 5 rest on it: the spawn argv isgemini --approval-mode yolo --skip-trust --session-id <run-uuid> …,--session-idis what gives lab "deterministic transcript identity", andLocateTranscript"resolves via the spawned session-id (filename issession-<sessionId>.jsonl)". Amendment 1 confirmed the flag live at 0.42.0, including its exit-42 collision behaviour. The v0.53.1 flag table (docs/cli/cli-reference.md:56-67) lists--skip-trust,--approval-mode,--yolo(deprecated),--resume/-r,--list-sessions,--delete-session— and no--session-id. Session management in the window was reworked repeatedly (v0.43.0"refactor(core): agent session protocol changes", "feat: export session to file and import via flag";v0.44.0"refactor: decouple stored session deletion from ChatRecordingService (#22920)";v0.45.0"filter internal session context from history during resumption";v0.47.0"avoid persisting empty resume sessions").The docs table may simply be incomplete — but if the flag is genuinely gone, decisions 4 and 5 have lost their mechanism and need re-grilling (a lab-minted id is what makes the transcript locatable without a cwd heuristic) before any adapter code is written. This is the first thing to check with
gemini --help.2. Session retention has flipped from opt-in to on-by-default. Decision 11 pins:
v0.53.1
docs/cli/settings.md:40-41:general.sessionRetention.enabledtruegeneral.sessionRetention.maxAge"30d"If that holds, transcripts older than 30 days are deleted by the CLI on its own schedule, and lab's durable-transcript requirement is no longer satisfied by default. The decision-7 settings merge inverts: it must now explicitly set
general.sessionRetention.enabled = falserather than merely never enabling it. Decision 11's text needs rewriting either way.Other drift against the pinned decisions
Decision 4 — spawn argv.
--approval-modeand--skip-trustboth survive at 0.53.1 (cli-reference.md:56-57), andyolois still a valid choice — but the choice set grew todefault | auto_edit | yolo | plan, andv0.44.0"feat(cli): merge Auto modes into a single Auto mode (#26714)" reworked the enum underneath.--yolo/-yis now marked deprecated in favour of--approval-mode=yolo, which is already the form the spec uses.v0.43.0"allow redirection in YOLO and AUTO_EDIT modes without sandboxing (#26542)" changes what yolo permits.Decision 7 — context files (
GEMINI.local.mdvia a merge into~/.gemini/settings.json). The v0.53.1 settings table'scontext.*block listsdiscoveryMaxDirs,loadMemoryFromIncludeDirectories, and thefileFiltering.*family — nocontext.fileNamerow. S4 pinned that key (nestedcontext.fileName, string or array, all matches concatenated). The table may be curated rather than exhaustive, but the key must be re-confirmed against the real settings schema before the merge is written; if it moved, decision 7's whole mechanism moves with it.Decision 8 — native skills (
.gemini/skills).v0.49.0"fix(cli): prevent path traversal vulnerabilities during skill install (#27767)" — a security fix in exactly the surface decision 8 turns on. Skills also churned across the window (v0.43.0"exclude extension context from skill extraction agent",v0.44.0"add agent-tui and tui-tester skills",v0.43.0"clarify Auto Memory proposes memory updates and skills"). Re-scrape the skills contract.Decision 6 / S2 — telemetry as the liveness substrate. This one survives. v0.53.1
docs/cli/telemetry.mdstill documents the same nineGEMINI_TELEMETRY_*env vars in the same shape, and both events the design depends on are still there:gemini_cli.user_prompt(line 325) andgemini_cli.conversation_finished(line 670). Two notes:logPromptsdefaults totrue, so the spawn env must setGEMINI_TELEMETRY_LOG_PROMPTS=falseexplicitly (S2 already says so — this confirms it is load-bearing, not belt-and-braces); andv0.43.0"fix(telemetry): inject quota_project_id to prevent fallback to default oauth client (#26698)" touches the telemetry auth path. The residual-live item from S2 — doesconversation_finishedfire after every turn including tool-heavy ones — is still owed and is now owed against a much-changed codebase.Decision 5 — transcript path. v0.53.1
docs/cli/session-management.md:18says sessions live in~/.gemini/tmp/<project_hash>/chats/. Amendment 1 established from the 0.42.0 bundle that the dirname is not a hash — it is a slugifiedbasename(cwd)deduped via~/.gemini/projects.json, and theprojectHashfield is legacy-migration only. Either the doc is stale or the scheme reverted.LocateTranscriptdepends on which; re-verify on the real binary, not the doc.Decision 3 / S5 — auth. Two changes land on the seeded-
oauth_creds.jsonplan, one helpful and one risky:v0.43.0"fix(core): prevent silent hang during OAuth auth on headless Linux (#26571)" — directly theNO_BROWSER=truepaste-code flow S5 has to drive. In our favour.v0.44.0"fix(core): preserve OAuth refresh tokens during rotation and retrieval (#26924)" and "fix(cli): allow keychain auth for--list-sessionsand non-interactive mode (#26921)" — a keychain credential path now exists. If creds can live somewhere other than~/.gemini/oauth_creds.json, the seeded-creds decision and theAuthStatus/Logoutdesigns (creds-file presence, creds-file removal) need re-checking on the target platform.Decision 9 — model catalog is stale.
v0.44.0added gemini-3.1 aliases and thinking config;v0.46.0transitioned to a flash GA model;v0.47.0moved auto mode to 3.5 flash and fixed Vertex model mapping;v0.44.0/v0.45.0reworked auto routing and preview-model fallback;v0.44.0/v0.49.0changed quota-exhaustion fallback behaviour (relevant to S6's "429 vs silent fallback model" question and toflash_fallback). The catalog must be re-taken live under the real account regardless — this is just notice that the candidate list in decision 9 (gemini-3-pro / gemini-3-flash) is out of date.New surface that did not exist at 0.42.0: native git-worktree support. v0.53.1 ships
docs/cli/git-worktrees.md— "you can use Git worktrees to give each Gemini session its own copy of the codebase", experimental and settings-gated. Every lab instance already runs in a linked git worktree. The CLI now has its own opinion about worktree-per-session, which can collide with lab's. Expect to leave it disabled and record that in compat.md; check that it is genuinely off by default and that its presence does not change workspace/trust resolution in a worktree cwd.Security fixes in the window that reach how lab would use this CLI
Recorded because, as with codex (#249), this binary would run inside lab instances:
v0.44.0— "security: update dependencies to fix critical and high vulnerabilities (#27077)".v0.49.0— "fix(cli): prevent path traversal vulnerabilities during skill install (#27767)" → decision 8's surface.v0.51.0— "fix(core): resolve symbolic link directory escape in memory import processor (#28233)" → the context-file loader, i.e. decision 7's surface, and lab worktrees are exactly the symlinked/indirect workspace shape such bugs live in.v0.51.0— "fix(security): enforce case-insensitive sensitive path blocklist and vscode hitl (#27966)".v0.53.0— "fix(a2a-server): enforce workspace trust and task isolation to prevent RCE (#28470)" and "refactor(cli): align macOS permissive Seatbelt profiles with deny-default model (#28424)". The a2a-server is not a surface lab uses, but the workspace-trust model is what--skip-trustopts out of;v0.49.0"Fix/pending tools and trust overrides (#27854)" moved it too. Re-confirm that--skip-truststill means "per-session trust, zero global mutation" (decision 4) and has not become a broader grant.One thing for the maintainer, not the implementing agent
v0.47.0shipped "Add documentation and migration commands for Antigravity CLI" and "update the max amount of times the Antigravity transition banner can be displayed". The release notes do not say what the relationship is, but migration commands plus a transition banner read like gemini-cli is being pointed at a successor. Since this issue is a whole third adapter at the codex bar — the most expensive kind of work in this repo — it is worth confirming the tool is not on a deprecation path before spending that. Not something an agent can decide.Suggested handling
Nothing here changes the shape of the adapter, and none of it is a reason to close the issue. But the spec is currently pinned to a version whose evidence has aged out in at least two places, so: re-pin the version (probably 0.47.0), re-drive the S1–S7 spikes against it, and post that as Amendment 3 before adapter code — the same order this issue already mandates, just against a moving target. Items 1 and 2 above (
--session-id, session retention) should be answered first, because if either lands the way the docs suggest, decisions 4, 5, and 11 need re-grilling rather than re-verifying.Filed by the Daily agent update check schedule, 2026-08-02. Release notes from the GitHub releases API for
google-gemini/gemini-cli(v0.43.0…v0.53.1); flag/settings/telemetry evidence from that repo's docs tree at tagv0.53.1; nixpkgs versions frompkgs/by-name/ge/gemini-cli/package.nixat the two revs quoted. No live binary was driven.