Skip to content

feat(dev): gate host-facing epoch adoption on the dev-epoch contract matrix (#218 stage 4 / #179 stage 4) - #385

Merged
ScriptedAlchemy merged 3 commits into
mainfrom
feat/179-s4-dev-matrix-v2
Sep 3, 2026
Merged

feat(dev): gate host-facing epoch adoption on the dev-epoch contract matrix (#218 stage 4 / #179 stage 4)#385
ScriptedAlchemy merged 3 commits into
mainfrom
feat/179-s4-dev-matrix-v2

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

Summary

Stage 4 of #218 and the last design item of #179: a rebuild that would change the source→artifact→dev→installed-host contract is surfaced, not silently applied.

  • dev.contracts (opt-in, project-declared). agent-bundle.config.ts points at a project-local fixture module (Record<routeId, ContractRouteFixture>, the same shape runContractMatrix takes). It is reloaded and validated per prepared epoch (loadDevContractMatrix); an invalid declaration or module never fails compilation — it fails that epoch's gate with AB7210.
  • dev-epoch proof level + runDevEpochContractMatrix. The shared Generated plugin contract matrix: catch source→artifact→dev→installed-host drift before release #218 matrix runs against an already-open, epoch-pinned generated stdio session opened through the same McpSessionService live host connections use; boundary capabilities are honest (not-applicable for module-backed checks, same reasoning as packed/installed-host). ContractMatrixViolationError now carries structured failures so the gate can report exact check names per route.
  • EpochAdoptionPolicy. Sits between artifact.available and the two host-facing surfaces — HostMcpRoutes (stage-1 proxy endpoint) and DevHostInstallManager (stage-2 installs). Undeclared → every epoch adopted directly (unchanged behavior). Declared → only a passing epoch is adopted; a failing epoch stays inactive for hosts (AB7211), the last passing epoch keeps serving, superseded runs are discarded, and dev.contract.status is published per evaluation. Workbench playground sessions stay independently epoch-pinned and are never gated. Cold start with a failing initial build seeds the restored last-good epoch through the gate instead of leaving hosts with nothing.
  • Workbench visibility. ProjectStatus.hostAdoption (mode, adoptedEpochId, latest contracts evaluation) on startDevServer().status() and /api/project/status; the Overview renders a Host adoption section (host-facing vs published build, failed checks grouped by route) and folds gate diagnostics into Diagnostics; the project client refreshes on dev.contract.status; the Logs page accepts the new record kinds.
  • Diagnostics. The prior slice reused AB7005, which is already the Cursor install content-collision code — the gate now uses AB7210/AB7211, and docs/diagnostics.md gains the "Live development into hosts" section covering AB7200AB7202, AB7210AB7211, AB8024AB8025 (the stage-1/2 codes were undocumented).
  • Docs: docs/framework-mode.md "Live development into hosts", docs/architecture/rsc-runtime-workbench.md host-adoption axis, package README, root README dev command; changeset for agent-bundle (minor).

Evidence (local, rebased on ccb9cd18a)

  • pnpm build ✅ · pnpm typecheck ✅ · pnpm lint 0 errors / 0 warnings ✅
  • pnpm test:unit 2683 passed; the 2 failures (mcp-probe-service timed-out-teardown timing, native-claude-contract 5 s timeout) reproduce identically on a clean origin/main checkout in this environment.
  • pnpm test:route-unit 35/35 ✅ · pnpm test:projection 63/63 ✅
  • pnpm test:integration:run 75 files, 941 passed / 0 failed / 30 skipped ✅, including:
    • tests/dev-contract-adoption.test.ts — real dev server + real dev proxy stdio client: fixture edit → failed gate → dev.contract.status on the wire with {"routeId":"tool:fixture/unknown","checks":["coverage"]}, zero tools/list_changed, the connected proxy and a late-joining proxy both still serve v1, status().hostAdoption reports the failed epoch beside the adopted one; repair → exactly one list_changed on the same connection, v3 served, hostAdoption passed. Plus the undeclared-project path (direct adoption).
    • packages/workbench/tests/host-adoption.e2e.test.ts — real browser at 1440×900 against a live dev server: Host adoption data-state="passed" → break fixture → data-state="failed", "hosts keep build ", violations table, Diagnostics (1) with AB7211 → repair → passed, Diagnostics (0).
    • tests/epoch-adoption-policy.test.ts (direct/gated/superseded/seed/status), tests/dev-contract-config.test.ts (AB7210 without failing preparation), Workbench overview-model / overview-page / log-client / project-client schema tests.

Test plan

  • Unit / route-unit / projection
  • Full integration suite incl. the new adoption integration test and Workbench e2e
  • CI (gh pr checks --watch)

@changeset-bot

changeset-bot Bot commented Sep 3, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6e4b2a1

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

This PR includes changesets to release 1 package
Name Type
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 3, 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-03T05:51:33.262375Z f12dc34 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.

@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: f12dc34c1e

ℹ️ 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".


const targetFor = (prepared: PreparedProject): string => {
const targets = prepared.model?.targets.map((target) => target.name) ?? [];
const target = targets.includes('portable') ? 'portable' : targets[0];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Select a target that contains the configured server

When a valid multi-target project includes portable but the selected MCP server is restricted to another target (for example, targets: ['claude']), this always chooses portable. That target's generated manifest omits the server, so McpSessionService.open() fails and every epoch is rejected with AB7211. Choose from the selected server's target intersection rather than from all project targets.

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 on main in #408 (a00273b): devContractTarget(prepared, serverName) intersects the project targets with the selected server's own targets (from model.mcpServers) and prefers portable only when eligible, so a targets: ['claude'] server is opened on claude instead of a portable manifest that omits it; a server emitted for none of the project targets fails with a named error. Unit-tested in the new dev-contract-runner.test.ts.

Comment on lines +64 to +65
const matrixClient = (session: McpSession, signal: AbortSignal): ContractMatrixClient => ({
callTool: async (params, options) => session.callTool({

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve notification support in the matrix client

For any fixture declaring lifecycle, executeLifecycleTransitions() accesses the client's SDK _notificationHandlers map, but this adapter returns a new plain object without that map. The first lifecycle run therefore throws while calling notificationHandlers.get(...), is reported as an incomplete AB7211 evaluation, and prevents every epoch from being adopted. The adapter must expose a supported notification path to the underlying session before lifecycle fixtures can be gated.

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 on main in #408 (a00273b): ContractMatrixClient gains an optional observeProgress(listener) => unsubscribe seam (ContractMatrixProgressSource, exported from agent-bundle/test). executeLifecycleTransitions uses it when present, falls back to the SDK Client handler map otherwise, and reports a named lifecycle failure instead of throwing on a missing private field. The dev runner's adapter implements it over session.subscribeTrace (progress notifications are recorded synchronously on receipt), so lifecycle fixtures can gate epochs.

Comment on lines +181 to +182
#adopt(epochId: string): void {
this.#currentEpochId = epochId;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Pin the adopted epoch until it is replaced

The policy retains only the adopted epoch's ID, not an EpochReference. If no host connection currently leases the last passing epoch, each failed rebuild makes another epoch active; after six such failures, EpochStore retention removes the old passing epoch. A later host connection then tries acquireEpochReference(adoptedEpochId) for a deleted directory and cannot serve the advertised last-good build. Hold a lease for the adopted epoch until another epoch is adopted or the policy closes.

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 on main in #408 (a00273b): EpochAdoptionPolicy takes a lease option (wired to epochStore.acquireEpochReference) and holds the adopted epoch's EpochReference until another epoch is adopted (new lease acquired before announcing, old one released after) or the policy closes. A candidate that cannot be leased is not adopted and is published as AB7211 status. Tests cover the lease lifecycle and the lease-failure path.

serverName,
target,
});
const signal = AbortSignal.timeout(session.timeoutMs);

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 Apply the session timeout per matrix request

This creates one 30-second signal for the entire matrix and combines it into every later request. A valid matrix with enough routes, or several individually valid slow calls whose cumulative duration exceeds 30 seconds, therefore aborts all subsequent operations and rejects the epoch even though no request exceeded its own timeout. Create request-scoped timeout signals, or use a separately documented whole-matrix deadline rather than reusing the session's per-request timeout.

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 on main in #408 (a00273b): the matrix adapter creates AbortSignal.timeout(session.timeoutMs) per request (combined with any caller signal) instead of one signal for the whole matrix; unit-tested to produce a distinct, unaborted signal per call.

@pkg-pr-new

pkg-pr-new Bot commented Sep 3, 2026

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

commit: 2d2f455

Run opt-in project fixtures against each generated epoch before host-facing connections and installs adopt it, preserving the last approved epoch on contract failure.
…ch host-adoption visibility (#218 stage 4)

- AB7005 was already the Cursor install content-collision code; the contract gate now
  reports AB7210 (invalid dev.contracts declaration/fixture module) and AB7211 (matrix
  violations), documented in docs/diagnostics.md alongside AB7200-AB7202 and AB8024-AB8025.
- EpochAdoptionPolicy.seed(): a failing initial build publishes no artifact.available, so the
  restored last-good epoch is run through the gate instead of leaving hosts with nothing.
- ProjectStatus.hostAdoption snapshot (mode, adoptedEpochId, latest contracts evaluation) on
  startDevServer().status() and /api/project/status; the Workbench Overview renders it as
  "Host adoption" with the failed checks per route and folds gate diagnostics into Diagnostics.
- Workbench project client listens to dev.contract.status so the Overview refreshes when the
  gate settles; host-adoption.e2e proves the failed/passed cycle in a real browser at 1440x900.
- Shared dev-contract fixture project for the integration test and the e2e; drop the
  wall-clock negative sleep from the adoption test.
@ScriptedAlchemy
ScriptedAlchemy force-pushed the feat/179-s4-dev-matrix-v2 branch from 2d2f455 to 6e4b2a1 Compare September 3, 2026 06:23
@ScriptedAlchemy
ScriptedAlchemy merged commit 5fac480 into main Sep 3, 2026
4 checks passed
@ScriptedAlchemy
ScriptedAlchemy deleted the feat/179-s4-dev-matrix-v2 branch September 3, 2026 06:24
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
/#379/#385/#390) (#408)

* fix: address late review threads on merged PRs #368/#373/#374/#377/#378/#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)

* fix(dev): lease before publishing contract status; align fixtures with the portable byte lane

- EpochAdoptionPolicy acquires the epoch lease before publishing a passed
  dev.contract.status and announces adoption synchronously with it, so a
  status reader never sees "passed" for an epoch that is not yet adopted
- hooks.test: Codex PostToolUse accepts a string tool_response and rejects a
  missing one; Claude keeps the object check
- mcp-session-service/public-api-packed fixtures: Agent Plugins forbids
  placeholders in headers and non-bare/non-./ commands, and ordinary artifact
  validation now enforces the standard, so the fixtures carry a literal header
  and a bare `node` command

* chore(changeset): drop the Codex tool_response bullet already released by #404

* fix(playground,test): withdraw a failed catalog publication before releasing its staging link; bind custom observeProgress

- #persistSnapshot rolls the sidecar back while the staging link still exists
  when the post-link directory fsync fails, so a concurrent reader keeps seeing
  an in-progress publication until the path is withdrawn instead of adopting a
  briefly singly linked file
- contractProgressObserver invokes a client's observeProgress method with the
  client as receiver

* chore(changeset): one-paragraph summary ending with the PR reference

* fix(playground): recover a catalog staging link abandoned by an exited publisher

After the settle deadline, a matching .stage-<pid>-* link whose publisher pid
no longer exists is an abandoned publication of an already fsynced sidecar:
withdraw the orphan and adopt the sidecar instead of rejecting the epoch
forever. A live publisher's staging link is still never yanked. Adds the
@internal catalogStagingSettleDeadlineMs seam for deterministic tests.

* fix(playground): fsync the catalog directory after withdrawing an abandoned staging link

* fix(dev): forward request _meta (progress token) through McpSession and the dev matrix client

Lifecycle fixtures pass their generated progressToken as params._meta; the
session adapter and McpSession.callTool dropped it, so generated routes never
enabled sendProgress and every progress-gated lifecycle fixture failed the dev
matrix. McpSessionToolCallOptions and McpClient.callTool now carry _meta.

* fix(dev,playground): restart the adoption drain after a handoff race; keep the staging link when a sidecar rollback fails

- EpochAdoptionPolicy reschedules its drain from the completion handler when a
  candidate arrived between the loop's last empty check and #processing being
  cleared, and settled() waits through restarts
- #persistSnapshot releases the staging link after a failed publication only
  once the owned sidecar is confirmed withdrawn, so a rollback failure never
  leaves a singly linked sidecar for readers to adopt

* ci: retrigger checks for the rebased head

* chore: drop the portable byte-lane changes superseded by #406; keep the _meta assertions

* fix(playground): restore the staging guard when a recovery fsync fails

* fix(playground): accept a concurrently restored staging guard (EEXIST aliasing the sidecar) during recovery

* fix(dev): recheck supersession after the adoption lease settles

* fix(playground): keep a fresh pid-owned guard when recovery can neither re-link nor withdraw the sidecar

* fix(playground): fsync every compensating recovery guard before trusting it
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