Skip to content

fix(session): isolate stalled presentation probes by scope family - #405

Merged
T-Auto merged 19 commits into
mainfrom
fix/isolated-ptc-reconciliation
Oct 25, 2026
Merged

T-Auto merged 19 commits into
mainfrom
fix/isolated-ptc-reconciliation

Conversation

@Haoming9527

@Haoming9527 Haoming9527 commented Oct 24, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #359. Refs #346. Depends on #383 and #390 (their commits are included; merge those first to narrow this diff).

What was wrong

PTC and discovery each put every tracked agent's presentation probe on one shared promise chain. A real system-prompt/assemble hook that never resolved for one root blocked creation and prompt assembly in a second root, and unload waited on that unanswered hook. Memoization reduced probe cost but did not remove this dependency.

The change

Run probe work independently for each inheritance family. Ancestor/descendant overlays still publish atomically; unrelated roots have separate readiness and execution barriers. Supersession, agent disposal and component unload cancel the obsolete OPS wait even when a host hook ignores its signal. Late responses cannot commit registrations or run an old assembly's withdrawal guard against a newer publication.

Discovery uses a synchronous registration helper for its already-known exact agent scope, keeping the whole family commit synchronous. Mutable session/policy input checks, attributed invalidation, cached verdicts, ownership precedence and live invocation checks remain intact. Fresh pre-step and callback probes forward/check cancellation before consuming or adding selections.

This proves the shared-queue failure class, not reproduction of the original 36-plugin Desktop freeze. An uncooperative hook can still delay its own inheritance family, or its own direct turn/call probe; this change adds no timeout reset and does not manufacture a successful presentation verdict. The repository does not own another plugin's underlying promise. No new dependencies, schemas, argument contracts or runtime binaries changed.

The follow-up itself adds fewer than 200 implementation lines. Base is main so the repository's existing pull_request branch filter runs; #383/#390 are preserved without rewriting their history.

Validation

  • New real-host suite: 12/12 checks passed, 0 skips. It tests PTC and discovery separately and together; unrelated root creation/assembly/authority/disposal; unload while the host ignores cancellation; stale configuration results; stale withdrawal guards; actual parent-child family dependencies; and aborted fresh probes.
  • Source red/green against edf70d8: all eight initial isolation checks failed against the old implementation. Removing only the cancelled-waterfall guards failed exactly the two stale-withdrawal checks; restoration passed. Four further parent-child/direct-cancellation checks were added after independent review, giving twelve passing checks.
  • Final build/typecheck and nine focused discovery, PTC presentation, scope and incremental suites passed (26 seconds), including the twelve isolation checks; zero skips. Prior focused lifecycle verification also passed. No assertion retries were used.
  • Build/typecheck, source-size and diff checks passed. Paired English/Chinese SDK docs and the Unreleased changelog describe the scope and remaining Desktop evidence boundary.
  • Current-head PR CI at cb162e5 passed every executed check, including Windows, Linux, macOS, Node-floor and CI required. Conditional benchmark smoke was skipped as designed.

Environment

Windows x64, Node v24.19.0, real locked DSH host packages. Based on #390 edf70d8, including #383 and main 662a388. Native source is unchanged from main; staged worker SHA-256 d0df8353a57ecda0abe13dc55989012baa61f11bcb484dc26550935a070a379c. Publication-only regressions point at the current Node executable and assert that no worker is spawned. No provider/model request or Desktop recovery is claimed.

ccch1mneyyy and others added 19 commits October 21, 2026 03:31
PTC and discovery assembled the whole prompt once per tracked agent on
every reconcile, so creating one session paid for every session still
tracked. 38 bundles on Windows x64 grew 264/308/396/475/590/681/748/947
ms across eight session/create calls; with this change the same eight
rounds measure 195/226/270/260/295/329/358/373 ms.

Each agent now memoizes its presentation verdict against the tool names
it publishes and re-probes only when that set changes. reconcile() takes
the agents a change actually touches, and the publish-time snapshot reads
names instead of rebuilding a scope view per tool.

Refs #333, #359
`reconcile` advanced `epoch` on every call, so the passes the new `targets`
argument turns into no-ops still invalidated every published `execute` guard and
every Native shadow. Those then answered POLICY_DENIED for a scope that was still
authoritative, which is why `discovery-composition` lost its combined Native/PTC
checks and `ptc` lost its presentation-change race.

A probe also recorded its surface on the entry before the republish that consumes
it ran. A pass a later reconcile superseded therefore left the next pass reading
that surface as already published, returning early forever; the PTC verdict never
took effect and `native-result` timed out waiting for the Native/PTC projections.

A separate `published` counter now moves only when a pass actually republishes.
`ptc`'s shadows and `discovery`'s guards compare it, because `discovery`'s probe
assembles the prompt and that reconciles other scopes. Probed surfaces stay
pending until the republish commits them.

Verified: `npm run typecheck`, `npm run verify` — 125 of 127 suites pass, the two
remaining are the local-only failures `bash-ipc` and `permissions`, which need the
pinned runtime receipts this checkout does not build and fail identically on
b963fb2.
…napshot

A departure passed an empty target set, which the pass normalized to "no restriction" and then probed every tracked agent on the one pass that has nothing to probe. An empty set is now a probe-free pass: it names nobody to probe while still asking for the republish a departure owes.

The per-publish snapshot now reads tool names through one src/surface.ts helper instead of an identical surfaceOf in each plugin, and both memo keys document that names are the whole key while the presentation verdict also reads the tools:ptc-only section text, so a flip without a name change costs one pass and never a permanent wrong surface.
`surfaceOf` asked `tools.schemas()` for the names it memoizes, and that
projector rebuilds one deep copy of every visible parameter schema per call.
Read the same visible map through `tools.view(scope)` instead, keeping
`schemas()` as a fallback for a runtime that exposes no view, and pair each
name with a `WeakMap` identity for the definition the view holds. A names-only
key returns a stale hit when a scoped registration shadows a global of the same
name, so missing the shadow publishes nothing on the change that owed a
republish. Identity costs one lookup against an object already in hand rather
than the `tools.get()` pairing that would rebuild the whole view per name.
visibleTools (the tooling:shell/file prompt section and the host-shell
denial gate) asked tools.schemas() and kept only the names, paying one
deep copy of every visible definition per assembly: 34.2 ms for 258
tools at the issue's own surface shape on the published 0.2.1-rc.2
runtime, against 0.043 ms through tools.view() (~800x). Cost tracks
nested schema nodes, not payload bytes (811 KB flat = 16.5 ms; 705 KB
nested = 34.2 ms), so the fixture is pinned to the issue's measurement.

visibleNames() reads view().keys() with schemas() as the fallback for a
host whose view is not reachable, so correctness outranks the cost there.

The discovery pre-step assembly is deliberately left alone: it is the
self-correction path for a presentation that flips without the names
moving, and memoizing it there breaks the ptc-sdk projections (#369).

Tests assert the two review gaps as well: a tools/change with no registry
movement reconciles from the discovery probe memo without assembling, and
the denial gate restricts without projecting schemas. Both fail when
reverted.

Stacked on #368: surface.ts and the view machinery come from there.
@Haoming9527

Copy link
Copy Markdown
Collaborator Author

Combined compatibility has now been checked on work/reviewed-integration-20261025, commit 2e261035f88780015a7e132d36e7614e6cf4a8de, against main 662a388.

It preserves #344, #381, #383 and #388–#405, with prerequisite ancestry reused. Draft/NO-MERGE #362 is excluded. Competing changelog additions were preserved together; the only workflow conflict was a comment. Source changes combined without textual conflicts. No GitHub PR was merged.

Integration workflow 40115118596 passed all twelve jobs, including Windows, Linux, macOS arm64, macOS Intel, Node-floor and all three optional model-free benchmark smoke legs. Local combined verification also passed build/typecheck, 89 workflow checks, 133 manifest checks, the 426-file size gate, 23 benchmark/session/review/manifest suites and five metadata/settings/policy/presentation suites. The local catalogue suite skipped for a missing integration worker and is not counted as a local pass; the complete platform CI gate supplies the runtime coverage.

Dependency review order remains #383 → #390 → #405, #391 → #396, and #381 → #399 → #402 → #404. #404 also contains #401's all-target CI change and targets #402's branch to keep its own diff small. The integration branch retains the cross-PR conflict resolutions for review.

#261, #207, #303 and #360 remain open under the requested exceptions. Administrator enforcement, Desktop validation and the controlled #360 model experiment are not inferred from these passing gates.

Thatgfsj
Thatgfsj previously approved these changes Oct 24, 2026

@Thatgfsj Thatgfsj left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

复审无误,批准合并

@Haoming9527
Haoming9527 dismissed Thatgfsj’s stale review October 25, 2026 03:29

The merge-base changed after approval.

@T-Auto
T-Auto merged commit edf367f into main Oct 25, 2026
9 checks passed
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.

[BUG] 0.4.1:ptc 的全局串行链 + 强制 reconcile 使所有会话在提示词装配阶段同时卡死(33 插件 + 10 MCP)

5 participants