Repository navigation
refactor(intents): make hook ordering explicit and run hooks concurrently behind experimental.concurrent-hooks - #734
Merged
Merged
Conversation
…e-writer Prepares the dispatcher for running a hook point's handlers concurrently: nothing may depend on the order modules are registered in, or on the order `collect()` returns results in. Implicit order dependencies, now capabilities or hook points: - delete "shutdown": the agents require `agent-stopped`, so they stop the agent only after the api-server has closed its terminal - delete/hibernate "shutdown": the lifecycle claim provides `workspace-claimed`; the agent resolver and the api-server's terminal close require it - vscode:modal-changed: terminal-focus provides `modal-recorded`, which the agents require before re-reporting status - app:shutdown gains a "handoff" hook point between "stop" and "quit" for the update installer, which must start after teardown and before app.quit() Result folds: - `lastDefined` becomes `onlyDefined`, which throws when two handlers define the same field; the hand-written last-write-wins folds use it too - `mergeRecords` merges env/metadata maps and throws on a key clash - `detect`'s blocking processes are concatenated rather than picked Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mental.concurrent-hooks With the flag on, the dispatcher starts every handler whose `requires` are met at once and re-checks the waiting ones as each finishes, instead of running them one at a time. Events take the same path. A handler sees the capabilities provided before it started; every handler still runs, errors are collected per handler, and unsatisfiable handlers are still skipped. A hook point with a single handler keeps the sequential path, so its timing is the same either way. The flag defaults to false and is read once per hook point, so it applies live. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every launch passes --experimental.concurrent-hooks=true, so a hook dependency that only holds by registration order fails in e2e before the flag becomes the default. Unit and integration tests keep the dispatcher's sequential default. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
stefanhoelzl
force-pushed
the
concurrent-intents
branch
from
October 2, 2026 18:26
e7b6352 to
f8cfb54
Compare
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.
requires/providescapabilities or hook points:shutdown: the agents requireagent-stopped(CAPABILITY_AGENT_STOPPED), so they stop the agent only after the api-server has closed its terminalshutdown: the lifecycle claim providesworkspace-claimed; the agent resolver and the api-server's terminal close require itvscode:modal-changed: terminal-focus providesmodal-recorded, which the agents require before re-reporting statusapp:shutdowngains ahandoffhook point betweenstopandquitfor the update installer (quitAndInstallmust start after teardown and beforeapp.quit())results[]carries no order:lastDefinedbecomesonlyDefined(throws when two handlers define a field),mergeRecordsmerges env/metadata maps with a per-key clash check, and the hand-written last-write-wins folds (resolve/open project, resolve workspace identity and state, get-status, switch, delete) use them.detect's blocking processes are concatenated.experimental.concurrent-hooks(defaultfalse, applies live): the dispatcher starts every handler whoserequiresare met at once and re-checks the waiting ones as each finishes; events take the same path. A hook point with a single handler keeps the sequential path, so its timing is unchanged. Sequential mode is tick-for-tick identical to before.e2e/fixtures.ts), so an order-only dependency fails in e2e before the flag becomes the default. Locally: 5 repeats of the warm suite (600 spec runs) with the flag on, one failure — a pre-existing startup race (ch ws createright afterch project open, its nestedproject:openblocked by idempotency while app:ready restores the same project), being investigated separately.docs/INTENTS.md(ordering contract, fold policy, the flag, app-shutdown hook points),docs/TESTING.md,docs/USER_GUIDE.md(settings),CLAUDE.md.🤖 Generated with Claude Code