fix: CLI parity — runtime account selection, live reasoning, watcher restart loop - #177
Merged
Merged
Conversation
With several stored rows for a provider the adapter pinned the lowest row id. That bypassed gjc accounts pin, routing exclusions and usage-limit rotation, and sent every Anthropic turn to an account whose organization refuses OAuth (403) while the CLI answered from another. Only an explicit credentialId installs a selector now; the run reports the row the runtime settled on.
Reasoning reached the browser as one block at thinking_end, so a long reasoning phase looked frozen while the terminal streamed it. thinking_delta previews now fill one live row per session, painted once a frame; the thinking record replaces it, and the end of the turn clears one that never got its record.
macOS reports a metadata change on the session-scope directory for every atomic transcript write. The watcher rescanned that directory each time; a scope with 25k entries overflowed the 4096-entry bound and restarted the watcher into a full reconciliation over and over.
SDK sessions in the Bun suites wrote their handled errors into ~/.gjc, where they read as app crashes (8 of 12 post-0.17.6 records). Each Bun test file now runs with a throwaway HOME; git keeps the operator's global config.
Account selection and the service tier follow the runtime, as in the CLI; the handoff records the parity matrix, the fixes, the remaining gaps and the beta.20 blocker.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
An adversarial pass comparing the app with
gjc -pon SDK 0.17.6. The same prompts ran through the app's public WebSocket (chat.send, which is how the browser sends a turn) and through the CLI, in one fixture repo reset between runs. The pass found four defects, fixed here.Fixes
1. The runtime now chooses the account, as it does in the CLI (
fix(gjc), blocker)When a provider had several stored rows, the adapter pinned the lowest row id. For Anthropic that row belongs to an organization that refuses OAuth, so every app turn failed with
403 oauth_not_allowed_for_organization. The CLI answered from another account because the runtime's own selection honoursgjc accounts pin, skips accounts its routing has excluded and rotates when one hits a usage limit. A pinned selector disabled all three. Only an explicitcredentialIdinstalls a selector now, and the run reports the row the runtime chose (getSessionCredentialRowId). Contract tests assert that no selector is installed and that the reported row is the runtime's choice, not the lowest id.2. Reasoning streams while the model thinks (
feat(chat))The CLI streams reasoning deltas. The app sent reasoning only at
thinking_end, so a turn looked frozen for the whole reasoning phase (16 s in one measured turn).thinking_deltaframes now fill one live__thinking_<session>row, painted at most once per frame. Thethinkingrecord replaces it, andcompleteclears one that never got its record. Tested at two levels: the event mapper, and a DOM test against the real store. Checked in a browser: under the Detailed density, "Reasoning…" fills in while the turn runs. Balanced and Compact still hide reasoning by design.3. The native session watcher no longer restarts on every transcript write (
fix(core))macOS FSEvents reports
Modify(Metadata)on the session-scope directory for each atomic transcript write. The watcher treated that as a new directory and rescanned it. A 25k-entry scope exceeded the 4096-entry backfill bound, so the watcher failed, restarted and ran a full reconciliation. It did this 16 times in a few minutes ofnpm run dev. The cause was pinned down with an instrumented binary; only created or renamed directories are backfilled now. A regression test covers a metadata event on a directory above the bound. After the fix the probe survives live CLI writes.4. Tests no longer write into the operator's crash journal (
test)Bun test files wrote their SDK handled errors into
~/.gjc, where they read as app crashes. 8 of the 12 records after the 0.17.6 upgrade came from this suite. Each Bun test file now runs with a throwawayHOME, andGIT_CONFIG_GLOBALstays set so commit fixtures still work. Zero records were written during the fullnpm run verifybelow.Parity confirmed
Checked through both paths:
AGENTS.mdrules are applied, and the skill list is identical.task/subagentdelegation works.Checked on the app side:
/fast,/effort,/context,/usageand/toolsall work in the app.gjc -pdoes not run slash commands.Latency for the same one-word turn: app 11.6–11.9 s, CLI 13.5–13.8 s (the CLI pays process start).
The service tier follows the runtime, as it does in the CLI (#160). A new session reports no tier; after
/fast onthe next turn reportspriority; after/fast offthe tier is gone. The app pins none. The spec records this.Known differences, left as they are
server/gjc-agent-tools.ts: python/eval, job/monitor, github, debug, checkpoint/rewind, tool discovery and move_session. A project's own.gjc/mcp.jsonand extension modules do not load.ChatInterfacecallssessionStore.setActiveSessionduring render, which logs a React warning on every session open. Zero-delay synthetic typing after a reload trips "Maximum update depth" in the composer resize; it does not reproduce at a 10 ms keystroke delay.Verification
npm run verify: exit 0. This covers audit, licenses, typecheck,check:core(69 Rust tests), the full Node and Bun suites, the 8 GJC e2e tests, lint, identity and build.Refs #158 #160 #162