Repository navigation
Add before and after hooks for component expansion and bound commands #956
Description
Story
Component authors can observe component expansion and bound shell commands before and after execution without being responsible for invoking the operation or returning its result.
The approved factory returns the operation and two independent registration functions:
const { expand, beforeExpand, afterExpand } =
createHooks<"expand", TArgs, TResult>(...);
yield* beforeExpand(recordExpansionStart);
yield* afterExpand(recordExpansionEnd);
const value = yield* expand(args);The constructor arguments are resolved in the implementation plan; the returned names and the behavior below are the accepted contract.
Current gap
Component.expand and Component.applyBoundModifiers expose continuation middleware, then separately enforce invocation count and preserve the outcome that core produced. Their instrumentation needs before/after hooks, while core owns execution and settlement.
The approved design is recorded in #954, under Middleware and hooks in architecture.md, with architecture rules 33–40. The adapter has no implementation in that PR.
Contract and acceptance
createHookscomposes the existingcreateApiwithout modifying that dependency. Hook registration is scoped and exposes neithernextnor a replacement return channel.- The owner invokes the implementation once after before hooks complete normally. A throwing before hook prevents the implementation from starting.
- After hooks receive the arguments and Effection's
Result<TResult>: success, the before-hook rejection or implementation failure, or a dedicated cancellation error. Cancellation still unwinds as cancellation. - Before hooks run outer to inner; independently registered after hooks run inner to outer, including when a before hook rejects execution. One failing after hook does not skip the remaining after hooks. Every after hook observes the same outcome selected before after hooks run.
- A failing after hook is reported without changing a completed effect or its saved result. An operation failure remains primary when both fail. The adapter never retries the operation to recover from a hook failure.
- The call owns hook work and waits for cleanup. Concurrent calls remain independent, and separately loaded package copies preserve the existing scoped composition behavior.
- Migrate component-expansion and bound-command observation as real production consumers. Preserve their progress reporting, bound process results, timeout behavior, cancellation and teardown. Remove delegation bookkeeping only where the adapter replaces its responsibility.
- Update the affected public references and conformance clauses with the migration. Ordinary modifier replacement, generated evaluation admission and durable ownership retain their own contracts.
Evidence
Use focused adapter regressions and the production consumers. A before-hook rejection performs no underlying work but reaches after hooks as a failure; a completed effect followed by a hook failure is not repeated; cancellation reaches after hooks and joins cleanup. Cover ordering, independent registration and scoped composition without adversarial native-object permutations.
Existing regression entry points include:
deno task test packages/core/tests/component-expansion.test.ts packages/core/tests/foreground-exec.test.ts packages/core/tests/exec-timeout.test.tsThe Planner chooses and freezes the exact affected evidence after tracing consumers and resolves package/export placement and the remaining constructor/type details before assigning implementation. A new material API or ownership decision returns to the Architect.
Dependencies and scope
Depends on the approved contract in #954. #957 classifies mixed APIs in parallel; #958 owns the broader defensive diagnostic/test cleanup. Related ownership work: #491. This issue owns the adapter and its first two consumers; it does not convert every middleware API or introduce a new permission, identity, persistence or provider protocol. Delivery completes when the implementation, first consumers and their updated references merge.
Activity