Skip to content

integration: verify the reviewed November 2 issue fixes together - #557

Merged
T-Auto merged 86 commits into
mainfrom
work/reviewed-integration-20261102
Nov 2, 2026
Merged

T-Auto merged 86 commits into
mainfrom
work/reviewed-integration-20261102

Conversation

@Haoming9527

@Haoming9527 Haoming9527 commented Nov 1, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Review order and deferred validation: #556.

Combined integration of the individually reviewable November 2 fixes. No GitHub PR has been merged. Review the source slices in #556 before merging this combined tree.

main 065cd7f
  + review/install/runtime diagnostics
  + scoped publication + Bash ownership
  + file contracts/cache/Windows DACL/bounded reads
  + opt-in download guard + conditional wait guidance
  + evidence/design documents + contributor corpus benchmark
  = one conflict-resolved tree for combined verification

Preserves all 22 reviewed source heads from #503, #520, #538–#552, #554, #558, #559, #561, #562 and #572 except the separate release/0.4.4 PR #546. Independent PRs remain the review units. The alpha.2 compatibility investigation #555 and later alternative contributor PRs #563–#571 are excluded; their scope comparisons and review findings are linked in #556.

Additive changelog conflicts retain both entries. The benchmark/publication dependency conflict retains the documented rc2 development graph and matching timeout-policy, alongside existing isolated rc5 evidence; it keeps the download-guard exports and all reviewed implementation changes. The search merge retains both profiling and WSL Windows-path restrictions. No compatibility range is widened.

Evidence

  • Before: independently tested branches overlap in changelog and host test dependencies, so sequential merging requires explicit resolution.
    After: a clean combined tree preserves each contributor head and the intended behavior of both sides.
  • Each included implementation PR has focused regression evidence in its own description. Current exact-head job checks, rather than workflow rollups, are the CI reference.
  • Head 8d0f503: all nine executed CI jobs passed, including Windows, Linux, both macOS architectures and the Node floor; the optional benchmark job was skipped (run).
  • Combined head 3ff70c2 includes those follow-ups and the npm 12 release fix; all nine executed CI jobs pass (run), with only the optional benchmark skipped. Fresh native/IPC builds, source-size and 86 manifest checks pass. Ten focused suites (55 checks) pass across the new CLI/build/search/security/packaging changes.
  • Head d81fa602 includes WSL and the capability inventory; all nine executed CI jobs pass (run). Fresh build/typecheck/native staging pass; 32 Rust tests pass with one existing UNC test ignored locally, and seven file/security/search suites pass 45 checks. The final manifest/docs run passes 21 checks across two suites; manifest validation passes 89 checks.
  • Final head 0bb2230 changes only the WSL acceptance script and bilingual documentation. It preserves fix(cache): avoid workspace preparation in full-access sessions #544's intentional full-access cache refusal on legacy hosts: when the cache-only capability is absent, delegated fallback must have no storage authority and exactly match the opt-out Windows bounded result. When that capability exists, a missing reference remains a failure. The original failed assertion and captured absent-storage envelopes are retained as evidence of the corrected fixture assumption, not a production capture regression. All five real Windows cache-contract checks pass, including private ACL spill/paging/cleanup.
  • Actual Windows + Ubuntu 26.04.1 WSL acceptance on final 0bb2230: workspace-write 12/12 and danger-full-access 12/12, zero skipped/failed, on the locked rc.2 graph. Both observe the Linux worker PID/start time and its absence after cancellation. Windows payload SHA-256 3d97ad84dc57dc464e2e231580b9fdfb6f698b59586a7ad76a71b165f5fce17f; the Linux payload comes from the successful d81fa602 integration CI, whose entire production/Rust/dependency tree is unchanged by the final four-file test/docs follow-up. Final-head CI: all nine executed jobs passed, including Windows and both macOS architectures; only optional bench-smoke skipped. Actual job conclusions were inspected, including the Windows job that permits continue-on-error.
  • The earlier full local Windows gate recorded 187 passing suites, four failures and four permitted platform/external skips. Two failures were the npm 12 pack-envelope bug, now fixed in fix(release): support npm 12 package-keyed pack reports #561 and retested against actual packing and hash/provenance assertions. Two large fixtures exceeded the default 120-second limit; with an explicit 600-second local budget and no retries, Bash IPC passes all eight checks in 176 seconds and permissions passes all 15 checks in 187 seconds. The original red gate is retained.
  • The local system tar rejected Unicode filenames. GNU tar 1.35 extracted the same artifact successfully; its Unicode document matches commit 8d0f503, and the packed native runtime closure passes. No system configuration was changed and no damaged artifact was accepted as a full extraction.
  • The PR is reviewable; no merge or release has occurred. Protected and deferred issues stay open.

Merge Danger

Door: two-way

This branch contains reviewed slices rather than a new combined feature design. Review each linked PR before accepting the integration, and rerun checks if main or a contributing head changes. The development graph resolution needs the combined benchmark and publication checks.

Blast Radius: integration

File permissions, publication lifetimes, shell diagnostics and optional prompt guidance interact here. #546 remains a separate release branch; unsupported alpha.2 work is deliberately not included.

OldLigant and others added 30 commits October 30, 2026 20:42
Complete the evidence layer #103 started: #103 compared two worker binaries on
disposable synthetic fixtures, so it could not see host dispatch, the official tools,
or a real source/runtime tree. This adds `node bench/cli.mjs calls`, which measures
official `grep`/`glob` versus OPS, or two OPS builds in one process, over a real corpus.

Bench-only: lib/ and native/ are untouched. Each corpus manifest declares a shape range
that the runner checks before it measures, and records a content identity and per-arm
coverage (scanned files, truncation, matched observed/total, displayed items). Reports are
created exclusively and never overwrite evidence. Timings are manual evidence and never
gate CI.

Four corpora ship as recipes rather than a downloader: a pinned harness client checkout,
an installed CPython stdlib, a rustup toolchain, and the checkout's own installed
@deepseek-ai packages. The in-budget corpora carry eight cases, including the #384
exact/bounded pair at limits 5 and 50; the over-budget rust toolchain carries the coverage
cases only, because a full count never completes there. Automatic materialization and the
independent npm fixture stay out of this change; the operator prepares the tree.

Validation: 22 bench suites pass, check-file-size (476 files, all <= 1000 lines),
validate-manifest (83 checks), and the browser/settings suites still pass after rebuild.
The shipped bundle mounts one `dsh-ops` row and retires the legacy per-component rows
(`file`, `shell`, `ptc`); mounting `lib/components/file/index.js` directly therefore became a
no-op, and every candidate call failed as `unknown tool "ops_<name>"` — the deterministic
tests never boot a host, so they could not see it.

The harness now opts into the plugin's documented verification path
(`DSH_OPS_LEGACY_ROWS=1`) before it imports those rows. Shipped behaviour is unchanged: the
variable only affects a process that mounts the retired rows itself.

Verified on main@3882e69 with the real host: python-stdlib glob-name candidate 494.1 ms
(scanned 4120, complete) and a ten-repo workspace glob-ts candidate 751.6 ms (scanned 10,000,
scan-budget), with no errors; 22 bench suites still pass.
Every publication hook re-assembles after next() when it retires its own
tools, and that rebuild re-enters the same waterfall. When two hooks both
wanted to withdraw in one request they re-assembled in turns, so the request
never settled - the spin behind an idle Host at 94% CPU reported in #445/#509.

claimPresentationRebuild() in publication/presentation.ts gives the first
withdrawal in a request its rebuild and tells every later hook to keep the
assembly it already holds. The three publication hooks (ptc, native,
discovery controller) use it instead of assembling unconditionally.

The session shell hook no longer waits through reconciles queued after its own
assembly started, and answers an inspection instead of waiting for a
publication an inspection cannot change.

Tests: test/presentation-rebuild.test.mjs covers the per-request budget and
that two withdrawing hooks settle at two passes each.
… probe

A host that emits `tools/change` without a scope payload (the official
0.2.1-rc.4 contract is deliberately unfiltered) made every live session
re-probe, each probe costing a full prompt assembly. At 198 live sessions
this measured 23,143ms and 1,393 assemblies per session creation.

Two changes:
- publication/changes.ts: read the scope payload newer hosts attach,
  falling back to our own `withToolChange` window when the payload is
  empty or absent. An empty payload must not override our attribution
  with "global", or our own publications would be misread as global. The
  callback also learns who spoke: `'host'` when the payload named a
  scope, `'local'` when our own window supplied it. A local declaration
  is authoritative about changing something unrelated to any tracked
  session, while an unrecognized host scope is host state this row has
  not admitted yet and must stay conservative.
- discovery/controller.ts: on an unattributed change, probe one
  representative session (satisfying the probe-memo "at least once"
  contract) and lazily invalidate the rest. `system-prompt/change` takes
  the same path, since a prompt change moves presentation only.
  Publication-set changes (modes, runtime, configuration) keep using
  `refresh`, which must visit every entry to withdraw stale
  registrations.

Three details carry the difference between "one probe" and "one probe
that is actually useful":
- The representative is chosen through the same `coordinable` predicate
  the reconcile loop uses, so the "at least one probe" contract cannot
  silently degrade to zero probes, including on an external-only mount
  where no sandbox policy is injected.
- `reconcile` walks the representative's whole scope family, and it does
  so on a later microtask. Invalidate the siblings before that walk and
  the family is re-probed member by member, the per-session cost this
  change exists to remove, one family at a time. The siblings therefore
  go stale only after the representative's probe has run.
- A named scope that sits on no tracked session's chain still moved the
  registry, so a host-attributed change with no matching session falls
  back to the conservative path instead of being dropped.

Refs #490
The pinned packages move to 0.2.1-rc.5 and Cordis to 4.0.5. Three host
changes reach this plugin: ToolPresentationMode drops `both`, so every
presentation list and loader description names only `native` and `ptc`;
ConfigFormSnapshot drops `mode`, so a Client form is editable when it is
ready and writable; and run_code asks the host for the directory it
executes in, which the test hosts provide only where a suite runs code.
The test fixtures follow rc.5 as well: invoke names its PeerScope, a user
message carries its source, and the gateway client is resolved through
its condition exports.
A restricted (read-only / workspace-write) ops_bash call that cannot prepare the
private Bash runtime used to answer with one parameterless catch: the caller was
told the runtime was "unavailable or incompatible" and told "Do not retry",
whatever the actual stage was - a missing payload, an IPC receipt mismatch, an
interrupted staging copy, or a lifecycle lock left by a dead host. The staging
path is recoverable by design (verify -> quarantine -> stage again), so the
blanket advice was wrong for exactly the failures another call repairs.

- Add src/bash-runtime-failure.ts: named stages plus the retry and next-action
  advice rendered for the model.
- Name the stage at every preparation failure in bash-ipc.ts and keep the
  message (stage + reason + remedy) through shells/session-execution.ts.
- Report the recorded lock owner and the recovery command when a lifecycle lock
  is left behind.
- Reclaim interrupted stage-* copies older than one hour, count them in the
  reported reclaimable bytes, and remove them all with a confirmed
  bash-runtime clean-cache.
- Document the symptom, diagnosis and recovery in both languages, and add
  regression tests: reverting the session layer turns the integration test red.

This changes diagnostics and cache lifecycle only: no payload byte, no tool
schema, no approval path and no hashed artefact changes.
- docs: name only native and ptc (native-ptc, ptc-cost, ptc-sdk-loading,
  result-budgets, discovery and their Chinese twins); drop the hybrid row
- diagnostics: stop suggesting the removed Native/both presentation
- test: replace presentAs('both')/mint('both') with real presentations;
  retire the discovery-incremental case that needed a mixed scope
- settings-ui: drop the dead mode fixture field and assert that a ready
  document refusing writes stays uneditable
- fixtures: share workingDirectoryOver and ownerPeer, resolve both client
  entries through package condition exports, document withWorkingDirectory
- deps: remove the unreferenced dsh-working-directory devDependency
…ery session

A bare tools/change cannot move a presentation mode: the collapsed section
text and the wire projection depend on the mode alone. Only a
system-prompt/change since the last re-derivation can have flipped one, so
that is what spends the probe now; a probe a superseded publish still owes is
spent rather than dropped, and the assembly guard publishes before a PTC-only
prompt is served. One unattributed change over 198 live sessions drops from
198 assemblies to 0.

The republish itself is never skipped. A restriction or a scoped shadow moves
what a Native session sees while the probed surface stays identical, so the
queued request still drives the publication and only the verdict is carried
over for the next real probe at pre-step.

Refs #490
Three scripts that measure the session-creation cost the issue reports, so the
numbers in the issue can be re-derived rather than trusted.

`probe-490-curve.mjs` measures assemblies per event against the live session
count, with `create-only` as the control that separates creation cost from the
change that follows it. `probe-490-epoch.mjs` answers which host action emits
`system-prompt/change` — the fact the PTC deferral rests on. `probe-490-surface.mjs`
records a ruled-out hypothesis: the per-session surface read is about 0.03ms, so
the remaining cost is not there.

They sit in `bench/probes/` because `bench/` keeps only CLI entry points at its
root and groups everything else by purpose. Not part of the default gate.
…bling

A bare registration cannot move a presentation mode, and a scope whose registry
mode already resolves to `native` renders the collapsed section empty. Reading
that mode from the scope chain is the same source the section itself reads, so
the verdict it can return is the one already held: the assembly is skipped
instead of paid.

On a 12-agent Native fleet one unattributed `tools/change` drops from 12
assemblies to 0; a single flip to PTC costs 1 instead of 23.
Merge fix/490-upstream, fix/490-ptc-lazy-verdict and fix/490-provider-scan
into a single change set so the maintainers can review one ordering instead
of three branches that rewrite the same files.

The PTC side takes the provider-scan branch's later state, which already
contains the lazy-verdict deferral, and regains the registry mode precheck the
merge would otherwise have dropped: a scope whose `modeFor` is not `ptc`
answers from the scope chain instead of assembling. The discovery side keeps
the upstream bounded representative probe, whose change-attribution file now
also carries the host-payload origin that both later branches rely on.

The lazy-invalidation assertion gains the PTC claim: in a PTC deployment the
discovery component answers from an exact PTC surface claim, so an unattributed
change assembles nothing at all rather than one representative.
The deleted suite appended the PTC section from its own assemble hook, so its
assertion measured the injection rather than the lazy invalidation it named.
The behaviour it claimed to cover is already locked by the representative-probe
suites, which fail when the boundary is removed.

Also delete the probe option the host fixture never accepted, and say where a
lazily invalidated scope re-observes its presentation: agent/pre-step and tool
execution check freshly regardless of cached state.
…6a2-8c35-9c371a16--rc5-compat-integrated

# Conflicts:
#	docs/CHANGELOG.md
…ooks

The #524 report filed a pnpm `ERR_PNPM_UNKNOWN` against this plugin, but the
failure is pnpm failing to `open` a file while unpacking a *different* plugin
from a Git tarball into its own store; no dsh-ops code path calls a package
manager. The part of that report this repository does own is the Git channel:
`git+https` cannot produce a loadable plugin, because `lib/` and `runtime/`
are gitignored and `prepack` never stages the native worker.

- Document the unmapped Windows `open` error as a local store/volume condition,
  in both languages, with the store-prune, security-software and store-dir steps
  a reader can execute, and the instruction not to relax supply-chain checks.
- State the install-channel contract in usage/README/CONTRIBUTING: the registry
  tarball is the only channel; a source checkout needs `npm ci && npm run build
  && npm run build:ptc && npm pack`.
- Refuse `prepare` and `postinstall` in `scripts/validate-manifest.mjs`, so
  "fix the Git channel with `prepare`" cannot land silently: `prepare` would
  compile `lib/` for every Git install and still leave `runtime/` empty.
- Prove both facts: a new `install-channel` suite runs the shipped gate against
  a staged package and fails when a hook is injected, and `docs-facts` requires
  the bilingual troubleshooting entry.
The gate refuses every `(pre|post)?(prepare|install)` script, but its label
named only `prepare`/`postinstall`, so an author adding an unrelated
`preinstall` would read a rule that does not match what fails. The failure
text also pointed at a step in an uncommitted internal plan, leaving a
reference that resolves nowhere outside the author's tree.

Name the rule after its actual scope, keep only the public `docs/upgrading.md`
reference plus the fact, and align the English and Chinese install-channel
docs and the changelog with the broader contract. The suite now asserts an
unrelated `preinstall` is refused too, so the wording cannot narrow again
without failing.
A component that registers or restricts while `system-prompt/assemble` runs —
issue #526's dsh-better-goal shape — made discovery treat its own probe's side
effect as an external change. The probe emitted `tools/change`, the listener
invalidated every entry and reconciled again, and the next probe emitted again:
one external activation became 3001 prompt assemblies and 3000
`tools.restrict()` records in 1.1 s, with RSS climbing from 77 to 156 MB while
the host was still booting.

Attribute a notification to the probe that caused it instead of widening the
`mutating` window, which cannot bound an asynchronous third-party handler:

- Track probe depth per asynchronous flow in `publication/presentation.ts`
  (`AsyncLocalStorage`), so an unrelated change during a slow assembly is still
  external and a nested assembly stays attributed to its outermost probe. A
  process-wide counter regresses `probe-isolation`'s late-probe cases.
- In `discovery/controller.ts`, count changes observed inside a probe and apply
  them at most once, after the probe returns, as a trailing reconciliation. A
  genuine external change re-arms that budget; a component that keeps mutating
  during assembly has its trailing change dropped and is named once in a
  warning, because reassembling to observe it is the loop itself.

`test/discovery-feedback-loop.test.mjs` pins the observable behaviour: one
external change costs one traversal (two with a one-off in-assembly change), a
real presentation change still withdraws and restores the surface, and 50
activations stay inside their per-activation budget. Reverting the attribution
turns three of its five checks red.

Closes #526
…iewed-integration-20261102

# Conflicts:
#	docs/CHANGELOG.md
…iewed-integration-20261102

# Conflicts:
#	docs/CHANGELOG.md
…iewed-integration-20261102

# Conflicts:
#	docs/CHANGELOG.md
…iewed-integration-20261102

# Conflicts:
#	docs/CHANGELOG.md
#	native/crates/ops-search/src/scan.rs
…iewed-integration-20261102

# Conflicts:
#	docs/CHANGELOG.md
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.

8 participants