Skip to content

feat(runtime): mount state kernel and notice ledger into generated request scopes (#233) - #251

Merged
ScriptedAlchemy merged 3 commits into
mainfrom
fix/233-request-scope-mounting
Sep 2, 2026
Merged

feat(runtime): mount state kernel and notice ledger into generated request scopes (#233)#251
ScriptedAlchemy merged 3 commits into
mainfrom
fix/233-request-scope-mounting

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

Summary

Closes the generated-runtime mounting gap tracked by #233 (items 1 and 2) and the #99 audit finding: every generated invocation path now mounts a correctly-scoped state driver and notice ledger, so a compiled bundle's routes get working (await agent()).state and .notices handles instead of permanent unavailable.

Mounting architecture

  • New @agent-bundle/runtime/mount subpath: createGeneratedRuntimeState({ definition, driver }) owns the project state store plus the notice ledger (agentNoticeStateDefinition(lifetime)) over one driver, with lazy opens and typed-failing handles when the driver cannot open (honest degradation, never fabricated success). Never imports node:sqlite — callers inject the driver.
  • Authoring surface: conventional src/state.ts default-exporting defineState({...}); the build statically extracts literal id/lifetime (parse-only, AB4818–AB4820 diagnostics; state: false opt-out; external lifetime rejected for generated mounting v1).
  • Generated MCP flight worker (where route components actually execute) owns the drivers: memory driver for request/process lifetimes, node:sqlite driver for workspace-durable rooted at $AGENT_BUNDLE_PLUGIN_ROOT/state (artifact-root fallback). The MCP host process forwards full invocation identity (requestInvocation, host, workspace) so notice admission runs exactly once, in the render scope, idempotent per invocation id. createWarmFlightHost grows optional runtimeState ownership.
  • Routed CLI plain commands, rendered CLI/script workers mount the same way (cwd .agent-bundle/state fallback for package bins). Stateless projects emit zero state/ledger code.

Proofs (per harness level)

  • route-unit: manifest registers the declared state; renderRoute auto-mounts fresh isolated bindings per render (sqlite temp root for durable definitions); caller-supplied context.state wins.
  • mcp-in-memory: journal tool commits and re-reads durable state through the real generated server; publish tool returns a pending notice.
  • packed-stdio: one shared tarball/install/build, two spawned servers — durable journal state survives the process restart (sqlite files verified at <pluginRoot>/state/), and a notice published in spawn 1 is observed attempted by a thin-client event invocation (IPC socket) in spawn 2, rendered by the compiled event route.
  • packaging boundaries: stateless artifacts contain no mount/sqlite/ledger identifiers (template- and built-artifact-level assertions); the runtime's exports/packaging test covers the new ./mount entry.

Deferred (recorded on #233): kernel-contract budgets (item 3) and inspect/Workbench lifetime visibility (item 4); external-lifetime generated mounting; app-configurable notice authorization policy.

Test plan

  • pnpm typecheck, pnpm lint
  • pnpm --filter @agent-bundle/runtime test (mount, warm-runtime, packaging boundaries)
  • pnpm test:route-unit, pnpm test:projection
  • scoped unit files (entry-shell emission matrix, state extraction, manifest)
  • node scripts/run-packed-tests.mjs — 23 passed, 0 failed

Refs #233, #98, #99.

@changeset-bot

changeset-bot Bot commented Sep 2, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 74d2e58

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@agent-bundle/runtime Minor
agent-bundle Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 2, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-02T00:30:21.673950Z 74d2e58 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@pkg-pr-new

pkg-pr-new Bot commented Sep 2, 2026

Copy link
Copy Markdown
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle@251
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/create-agent-bundle@251
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@251

commit: 74d2e58

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 74d2e58fef

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

|| lifetime === 'process'
|| lifetime === 'workspace-durable'
|| lifetime === 'external';
if (id === undefined || id.trim() === '' || !accepted) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject the notice ledger's reserved state ID

When src/state.ts declares the otherwise-valid ID @agent-bundle/runtime/agent-notice-ledger/v1, the generated owner opens both the project definition and the internal notice definition through the same driver under that ID. For process state, the memory driver can return the project store as the notice store; for durable state, both definitions address the same SQLite file. Notice operations—or state operations, depending on which open wins—then fail with schema/event errors, so this reserved ID needs to be rejected during extraction.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in #254.

Comment on lines +447 to +448
|| context.state !== undefined
|| context.noticeLedger !== undefined

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Mount each missing route-unit binding independently

When a route-unit test supplies only context.state, this condition skips the entire manifest mount and leaves agent().notices unavailable; supplying only context.noticeLedger similarly removes the generated state binding. Production generated routes mount both, so tests that override one binding cannot accurately exercise routes using the other. Preserve each caller-provided value while auto-mounting the independently missing binding.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in #254.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant