release: 6.0.0 - #80
Merged
Merged
Conversation
SessionPersistence takes restore, the open-tab record and the title (rename and the generated one). The forwarders are gone and every caller reaches the owner: cards for pending permissions and edit snapshots, tokens for the counters, diffs.refreshAfterRewind, rollback, lifecycle for isStarting/refreshBootState/dismissLoginCard, turnControl.interrupt, agentScanner.scan, and ClaudeSettings — through the now-public project — for provider, checkpointing and the guard state. The two constants with a single user move there (EXPIRED_TOKEN_NOTICE to ConversationEvents, the control timeout to SessionControlClient). The detekt baseline is emptied: neither LargeClass nor TooManyFunctions holds on ClaudeSession any more, which was the agreed signal that the split is done.
SessionProcess owns the ClaudeProcess handle, the launch generation, spawning for one generation and what an exit code means; SessionLifecycle keeps the policy — when to start, the gates, the boot state, stop and shutdown. The generation is still bumped at the same points (start, the first line of stop and of shutdown), so a late exit callback is ignored exactly as before. refuseRemoteProject no longer clears a starting flag that is never set when the gates run.
LoginCoordinator keeps the public surface, the route decision and the sign-in state. LoginAttempt (LoginFlows.kt) is the single PTY flow; the card and the dialog are two LoginUi implementations of it, so the two near-identical flows collapse into one. SignInCompletion is the verify-and-vault step. SessionNotifier gains the two notifications with an action that were built inline. Deltas from folding the dialog flow into the shared one: its code now goes through submitCode, so the 45 s verify watchdog covers it too; the dialog path stores the setup token like the card path always did; and a card sign-in that fails verification no longer raises a balloon on top of the card's own error.
…n the EDT Pressing Sign in ran resolveEnv() — which sources the user's env script and waits up to 15 s for it — and PtyProcessBuilder.start() on the EDT; verifying a finished sign-in resolved the env on the EDT again. The attempt now starts on the pool and reports back through edt; a cancel that lands while the spawn is in flight terminates the fresh PTY instead of leaving it orphaned, and a second press while one is starting is ignored. EdtAuthContractTest pins both hops.
…reader The whole read loop sat inside one runCatching, so an exception from a listener callback (onToken writing to a locked PasswordSafe) ended the reader: the code prompt that followed was never parsed, and the flow then reported the exit code as a clean success. Each callback is now isolated and logged at warn, the reader keeps going, and scanning stops once the URL, the prompt and the token have all been seen instead of re-parsing the accumulated output on every chunk. The PTY spawn is injectable so the reader has a test.
cancel() destroyed the PTY and the reader then reported the killed process's non-zero exit as a failed login: the card flipped from idle to an error after the user had cancelled, and the dialog route showed a 'Login failed' balloon right after 'Login canceled'. A cancelled flow now reports nothing.
SessionListing owns 'Open previous session': the newest files with title, first prompt, branch and timestamp. It reads each line through SessionTranscriptReader.parseRecord — the one JSONL line parser, now exposed — instead of carrying its own Json instance, and still stops at the first line that completes the three fields. EntryDTO and SessionRef move to their own file.
ProtocolParser keeps the top-level and system tables and the two status notices. MessageParsers takes assistant, user and stream_event; ControlParsers takes the control frames and the rate-limit event, with control_request going through a subtype table like everything else, so a new control subtype is a row and not another branch in the one function that grows per feature. unwrapToolError had no caller outside the parser and is private.
Msg is its own sealed hierarchy (ui/jcef/Msg.kt) and JcefBridge only parses. SessionControl gains three sealed sub-families — Vuln, Navigation, Onboarding — so the router's else-chain onto three boolean handlers is gone: every when is exhaustive and a new message that nobody handles is a compile error, not a warn at runtime. ChatBridgeRouter is the seven-arm dispatch; each family has a Bridge* handler in ui/. The whitelist and suspension arithmetic moves to GuardWhitelists and SecuritySuspensions as pure operations on State; the live half of a settings-menu toggle is the SettingsMenuApply table; the panel registry and its broadcasts are LivePanels; VulnPromptedActions lives in vuln/ next to what it quotes; pooled payloads go through JcefHost.execBuilt, which skips a disposed browser.
A settings toggle now learns whether the key is known by applying it to the live state once, then persists through update {}, so the block handed to update is pure on State.
The card's file_path comes from the tool input, and View diff read it synchronously on the EDT with no containment check and no size cap: a card naming a file under the home directory opened it in a diff, and a large one froze the UI while it was read. DiffPresenter.readCurrent decides — inside the project root, under 1 MB, absent files diff against nothing — and the read happens on the pool; openDiff is then handed the text it already has.
… the page are their own files PageAssembly builds the document and its CSP (appNames and CSS_PARTS live there now, which is where the frontend test loader and the guard-view contract read them). PageRoute is the ladder's pure part. SchemePageServer is the custom-scheme registration and its resource handler. PageDelivery is the ladder itself: the watchdogs, promotion, the loopback server and which rung has been proven. JcefHost keeps the browser, the JS query, exec and its queue, the deferred blocks and the load and navigation handlers. util/Edt.kt gains edtNow — run inline on the EDT, hop otherwise — which replaces the host's private copy.
The replacement chat for a closed last tab is built into a component that is not on screen yet. JBCefOsrComponent.addNotify is what creates the browser, and the 2.5 s ready watchdog was armed in deliver(), before that — so on a cold CefApp, or right after disposing the previous browser, the deadline fired before the first navigation could start and promote() walked the ladder. Its last two rungs, INLINE and NOTICE, loaded through loadHTML, which navigates to file:///jbcefbrowser/… and is refused by the host's own navigation guard: ready stayed false, the queue never drained, no script ran — no composer, no tabs, 'loading' forever — until Open previous session built a browser on a warm CefApp that met the deadline. The watchdog is now armed on the main frame's first onLoadStart, when a browser exists to load; the two dead rungs and the RemoteDevNotice page they served are gone (PageRoute is SCHEME → LOOPBACK → nothing); exec runs nothing in a disposed host and the unreachable JcefHost.dispose() is folded into the Disposer registration; and ChatTabsPanel.close opens the replacement in a finally, so a teardown that throws still leaves a chat on screen.
… switch on GitAction.behaviour is sealed — InitRepository, Prompt(text), Ide(actionId), Host(run) — and perform() is one exhaustive when over it. The two when(action.id) blocks that duplicated the catalogue as arms (and answered a new row with 'No prompt is wired') are gone: a row now says what it does where it is declared, and a row without a behaviour does not compile. Kind and ideActionId are derived so the catalogue's readers and its test keep their vocabulary. GitInit owns the one process the plugin spawns and the VCS mapping that follows; IdeActionInvoker owns the platform-action invocation. IdeActionApiContractTest follows both.
refresh() was reentrant only by a flag: a second caller arriving while a snapshot was being collected had its callback dropped, and the re-run answered the first caller twice. With two chats open, the one that asked second never repainted its Git card after a branch change. Callers now queue; each is answered by a collection that started after it asked, exactly once.
Four files in main and three tests still carried prose: the mechanism each one held (what UntrustedState disarms and why, why the trust gate leaves the binary paths out, why session-scoped suspensions are keyed per project, what the two reachability scans can and cannot see) now sits in the package maps next to the file it describes. One line in ClaudeProcess was the orphaned half of a sentence the previous sweep cut.
SettingsMenuRows builds the JSON the page draws; JcefSettingsMenu keeps the key vocabulary and applies a toggle to State. Both were one 300-line object with two unrelated reasons to change.
…cefHost PageLoadHandler holds the main-frame failure verdict and reports three outcomes: started, arrived, missed. installNavigationGuards is the pair of CEF handlers that refuse any navigation off the plugin's own page. JcefHost keeps the browser, the queue and the deferred blocks. PageStateRecoveryContractTest follows the handler.
… session The two forSession factories take the lambda plumbing out of ClaudeSession, which is left with declarations and its verbs. The injected-lambda constructors stay, because the tests drive both through them.
GuardFixture holds the base policy and the read/bash/edit builders; GuardProbe is the base class the guard tests extend for v/rule/why against their own policy. The same five helpers were copied into 27 files. No assertion changes: every test keeps the policy it had, spelled as basePolicy().copy(...) where it differed from the base.
Thirteen names promised an ASK, a card, a third-party distinction or a per-caller behaviour that the suite deleted releases ago while the assertion underneath said DENY. The verdicts are untouched; the names now say what is asserted, so the next reader does not trust the sentence over the code. SecurityRuleFamiliesTest also joins the shared fixture, the one file the previous commit missed.
…ALLOW' Eight fuzz tests asserted only that a vector did not pass, so a vector caught by the wrong rule — whitelistable under a different label, reported under a different reason — read as green. Each now asserts DENY and the rule (or, where the bases mix families on purpose, the small set of rules they belong to). The unreadable-script fuzz pins PRIVILEGE_ESCALATION for its sudo variant, because the command families are judged before the script is opened; that order is the guard's, and the test says so.
…each Sixteen assertions across four files repeated the guard's own reason wording as a literal, so a change to one word broke sixteen tests that were not about wording. GuardReasonWords holds each load-bearing fragment once; the production strings stay literal, because they are the copy the user reads. Where an assertion was really about which rule answered, it already asserts the rule.
Twenty assertion pairs asserted the same input twice against the same policy, left over from the trusted/untrusted split that no longer exists; the second copy could never fail on its own. The fuzz variant that aliased one payload under two names goes with them, as does a test whose two lines were already asserted verbatim elsewhere. The stronger-rule test now pins the rule instead of a verdict two other tests already pin. No expectation changes.
GuardCardMandatoryTest stubs the guard's verdict and drives PermissionBroker with it: card, refusal, bypass row, per-command approval. It runs no rule, so a name suggesting it proves cards are mandatory under the real guard overstated it. The stubbed verdict is the right seam for the broker and stays; only the name changes.
It reached a private field of GuardPaths and asserted its maximum pool size sat in 1..32 for a value of 8: a test of an implementation detail with a range wide enough never to fail. The invariant it stood for, that a hung mount cannot freeze evaluate() or pay a timeout per path, is what the budget and timeout tests beside it already assert against observable behaviour.
The refactor split left three references that never compiled, uncaught because the tree was written ahead of the gate: GuardWhitelists.all returned a Set where its signature promised a List, JcefChatPanel referenced SettingsMenuRows without importing it, and JcefAccountData read auth status off ClaudeSession directly when it now lives on lifecycle.auth. Main sources compile again.
Case folding in GuardPaths.under read System.getProperty(os.name) from a frozen val inside an object, so Windows and macOS containment were unreachable on Linux CI and the drive-rooted branch had no test. The choice becomes Policy.caseInsensitivePaths, threaded through under() and supplied by SettingsSensitivePolicy as SystemInfo.isWindows || isMac. The Linux default is false, which reproduces the os.name behaviour on the CI host exactly, so no verdict changes. WindowsVectorCorpus and GuardWindowsContainmentTest exercise the branch: case-folded drive roots, a UNC project root, backslash-home normalisation, and one parameterised vector per rule family in Windows spelling, each pinning the rule the guard gives today.
The model list moved to session.catalog in the Phase A split; the integration test still called the removed session.modelOptions(), so the test tree did not compile. It now asserts over catalog.models, which is where the binary-reported models live.
The script-launch scanner split a command on the bare pipe character, so a clobber redirect (a greater-than immediately followed by a pipe) was torn in two. Its target became the first word of a second segment, matched as a launched executable, and refused as SCRIPT_EXECUTION when it is really a file write. With the pipe no longer split out of the clobber operator, the target stays in the verb's segment and the write is judged by ShellFileWrites, which already understood the operator. Denied before, denied now, under the rule that fits. Found by the hardened mutation fuzzer.
LOCAL_ASSIGN required a separator immediately before the variable name, so the cmd assignment form with a quoted name left the variable unbound: the name sits behind the opening quote. A later percent-reference to it was then something the guard could not follow, and an ordinary Windows one-liner drew an UNRESOLVED_VARIABLE card. The pattern now also binds a name introduced by set with an optional quote. This can only remove a name from the unresolved set, never add a threat: a bound value is still judged by every rule. A variable nothing here defines still cards, as the test holds.
… the guard sees an own call flat The guard reads a tool call's path and content keys at the top level, which is where the native tools carry them; an own call carried them one level down, under args, so a write judged through the meta-tool did not look like a write to it. OwnTools.guardInput lifts the arguments beside the tool name before either gate (the server's GuardGate and the session's broker) evaluates the call, so both shapes get the same verdict. Independently, the tools that create or move files now refuse a destination outside the project root, as delete_file and the VCS write tools already did: write_file, create_file, replace_text, insert_text, move_file, file_from_template and worktrees add. GuardGateTest pins the nested write with a probe that reports the target missing.
CodeQL on the release pull request: the per-session socket directory was created owner-only, but its parent under the system temp directory took the default permissions, so other local users could list the session ids. The parent is now created owner-only and, when an older build left it open, tightened on the next start.
…instead of opening the browser The GitHub plugin replaces the list with the request's details inside the same content, so the second open of the same number found no list row and fell back to the browser. When no row is found, the tool now looks for the component of the Pull Requests window whose context enables Refresh Pull Request and fires it there (shown=refreshed); the browser is only for an IDE without the view.
A Unix socket takes the process umask at bind, which the plugin does not control; the owner-only directory around it was the only thing keeping other local users from connecting. The endpoint now sets the socket to rw------- right after bind, so the file defends itself whatever the umask, and ServerEndpointTest pins it.
…still without the focus ToolWindow.show marks the window visible but leaves another window selected on the same side, so 'show me the pull request' answered shown while the user kept looking at Bookmarks. Every reveal of a tool window now activates it with autoFocusContents=false: the window takes its place in front like a selected tab, the keyboard focus stays where it was, and the focus contract keeps refusing activate(..., true).
…n it is already open View Pull Request opens the request as an editor tab named by its number; asked again while that tab sat behind others, the tool refreshed the details panel instead of putting the tab in front, which is what 'show me the pull request' means. The tool now selects the open tab first (openFile without focus, like a source file), opens it from the list otherwise, and answers none rather than refreshing anything.
…without its defaults The models were only ever decoded, so the encoders the serialization plugin generates never ran and the package sat under the branch floor. Each model is now encoded and decoded back, full and empty, under the protocol's Json and under one that omits defaults; the usage window and rate-limit helpers get their branch cases too.
OsvScanner reached OsvHttp through a plain call, so nothing could stand in for the network and its 45 lines were never run. The call is now an injected function, OsvHttp.post by default, and the test drives the batch, the hydration, the silences, a malformed answer, cancellation at both points and the cut above the hydration cap.
…udit reads it first EnvScriptLoader.load ran a shell nobody tested, and SourceScriptAudit had no test at all. A temporary script is now sourced on POSIX and its export comes back; a blank, missing or exiting script yields nothing; the audit passes a harmless script, names a destructive one and ignores what it cannot read.
…ugh a real control client SessionQueries and RemoteControl were only reached through the live session. They now run against SessionControlClient with a fake scheduler: each ask writes its subtype and decodes its reply on the EDT, a stopped session answers nothing, the usage reply feeds the quota warnings, and Remote Control records state, error and the notice for every outcome. The protocol round-trip test takes the formatter's shape.
…d the gate probes the stand-in binary The login coordinator built its attempt and the attempt built its PTY, so no test could stand between them. Both take a factory now, the real ones by default. A scripted process plays the binary's own output: the url and the code prompt reach the ui, the pasted code reaches the pty, a verified login restarts the chat, a failed one carries the binary's wording, cancelling forgets the attempt, and another provider is told to use its key. The gate's probe, renewal short-cuts and the dialog login ui are driven against fake-claude and the test dialog manager.
…d tool window A Pull Requests tool window with a list of rows is registered in the headless fixture: the request's row is selected and yields the list's context, an unknown number yields none, the window opens with and without focus, and an open timeline tab is selected while a missing one is reported.
…un against the headless IDE A target's parsing and the contexts it yields (a file with its editor, PSI and caret element, a directory, the project), the reveal of a file and of a registered tool window and its content, an action dispatched and a toggle flipped through IdeActions, and every verb of the card places against the windows the fixture has and lacks.
…eveal test stops depending on test order NoticeNarrator had no test of its own: status notices, notifications loud and quiet, denied permissions with their best reason, recalled memories, persisted and failed files, plugin installs by status, both refusal shapes, informational notices by level and the logged rest. The reveal test no longer asserts on windows another fixture may have registered in the shared light project.
…nd the coverage gate measures what can run MarketplaceGateway takes its fetch as a parameter, the HTTP one by default, so the update rows, their defaults, a dead endpoint and a reply that is not a list are all tested; GitHubAvailability runs headless with and without the plugin. Two corrections to what kover measures: the uiTest source set was read as production code and showed up as a package at zero, and GitHubGateway joins the gateways that need a live plugin, an account and the network, on the same grounds as the database and OSV ones. No floor moves.
…e-for-jetbrains into feature/release_6.0.0
…code-for-jetbrains into feature/release_6.0.0
…nary 2.1.270 checkDrift flagged four control subtypes the SDK now declares and the plugin did not acknowledge: get_hooks_listing, list_permission_rules, reload_output_styles and update_settings. None of them backs a plugin feature yet, so they join KNOWN_SUBTYPES the way list_models did instead of getting a request builder nothing calls; the reachability contract rejects a member without a caller. The SDK also stopped declaring generate_session_title, get_plan, get_workspace_diff and side_question. The plugin still sends all four (session titles, the Plan view, the workspace diff and /btw), so they stay known until the binary refuses them at runtime.
…6.0.0 Feature/release 6.0.0
Owner
Author
|
This Release is being driven by Claude Code Native Plugin. |
Owner
Author
|
CodeQL The query treats
An explicit owner-only ACL for that branch is tracked for 6.0.1. |
… target IDE lacks The develop-to-main release PR went red on Plugin verifier with all eight targets Compatible: PyCharm has no com.intellij.modules.java, the optional dependency behind the uast domain, and the IntelliJ Platform Gradle plugin (2.16.0, 2.18.1 alike) parses the verifier's output by the "Missing dependencies" heading without telling an optional gap from a mandatory one. With FailureLevel.MISSING_DEPENDENCIES on, every optional dependency a target lacks is a failed verification. The protection that level gave, a mandatory dependency the IDE cannot satisfy means the plugin never loads, moves to PluginDependenciesContractTest: every non-optional <depends> must be a platform module every IntelliJ-based IDE ships, every optional one names an existing config-file, and the level stays out of failureLevel until the Gradle plugin ships its "missing mandatory" verdict marker. verifyPlugin now passes locally with the Java dependency declared: 8 of 8 Compatible, 42 experimental API usages, no deprecated or internal API.
2 tasks
… target IDE lacks (#81)
…y layout The release PR to main failed Build plugin with 121 legitimate entries listed as unexpected: the allowlist admitted only flat jcef/*.js|html and jcef/css/*.css, and since 6.0.0 the web app lives in directories by family (controllers, core, models, views, css/views/...). The pattern now accepts subdirectories while still demanding the js, html and css extensions, so a stray map, doc or fixture under resources/jcef keeps failing the build. Checked against the local 6.0.0 zip: 0 unexpected entries, 126 jcef files, all js, html or css.
1 task
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.
Summary
Promotes
developtomainfor release 6.0.0 — Claude becomes one with your IDE. Content: pull requests #76 and #79 (the plugin's own MCP servers, Claude God Mode, the live mirror, the IDE tool cards, pull requests and releases through the IDE's account, the guard inside the servers, the restructure, the TypeScript page, the Log view, the coverage suite and the protocol baseline for SDK 0.3.270 / binary 2.1.270, closing #75). Release text: the 6.0.0 block of CHANGELOG.md and RELEASE_NOTES.md.Replaces #77, closed once develop moved past its head.
Related issue
Closes #75.
Type of change
Risk and rollback
Risk: merging publishes 6.0.0 to the Marketplace. The IDE integration is on by default with a documented off switch; the permission surface is unchanged.
Rollback: a user on 6.0.0 stays there until they update; a fix ships as 6.0.1 through the same door.
Checklist
developcarries release: 6.0.0 — Claude becomes one with your IDE #76 and Feature/release 6.0.0 #79 with their checks green.Notes for reviewers
Opened, commented, watched and merged from inside the IDE through the plugin's own
pull_request_opstools.