@junghanacs/entwurf 0.33.0 → 0.34.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -31,7 +31,7 @@ Ask: does this follow capability rather than a surface name; record backend asym
31
31
  3. **One identity contract, capability domains.** Branch on rail/liveness capability, not a privileged citizen class.
32
32
  4. **Live-fact dispatch.** Never store liveness or infer send/resume before resolving the record and probing its rail.
33
33
  5. **Rejects are real.** Wrong intent, dead/drifted identity, undeliverable mailbox, ambiguity, and indeterminate probe reject without mutation or fallback.
34
- 6. **MCP and native modules are explicit.** MCP uses only explicit `mcpServers`; native extension modules use operator-declared paths through the owning bootstrap. Neither discovers ambient configuration, scans extension directories, or retrieves modules automatically. Native modules do not emulate a foreign extension API.
34
+ 6. **MCP and native modules are explicit.** MCP uses only explicit `mcpServers`. Native extension modules enter only through the owning bootstrap, from an operator-named absolute file or an operator-named absolute directory read once at launch: top level only, exact suffix filter, deterministic order, every selected entry a regular file. Neither discovers ambient or default locations, takes module paths from settings or environment, recurses, watches, or retrieves modules automatically. Native modules do not emulate a foreign extension API.
35
35
  7. **One meta-store contract.** Record body and filename agree; active records are readable regular non-symlink V3 files; `nativeSessionId` is unique. Writers and doctor certify the active store before writing. Address-bearing reads use the addressable reader; `readStoreRecordFile`/`O_NOFOLLOW` and lstat policy both remain load-bearing. Quiesce and fresh-cut an unreadable generation; redeploy a stale unknown-backend reader. Store protocol and gates: `meta-session.ts`, `check-meta-*`, `check-fresh-cut-gate`.
36
36
  8. **GC reclaims processes, not memory.** Preserve or archive records and transcripts; do not casually delete them.
37
37
  9. **Not a second harness.** No prompt reconstruction, transcript hydration, tool-result ledger, credential mediation, or harness emulation. Backends own auth and transcript.
@@ -88,13 +88,13 @@ A new contract's test goes beside the behavior it certifies — `pi-extensions/*
88
88
 
89
89
  ```text
90
90
  implement → affected focused gates → independent review → one amendment bundle
91
- → qualification once if gate/mutant/matrix changed → full floor once on frozen candidate → commit
91
+ → affected qualification PARTIAL once if gate/mutant/matrix changed → full floor once on frozen candidate → commit
92
92
  ```
93
93
 
94
- - GLG-requested branch checkpoint commits keep completed subject/proof bundles small: scoped review, affected focused checks and normal hooks/shared scanner before each checkpoint, with known RED/unmeasured boundaries recorded. A checkpoint is not frozen-candidate acceptance. The sequence above owns final candidate acceptance: required changed qualification, frozen FULL and native/LIVE/release floors remain mandatory before landing/shipment, never inherited from an interim commit.
94
+ - GLG-requested branch checkpoint commits keep completed subject/proof bundles small: scoped review, affected focused checks and normal hooks/shared scanner before each checkpoint, with known RED/unmeasured boundaries recorded. A checkpoint is not frozen-candidate acceptance. The sequence above owns final candidate acceptance: the affected qualification PARTIAL, frozen FULL and native/LIVE/release floors remain mandatory before landing, and the prepared SHA's CI qualification body before shipment, never inherited from an interim commit.
95
95
  - Inner loop runs only affected gates; review and its corrections finish before the full floor.
96
96
  - A changed contract names a production subject and independent oracle; its focused assertion has a stable QK and exact-once mutant. Matrices change by their declared axes/cells, not appended anecdotes.
97
- - Qualification body is scheduled once when its surface changes; its manifest/head checks remain in the deterministic floor. A release requires the exact-SHA qualification evidence required by VERIFY, never a nearby green run.
97
+ - Development never runs the whole qualification body. A changed gate/mutant/matrix runs its affected exact-argv groups once, after review and amendment, as PARTIAL receipts (`./run.sh check-gate-qualification --group '<argv JSON>' --receipt <absolute path outside the repo>`); the manifest head stays in the deterministic floor. The whole body runs once per cut, in the prepared SHA's CI: a prepared state awaits it and is never presumed PASS, and a release requires the exact-SHA evidence VERIFY names, never a nearby green run or a PARTIAL.
98
98
  - Freeze the candidate while the full floor runs; any worktree/index movement voids that receipt. Pre-commit is not the full floor.
99
99
  - LIVE/release floors are not lowered by a shorter inner loop. Failed gate or evidence downgrade blocks the relevant commit/release decision.
100
100
 
package/CHANGELOG.md CHANGED
@@ -4,6 +4,168 @@ All notable changes to this project will be documented here. Format follows [Kee
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.34.1 - 2026-10-09
8
+
9
+ ### Release coordination
10
+
11
+ - **A patch cut for #135.** Coordinated by the pi-durable citizen `20261008T113104-801621` and
12
+ implemented by Claude Code Opus as sole writer, under the maintainer's grant through prepare and
13
+ make; npm publication is not part of that grant. This section is written at prepare time, before
14
+ the release gates run: the `check:full`, package-consumer and `release-gate --cut` results are
15
+ recorded in the operator handoff and the GitHub release, not claimed here. Their raw logs stay
16
+ under the ignored `.tmp-verify/135-install-ownership/writer/release-0341/{p4,p6,p5}/`.
17
+ - **The published 0.34.0 does not carry these fixes.** Its registry bytes stay as accepted.
18
+
19
+ ### Herdr plugin
20
+
21
+ - **Component owners are asked before anything is fetched (#135).** The plugin build now runs the
22
+ selected components' read-only owner preflights — Pi package, Pi provider, and the Claude
23
+ meta-bridge plan — before runtime acquisition, on the same Pi state paths the activation reads. A
24
+ foreign Pi owner refuses with no runtime, journal, cache or ledger written, and the refusal says
25
+ to release the registration from the root that owns it (its own `remove-user-scope`): a takeover
26
+ from the plugin's root is not a route. The check is planned against the physical prospective
27
+ runtime root; a landed package whose name or root differs from that plan refuses as
28
+ `herdr-build-artifact-drifted`. The installed activation keeps its own preflight recheck.
29
+ - **`herdr-plugin-deactivate` without a ledger no longer reports success over leftovers.** With no
30
+ certified activation ledger it exits 0 only when nothing stands at the plugin's journal, runtime
31
+ and cache addresses; otherwise it names what it found and exits non-zero
32
+ (`deactivate-unattributed-leftovers`), removing nothing.
33
+ - **Only a regular file or nothing may stand at the activation ledger address.** A symlink,
34
+ dangling or not, or a directory at `activation.json` refuses as `activation-ledger-uncertified`.
35
+ - **Limits that stay.** The Claude install state records the last installer and is not root-bound.
36
+ Roots changed between runs, or a host changing between the early owner check and the installed
37
+ recheck, can still refuse after the download. The no-ledger deactivate reports leftovers and
38
+ neither reclaims nor reuses them. No wider Claude-root or ownership guarantee is added.
39
+ `docs/setup-clean-host.md` §1.1 names the four installation paths and these limits.
40
+ - **The runtime lock rides the `herdr-checkout` verification carrier for this window.**
41
+ `plugins/herdr/runtime-lock.json` returns from npm 0.34.0 to the closed `herdr-checkout` source,
42
+ so the lock stays coherent with the 0.34.1 package version without inventing a registry
43
+ integrity. The plugin manifest stays 0.34.0, and this is not a plugin pair: the npm re-pin to
44
+ 0.34.1 follows publication with its measured sha512 and its own source proof. Moving a host
45
+ between the published npm 0.34.0 plugin and this carrier, in either direction, needs
46
+ `herdr-plugin-deactivate` first.
47
+
48
+ ### Repository hooks
49
+
50
+ - **`prepare` no longer runs husky.** It only builds the bridge. The local
51
+ `core.hooksPath=.husky/_` override that shadowed the configured global guard was removed on this
52
+ checkout, and `.husky/pre-commit` (whitespace, lint, typecheck) is reached through a global guard
53
+ that chains to repository-local hooks. Another existing clone may keep its local value until it
54
+ is unset there (unmeasured). On this checkout a real `npm pack` inside `check-pack-install` left
55
+ `.git/config` and `.husky/_` unchanged. The `husky` devDependency is unchanged.
56
+
57
+ ## v2026.10.9-herdr.0.34.0 — Herdr Entwurf plugin 0.34.0
58
+
59
+ A Herdr plugin snapshot, not a core release: no package version, npm artifact or core tag moves.
60
+
61
+ ### Herdr plugin
62
+
63
+ - **Runtime re-pinned to the published 0.34.0 (#132 B).** `plugins/herdr/runtime-lock.json`
64
+ returns from the `herdr-checkout` verification carrier the 0.30.1 preparation rode to the npm
65
+ production source: `@junghanacs/entwurf@0.34.0` with the registry's measured sha512
66
+ (`sha512-rUpQ…/uJ/fw==`), which is the artifact accepted and published as 0.34.0. 0.31–0.33
67
+ were never pinned here. The manifest version moves 0.30.1 → 0.34.0; `min_herdr_version` stays
68
+ 0.9.0 and the measured Herdr supply stays 0.9.3.
69
+ - **Upgrading from a 0.30.1 plugin install needs an explicit deactivate.** That install recorded a
70
+ `herdr-checkout` runtime and ledger, and npm is a different acquisition source, which is refused
71
+ rather than inherited (`activation-artifact-source-drifted`). Run `entwurf
72
+ herdr-plugin-deactivate`, then `herdr plugin install` again. A host with no plugin installation
73
+ starts fresh.
74
+ - **The release skill's core baseline ignores Herdr snapshot tags.** `prepare` P1 took the newest
75
+ tag by version sort, and a `vYYYY.M.D-herdr.*` tag sorts above every `v0.x`, so it would have
76
+ become the next core audit baseline. P1 now excludes exactly that shape; a core `v20.1.0` is
77
+ still picked.
78
+ - **Plugin docs corrected before the tag (GLG's final review).**
79
+ - The direct route opens six fresh-call backends, `pi-durable` among them as the managed launcher.
80
+ The plugin's Herdr pair stays `{pi, claude-code}`, and the manifest now says it does not widen
81
+ with the core list.
82
+ - `smoke-herdr-plugin-build-live` is described as the checkout-carrier journey it is, not as
83
+ registry evidence.
84
+ - `herdr-plugin-deactivate` is given by its absolute runtime path, because the plugin puts nothing
85
+ on `PATH`.
86
+ - The plugin tag namespace is explained.
87
+ - A v1 record is now described as refused by the current inverse as well. The launch-rail doc
88
+ separates the `0.9.0` admission floor from the single Herdr version the gates certify (the
89
+ supply pin, 0.9.3).
90
+
91
+ ### Verification
92
+
93
+ - **Real-npm swap and torn-swap recovery, measured before commit.** This is the row the 0.30.0
94
+ pin left open. A one-shot driver ran the registry-shipped `scripts/herdr-runtime.mjs`, whose
95
+ sha256 equals this checkout's, against the real registry:
96
+ - A 0.33.0 → 0.34.0 swap.
97
+ - The same upgrade torn between its two renames (`torn-swap-with-candidate`), then resumed.
98
+ The last-good 0.33.0 tree was restored before 0.34.0 was acquired again.
99
+ - Both ended `runtime-ready` with observed digest
100
+ `sha256-e9d995ff3858ffca40fa12b4fce2a6db3802da3ee27c7b233142fc4c71f23450`, the accepted
101
+ artifact. Neither left a staging or backup directory behind.
102
+ - Each 0.34.0 runtime passed the installed-runtime check: three executable bins, the compiled
103
+ entry, and `check-bridge` with its exact eight verbs. The owned inverse removed cache,
104
+ runtime and journal.
105
+ - Two earlier runs stopped on defects in the driver's own verb parser, not on product behaviour.
106
+ - The focused Herdr gates (`check-herdr-plugin`, `-plugin-profile`, `-plugin-build`,
107
+ `-runtime-bootstrap`, `-activation`) and `check-gate-manifests` are green on this tree. No gate,
108
+ mutant or matrix changed, so no qualification PARTIAL is owed.
109
+ - The public-ref first-user path (`smoke-herdr-raw-install-live`) and the exact-SHA CI run on the
110
+ pushed commit. Their receipts are recorded in the GitHub release and #132, not claimed here.
111
+
112
+ ## 0.34.0 - 2026-10-09
113
+
114
+ ### Release coordination
115
+
116
+ - **One cut for #134 and #133.** Coordinated by the pi-durable citizen `20261008T113104-801621` and
117
+ implemented by Claude Code Opus under GLG's grant for prepare and make; npm publication is
118
+ coordinated with GLG separately. This section is written at prepare time: the prepared SHA's CI,
119
+ the preserved artifact and the tag are recorded in the GitHub release, not claimed here.
120
+ - **Verified for this cut.** `check:full` passed on the prepare tree (719 s). The first
121
+ `release-gate --cut` ended RED, MUST 23/1/0: `smoke-codex-fresh-live` timed out because Codex
122
+ 0.153.4 refused the stated Codex-side model `gpt-6-luna` for a ChatGPT account (vendor 400); that
123
+ run is preserved as RED. The rerun with the Codex model `gpt-5.6-terra` (Pi side unchanged) passed
124
+ MUST 24/0/0 and BEHAVIOR 1/0/0 in 1115 s, its own `check:full` 723 s. Before the commit,
125
+ `check-pack-install` (with the module-directory cell) and the `check-pi-durable-contact`/`-send`/
126
+ `-receive` gates against a bridge bundle emitted from the candidate passed, and an installed-package
127
+ fixture drove agent-config's background-bash module through a faux model: badge with the panel
128
+ hidden and at narrow sizes, report once, an interrupted task never re-run after `--continue`.
129
+ Vendor model calls there were 0 by construction; its token counts are faux accounting. Affected
130
+ qualification partials: 58/58 for #134 (earlier tree), 52/52 and 22/22 for #133. The whole
131
+ qualification body runs once, in the prepared SHA's CI.
132
+ - **Not measured, by GLG's decision for this cut.** GLG waived the agy shipped-lane criterion: the
133
+ three agy doctors passed, the first native-push probe of a fresh conversation was indeterminate,
134
+ and agy native-push stays NOT CERTIFIED. GLG deferred the badge on GLG's own visible terminal and
135
+ the new post-install Claude session of the meta-bridge criterion to a later session; the host's
136
+ dev-linked doctor passed with five live joins, and the meta-bridge writer is unchanged since
137
+ 0.33.0. None of these is a pass, and no 0.33.0 exception carries over.
138
+
139
+ ### pi-durable
140
+
141
+ - **Background-task badge (#134).** The carrier runtime observes the native task graph from open to
142
+ close; `/tasks` now toggles only the panel. The carrier TUI draws a kind-agnostic badge of live
143
+ background tasks (`node.background`) as the dock's first, never-shrunk row, naming the ones not
144
+ running and shortening down to one ASCII cell; a one-row terminal with live background work shows
145
+ the badge, and with none the screen is upstream's. The overlay patch now carries one section per
146
+ patched carrier file (`runtime.js`, `tui.js`), and the emitter refuses any other shape.
147
+ - **Module directory ingress (#134).** `--native-module-dir <absolute directory>` is the paired,
148
+ exclusive form of `--native-module`: one top-level read, `*.extension.mjs` non-dotfiles only,
149
+ byte-wise order, per-module guards, and named refusals for an empty directory, a non-regular
150
+ match, two names for one file and a cross-module extension or tool name. Hard Rule 6 now admits an
151
+ operator-named directory read once; ambient, default, recursive, watched and setting/environment
152
+ discovery stay forbidden.
153
+ - **Deferred:** #106 no-turn retirement moved to ROADMAP as GLG's deferral, not implemented.
154
+ #132's unfinished Herdr plugin alignment (npm re-pin, source proof, pi-durable under Herdr) moved
155
+ to ROADMAP, closed not-planned, to reopen against this release's published bytes.
156
+
157
+ ### Verification
158
+
159
+ - **Development runs no whole qualification body (#133).** `run.sh check-gate-qualification` now
160
+ hands its arguments to the runner's parser: `--group '<argv JSON>' --receipt <absolute path>`
161
+ runs one affected exact-argv group as a PARTIAL receipt, and a malformed, missing or repeated flag
162
+ is refused by name before any body or snapshot. Until now the wrapper dropped them, so a requested
163
+ PARTIAL started the whole body. Bare, it stays the whole body, which runs once per cut at the
164
+ prepared SHA's CI; a prepared state awaits it and is never presumed PASS. AGENTS, VERIFY and the
165
+ release skill retire the development rule of a local whole run when a gate, mutant or matrix
166
+ changes. Native/LIVE, the deterministic and package floors, the four required CI jobs and the
167
+ artifact acceptance are unchanged; P4 FULL and its CI repetition are not reduced here.
168
+
7
169
  ## 0.33.0 - 2026-10-08
8
170
 
9
171
  ### Release coordination
package/CONTRIBUTING.md CHANGED
@@ -30,7 +30,7 @@ pnpm check # everyday core (prints wall time; <=60s on the referenc
30
30
  pnpm run check:full # full deterministic floor — the required PR gate
31
31
  ```
32
32
 
33
- The deterministic floor is tiered (#70). `pnpm check` is the everyday core (biome, tsc, the vitest lanes, and the fast contract gates); `pnpm run check:full` adds the hermetic-integration and package/install tiers — including `check-gate-manifests`, the qualification HEAD (runner self-test, manifest-set validation, declared lane inventory; zero COMMITTED mutants executed — the self-test runs only its synthetic fixture mutants in tmp fixture repos — ~20s), but not the separately scheduled mutant-EXECUTING body `check-gate-qualification`, which CI runs on a branch push whose diff touched the qualification surface (and unconditionally on dispatch or the weekly schedule) and a gate-changing PR must run once itself. Exact membership is the named `check:*` scripts in `package.json`. Run `check:full` once on your frozen commit candidate — the pre-commit hook (`.husky/pre-commit`) carries only fast static checks (whitespace, lint, typecheck), not the full floor, so a green `pnpm run check:full` before commit is the evidence that your change holds (scheduling contract: AGENTS.md "Verification scheduling").
33
+ The deterministic floor is tiered (#70). `pnpm check` is the everyday core (biome, tsc, the vitest lanes, and the fast contract gates); `pnpm run check:full` adds the hermetic-integration and package/install tiers — including `check-gate-manifests`, the qualification HEAD (runner self-test, manifest-set validation, declared lane inventory; zero COMMITTED mutants executed — the self-test runs only its synthetic fixture mutants in tmp fixture repos — ~20s), but not the separately scheduled mutant-EXECUTING body `check-gate-qualification`, which CI runs on a branch push whose diff touched the qualification surface (and unconditionally on dispatch or the weekly schedule) and a gate-changing PR must run once itself. Exact membership is the named `check:*` scripts in `package.json`. Run `check:full` once on your frozen commit candidate — the pre-commit hook (`.husky/pre-commit`) carries only fast static checks (whitespace, lint, typecheck), not the full floor, so a green `pnpm run check:full` before commit is the evidence that your change holds (scheduling contract: AGENTS.md "Verification scheduling"). No install or build lifecycle script installs git hooks: a global `core.hooksPath` guard that chains to repository-local hooks runs its own checks, then delegates — to `.husky/pre-commit` here, unless an executable `.git/hooks/pre-commit` takes precedence.
34
34
 
35
35
  For changes that touch backend launch, session lifecycle, or `_meta` shape, also run
36
36
  the live ACP smokes that cover the touched rail — at minimum:
package/README.md CHANGED
@@ -27,10 +27,10 @@ Claude and Cortex Code are ACP backends. Codex and Antigravity stay native so th
27
27
  retain their own tools and work context. Capabilities differ by rail: a shipped
28
28
  citizen is not a promise of every lifecycle operation.
29
29
 
30
- > **Current release: [0.32.0](https://github.com/junghan0611/entwurf/releases/tag/v0.32.0)**
31
- > ([`80d66f4`](https://github.com/junghan0611/entwurf/commit/80d66f4ca66f0be674b2d2bfbe20c49717c0340e)).
32
- > Includes the pinned Pi 1.0.4 native durable app/TUI and one explicit native-module ingress.
33
- > npm publication and registry byte equality are [verified](https://github.com/junghan0611/entwurf/issues/130#issuecomment-6049936030).
30
+ > **Current release: [0.33.0](https://github.com/junghan0611/entwurf/releases/tag/v0.33.0)**
31
+ > ([`fea9b3d`](https://github.com/junghan0611/entwurf/commit/fea9b3dc4779ac5deb34e239abc8e0b5e3e95382)).
32
+ > Includes the pinned Pi 1.1.0 native durable app/TUI and one explicit native-module ingress;
33
+ > `npm` `latest` is `0.33.0`. What that cut ran and did not run is in its CHANGELOG section.
34
34
  > Release history and detailed evidence belong in [CHANGELOG](./CHANGELOG.md).
35
35
 
36
36
  ## Install
@@ -88,8 +88,11 @@ models/auth, queues, SQLite, transcripts and recovery remain upstream-owned:
88
88
  [Hard Rule 17](./AGENTS.md#hard-rules), **supply the missing surface; do not become the harness**.
89
89
 
90
90
  Optional `--native-module <absolute file>` initializes trusted ESM before runtime/TUI
91
- imports, then installs its default native Extension after the contact. No automatic
92
- module discovery or classic Pi API shim; guards are not a sandbox or rollback.
91
+ imports, then installs its default native Extension after the contact. After 0.33.0 (#134):
92
+ `--native-module-dir <absolute directory>` loads the top-level `*.extension.mjs` files of that
93
+ one named directory in byte-wise order instead, and the durable TUI keeps a live
94
+ background-task badge on screen at any terminal size. No automatic module discovery or
95
+ classic Pi API shim; guards are not a sandbox or rollback.
93
96
  Ordinary Pi's known `peerDependencies` warning reflects the separate native SDK supply.
94
97
  [Native setup](./docs/setup-clean-host.md#2b-optional-native-durable-pi-durable)
95
98
  owns flags, refusals and the extension contract.
package/VERIFY.md CHANGED
@@ -86,7 +86,7 @@ that moved out of `scripts/` and into a file beside its subject would otherwise
86
86
  ### The canonical floor — two entry points
87
87
 
88
88
  - **Deterministic floor — two tiers (#70):** `pnpm check` is the everyday CORE — toolchain (lint + typecheck), the vitest lanes, and the pure-unit / behavioral-contract / source-topology gates plus the cheap static coherence checks. It prints its own total wall time; acceptance is ≤60s on the reference host `oracle` (an operator measurement, never a hard wall-clock gate on arbitrary hosts). `pnpm run check:full` is the FULL deterministic floor — the core plus the hermetic-integration and package/install tiers — and is what the frozen-candidate protocol, push CI, release-gate, and `prepublishOnly` run. Exact membership is the named `check:*` group scripts in `package.json` (the executable SSOT — this document records meaning and principles, not the command list); a gate changes tier by semantic-class decision, never by getting faster or slower. One core member assumes a build output: the beside test `pi-extensions/entwurf-control.test.ts` (reached through `check-tests-beside-behavior`) registers the COMPILED bridge and refuses by name when it is absent. `prepare` normally builds it at install; after an `--ignore-scripts` install or a bridge source change, run `pnpm run build-bridge` (in the sandbox candidate when verifying one) before the core. The FULL tier carries the qualification HEAD (`check-gate-manifests`, through `check:hermetic`); the everyday core does not, and neither tier carries the separately scheduled mutant-executing body (`check-gate-qualification`).
89
- - **Discriminating power of that floor:** `./run.sh check-gate-qualification` (scheduled, not ambient: standalone when a lane changes a gate/mutant/matrix, in the CI `check` job on a branch push that touched the qualification surface, and — for a cut — at the prepared SHA, where the exact-SHA release oracle requires that CI step (#132: `release-gate` no longer runs a second, local BODY; this moves only the release-mode owner — the development rule of one local run when a gate/mutant/matrix changes stays in force until #133 retires it, and the 0.33.0 cut skips it only by a one-cut exception) — no longer inside the default check chains; a semver TAG push triggers no CI at all, and the exact-SHA evidence a release quotes is whichever run at that commit CARRIES the body — the branch run when the push touched the qualification surface, else a dispatch run; a push that touched none of the qualification surface skips the body, and `workflow_dispatch -f qualify=true` plus a weekly `schedule` run it unconditionally — the exact-SHA release oracle refuses a SHA whose body step did not conclude success) re-plants committed defect mutants (`scripts/mutants/*.json`, one per closed defect class) in an isolated snapshot repo and requires each to turn its gate red **bounded and at its claimed `[QK:<claim>]` signature** — a wrong-reason red fails, a baseline-red control voids the whole group, and the runner is negative-controlled on every run (zero-match/multi-match/survived/wrong-reason/hang/control-red/impurity). This measures whether the deterministic gates still *block* what they claim to block; it is **not a new evidence level** (L0–L5 are untouched) and never substitutes for LIVE evidence. Per-cut records cite claim IDs + killed mutant IDs — "N checks passed" alone is not evidence. `check-agy-permission-matrix` complements it with the enumerated permission contract space (literal cells + stated exclusion rules, oracle independent of the SUT). The gate's HEAD is separable and IS in the floor: `check-gate-manifests` runs the runner self-test, validates the committed manifest set against the origin index, and asserts the declared lane inventory, while executing zero COMMITTED mutants (the self-test runs only its synthetic fixture mutants, in tmp fixture repos) and never snapshotting the repo — three of the five reds qualification has ever produced in CI died in that head, in under five seconds.
89
+ - **Discriminating power of that floor:** `./run.sh check-gate-qualification` (scheduled, not ambient: the whole body runs only as the bare call — in the CI `check` job on a branch push that touched the qualification surface, and — for a cut — at the prepared SHA, where the exact-SHA release oracle requires that CI step once and the prepared state awaits it, never presumed PASS; a cut therefore batches its commits into that one prepared push (#132 took the cut's body off the local `release-gate`). Development runs no whole body (#133): a lane that changes a gate/mutant/matrix runs its affected exact-argv groups once, after review, as PARTIAL receipts — `./run.sh check-gate-qualification --group '<argv JSON>' --receipt <absolute path outside the repo>`, whose arguments reach the runner's parser unchanged and are refused there by name, before any body or snapshot, when malformed, missing or repeated. A receipt is a measurement of one group, never a qualification pass. Neither form is inside the default check chains; a semver TAG push triggers no CI at all, and the exact-SHA evidence a release quotes is whichever run at that commit CARRIES the body — the branch run when the push touched the qualification surface, else a dispatch run; a push that touched none of the qualification surface skips the body, and `workflow_dispatch -f qualify=true` plus a weekly `schedule` run it unconditionally — the exact-SHA release oracle refuses a SHA whose body step did not conclude success) re-plants committed defect mutants (`scripts/mutants/*.json`, one per closed defect class) in an isolated snapshot repo and requires each to turn its gate red **bounded and at its claimed `[QK:<claim>]` signature** — a wrong-reason red fails, a baseline-red control voids the whole group, and the runner is negative-controlled on every run (zero-match/multi-match/survived/wrong-reason/hang/control-red/impurity). This measures whether the deterministic gates still *block* what they claim to block; it is **not a new evidence level** (L0–L5 are untouched) and never substitutes for LIVE evidence. Per-cut records cite claim IDs + killed mutant IDs — "N checks passed" alone is not evidence. `check-agy-permission-matrix` complements it with the enumerated permission contract space (literal cells + stated exclusion rules, oracle independent of the SUT). The gate's HEAD is separable and IS in the floor: `check-gate-manifests` runs the runner self-test, validates the committed manifest set against the origin index, and asserts the declared lane inventory, while executing zero COMMITTED mutants (the self-test runs only its synthetic fixture mutants, in tmp fixture repos) and never snapshotting the repo — three of the five reds qualification has ever produced in CI died in that head, in under five seconds.
90
90
  - **The snapshot is source-only (#125).** It replicates the git work surface and never copies an ignored artifact from the checkout. A gate that consumes an ignored build output — the compiled `entwurf-bridge` a pi citizen registers — declares it on each of its mutants (`"build": {"argv": [...], "output": "<dir>"}`, one declaration per gate group). For that group only, the runner builds the output from the snapshot's own bytes before the control, rebuilds it after every mutation and from the restored bytes before the post-control (which must reproduce the baseline digest), never counts a build red as a kill, voids a run whose gate writes into the output, and removes the output when the group ends. The runner self-test owns this as `[QK:QUALIFY-BUILD-*]`.
91
91
  - **Live floor:** `LIVE=1 ./run.sh release-gate <scratch-project-dir> --cut` — `pnpm run check:full` + the v2-native live gates + the ACP plugin acceptance floor. It reports a **two-tier summary**:
92
92
  - **MUST tier** (release-blocking — owns the exit code; "green" applies only here): `pnpm run check:full`, `smoke-entwurf-v2-matrix-live`, `check-bridge`, `doctor-pi-provider` (#81 — `check-bridge` proves the launcher this checkout SHIPS; this proves the invocation the operator's pi provider actually EXECS, by booting it and requiring the entwurf verb set back, because `command -v` answers yes for a command that exits 127; wired as a step on 2026-08-19 after a relocated pnpm cmd-shim cost a cut sixteen LIVE steps before the same fact surfaced at `smoke-acp-bundled-mcp-live`), the resident-garden-guard zero-token half (record birth / record-keyed socket / attach-on-reopen), the `smoke-acp-*-live` ACP plugin smokes (socket-citizen / raw-turn / overlay / provider / session-reuse / carrier-augment / memory-containment / rgg / mcp / skill / bundled-mcp / v2-send), the axis wired in on 2026-07-31 that the aggregate had simply never listed (`smoke-claude-native-resume-live`), `smoke-entwurf-chain-live` — the cross-harness delivery chain (native Claude Code → pi GPT → pi ACP Sonnet → mailbox terminus) proving sender identity and replyability at every hop plus a real read receipt at the end — the integrated `smoke-mux-lifecycle-live` (its own note below), and the two OMP steps 0.16.0 wired: `smoke-omp-receive-live` (the addressed roundtrip into a live omp citizen; it reads the capability registry and decides its own outcome rather than passing by default) and `smoke-omp-fresh-live` (the clause 7 receipt the release stop now requires). (`smoke-session-id-name` is gone — #50 C3: its `--session-id`/`--name` substrate has no entwurf consumer anymore.)
@@ -96,7 +96,7 @@ that moved out of `scripts/` and into a file beside its subject would otherwise
96
96
 
97
97
  > **The herdr rail is an on-demand axis until it has run.** `smoke-herdr-fresh-call-live` opens a real caller inside a real herdr pane, has it invoke the PUBLIC `entwurf_fresh_call`, and reads the child's nonce callback — the one axis no deterministic gate reaches, since the rail is selected from the caller's own process environment. **The release aggregate does not yet require the herdr rail**: herdr is optional (a cut on a host without it must not block), and first evidence precedes promotion — a rail whose first acceptance run is days old has not earned a place in the gate that blocks every cut. Run it directly with `LIVE=1 ./run.sh smoke-herdr-fresh-call-live` whenever a cut changes herdr rail code, and promote it to a MUST only after the axis has a run history to point at. Without `LIVE=1`, or without herdr/pi/claude on PATH, it reports protocol SKIP — never a pass.
98
98
 
99
- > **The plugin's FIRST USER PATH is its own on-demand axis, and an npm acquisition receipt holds only for the ref it ran on (#118 홉 1).** Every other herdr gate proves the plugin against a source this repository controls: `check-herdr-plugin-build` drives a stub, and `smoke-herdr-plugin-build-live` redirects the product remote to a local bare clone with git `insteadOf` and asserts `identity.kind === "herdr-checkout"`. npm has been the production authority class since `dd84ac0`; the 0.30.0 follow-up re-pinned `@junghanacs/entwurf@0.30.0` with its published sha512 after the candidate windows rode `herdr-checkout` (`8b92e09`). The **unpublished 0.30.1 preparation** now uses that closed verification carrier again, without inventing a registry integrity. The source re-proof has separate dated npm rows: 2026-09-23 public `main=113ca53`, plugin 0.5.0, runtime 0.25.0 (CHANGELOG 0.25.1), and the 0.30.0 re-pin accepted at public `506c46f` in the [#126 thread](https://github.com/junghan0611/entwurf/issues/126). Neither certifies a future 0.30.1 registry acquisition. After publication and a measured re-pin, that source owes its own public-ref run; checkout candidate evidence does not transfer. `LIVE=1 ./run.sh smoke-herdr-raw-install-live [--ref <REF>]` fills that row in a clean `node:24` container with no socket, config, cache or repo mounted, no `insteadOf`, the public remote spelled as a user would type it, and pi / herdr / Claude Code installed in the IMAGE BUILD as scaffolding (Hard Rule 17 — the product face begins at `herdr integration install pi`). herdr itself comes from `scripts/install-herdr-ci.sh` against `scripts/fixtures/herdr-supply.json`, so no version or digest lives here. `--ref` defaults to `main` rather than a pinned SHA because receipt (a) is defined as what a user gets today; the resolved commit is printed, so the receipt stays exact. It takes a REMOTE REF and not an arbitrary commit — measured on herdr 0.9.1, `--ref <sha>` fails as `couldn't find remote ref`, because the checkout fetches by name — so a candidate is addressed by its branch and pinned by the resolved commit the gate prints. **Its first question is a measurement, not an assumption:** herdr registers a plugin through a running server and falls back to `persist_plugin_offline` only on a connection error (`src/cli/plugin.rs:914-981,1016-1021` @ `c77af189`, 0.9.0 source), and a container has no server. The 2026-09-23 PASS installed with no server on the 0.9.1 binary `113ca53`'s `herdr-supply.json` pins, so that install path worked on 0.9.1 — inferred from the PASS, not a direct observation of the fallback branch; a later "no" on another herdr is #118 investigation output rather than a completion. `--yes` is always passed so a non-interactive exit 2 (`:189`) can never be misread as a missing server. **At a ref whose lock names `herdr-checkout` the acquisition axis reports a NAMED SKIP rather than a red, and the rest of the journey still runs.** That lock is the candidate carrier a cut legitimately sits on — 0.23.0 was tagged that way and `dd84ac0` pinned npm afterwards — so a red there would report a broken install when what is true is that the ref does not lock npm. What is asserted at BOTH lock kinds is that the runtime came from the source the ref locks, and the package name is read from the checkout manifest rather than the lock, because a `herdr-checkout` lock carries no `name` (`herdr-runtime.mjs:317-328`) and joining `undefined` throws instead of failing an assertion. **Cells [6]–[9] carry it past the install into the USE path (#118 H1-4/H1-5).** An install is not a use, so the same container then runs what those bytes became: the compiled bridge entry, the three `REQUIRED_BINS` as executables, and a real `entwurf check-bridge` printing its exact eight-verb set — the installed-runtime row below, measured rather than inferred from the bootstrap's refusal to install without them. Then the pi that this plugin WIRED is started with no `-e` and no `--no-extensions`, because the subject is the wiring and not this checkout: one `get_state` in `--mode rpc` costs zero tokens and needs no provider argument (a bogus `--provider` exits 1 before session_start; omitting the pair lets pi resolve the default the extension itself registers), and while that session is alive the cell reads its V3 record, its gardenId-keyed control socket, and the installed `entwurf peer-facts` reporting it `alive`. The plugin's own status fan is then driven with NEITHER side stubbed, in both states a user can be in: with no herdr server it must name `herdr-agent-list-failed` and go red rather than draw an empty table, and under a headless `herdr server` — scaffolding for that second read only, stopped again before the teardown — it exits 0 and COUNTS the citizen as `unobserved` instead of dropping it. Last, the shipped `entwurf herdr-plugin-deactivate` runs from the installed bin, deletes the runtime it is executing from, and the plugin reinstalls onto the host it fully left — the deactivation row, on the npm source. **What these cells still do not close:** swap / torn-swap recovery on the npm source, the package-consumer proof, and the fan drawing a citizen as a ROW — that needs `placement.kind === "herdr-pane"`, so the pi session must live in a herdr pane a session reference joins, and a headless container has no panes. **This smoke carries no test beside behavior and no mutant**, and that is a property of its class rather than an omission: it is a real-live container journey whose oracle is the container's own end state, so there is no pure seam to assert against and nothing a source mutation could kill deterministically. Like the other two herdr LIVE lanes it is **on-demand and not an aggregate MUST** — herdr is optional, a cut on a host without it must not block, and first evidence precedes promotion. Without `LIVE=1`, or without a usable Docker daemon, it reports a named SKIP; `ENTWURF_REQUIRE_DOCKER=1` turns the Docker absence red. Neither skip is a pass.
99
+ > **The plugin's FIRST USER PATH is its own on-demand axis, and an npm acquisition receipt holds only for the ref it ran on (#118 홉 1).** Every other herdr gate proves the plugin against a source this repository controls: `check-herdr-plugin-build` drives a stub, and `smoke-herdr-plugin-build-live` redirects the product remote to a local bare clone with git `insteadOf` and asserts `identity.kind === "herdr-checkout"`. npm has been the production authority class since `dd84ac0`; the 0.30.0 follow-up re-pinned `@junghanacs/entwurf@0.30.0` with its published sha512 after the candidate windows rode `herdr-checkout` (`8b92e09`), and the 0.30.1 preparation rode that closed verification carrier again without inventing a registry integrity. The last published plugin snapshot `v2026.10.9-herdr.0.34.0` pins the published `@junghanacs/entwurf@0.34.0` with its measured sha512 (#132); 0.31–0.33 were never pinned. This checkout's unpublished 0.34.1 preparation rides the closed `herdr-checkout` verification carrier again, without inventing a registry integrity: it has no published npm SRI and is not a plugin pair. The source re-proof has separate dated npm rows: 2026-09-23 public `main=113ca53`, plugin 0.5.0, runtime 0.25.0 (CHANGELOG 0.25.1), and the 0.30.0 re-pin accepted at public `506c46f` in the [#126 thread](https://github.com/junghan0611/entwurf/issues/126). Neither certifies the 0.34.0 registry acquisition. That pin's real-npm swap / torn-swap recovery was measured locally on 2026-10-09 by driving the registry-shipped `scripts/herdr-runtime.mjs` (byte-equal to this checkout's) with its own npm acquisition: a 0.33.0 → 0.34.0 swap, and the same upgrade torn between its two renames then resumed, where the last-good 0.33.0 tree was restored before 0.34.0 was acquired again. Both ended `runtime-ready` at observed digest `sha256-e9d995ff…`, the accepted 0.34.0 artifact. That local receipt is recorded in the [#132 thread](https://github.com/junghan0611/entwurf/issues/132); it covers runtime recovery, not this smoke's cells. The public-ref acceptance of this smoke is PENDING the prepared push, and its actual result will be recorded there; checkout candidate evidence does not transfer. `LIVE=1 ./run.sh smoke-herdr-raw-install-live [--ref <REF>]` fills that row in a clean `node:24` container with no socket, config, cache or repo mounted, no `insteadOf`, the public remote spelled as a user would type it, and pi / herdr / Claude Code installed in the IMAGE BUILD as scaffolding (Hard Rule 17 — the product face begins at `herdr integration install pi`). herdr itself comes from `scripts/install-herdr-ci.sh` against `scripts/fixtures/herdr-supply.json`, so no version or digest lives here. `--ref` defaults to `main` rather than a pinned SHA because receipt (a) is defined as what a user gets today; the resolved commit is printed, so the receipt stays exact. It takes a REMOTE REF and not an arbitrary commit — measured on herdr 0.9.1, `--ref <sha>` fails as `couldn't find remote ref`, because the checkout fetches by name — so a candidate is addressed by its branch and pinned by the resolved commit the gate prints. **Its first question is a measurement, not an assumption:** herdr registers a plugin through a running server and falls back to `persist_plugin_offline` only on a connection error (`src/cli/plugin.rs:914-981,1016-1021` @ `c77af189`, 0.9.0 source), and a container has no server. The 2026-09-23 PASS installed with no server on the 0.9.1 binary `113ca53`'s `herdr-supply.json` pins, so that install path worked on 0.9.1 — inferred from the PASS, not a direct observation of the fallback branch; a later "no" on another herdr is #118 investigation output rather than a completion. `--yes` is always passed so a non-interactive exit 2 (`:189`) can never be misread as a missing server. **At a ref whose lock names `herdr-checkout` the acquisition axis reports a NAMED SKIP rather than a red, and the rest of the journey still runs.** That lock is the candidate carrier a cut legitimately sits on — 0.23.0 was tagged that way and `dd84ac0` pinned npm afterwards — so a red there would report a broken install when what is true is that the ref does not lock npm. What is asserted at BOTH lock kinds is that the runtime came from the source the ref locks, and the package name is read from the checkout manifest rather than the lock, because a `herdr-checkout` lock carries no `name` (`herdr-runtime.mjs:317-328`) and joining `undefined` throws instead of failing an assertion. **Cells [6]–[9] carry it past the install into the USE path (#118 H1-4/H1-5).** An install is not a use, so the same container then runs what those bytes became: the compiled bridge entry, the three `REQUIRED_BINS` as executables, and a real `entwurf check-bridge` printing its exact eight-verb set — the installed-runtime row below, measured rather than inferred from the bootstrap's refusal to install without them. Then the pi that this plugin WIRED is started with no `-e` and no `--no-extensions`, because the subject is the wiring and not this checkout: one `get_state` in `--mode rpc` costs zero tokens and needs no provider argument (a bogus `--provider` exits 1 before session_start; omitting the pair lets pi resolve the default the extension itself registers), and while that session is alive the cell reads its V3 record, its gardenId-keyed control socket, and the installed `entwurf peer-facts` reporting it `alive`. The plugin's own status fan is then driven with NEITHER side stubbed, in both states a user can be in: with no herdr server it must name `herdr-agent-list-failed` and go red rather than draw an empty table, and under a headless `herdr server` — scaffolding for that second read only, stopped again before the teardown — it exits 0 and COUNTS the citizen as `unobserved` instead of dropping it. Last, the shipped `entwurf herdr-plugin-deactivate` runs from the installed bin, deletes the runtime it is executing from, and the plugin reinstalls onto the host it fully left — the deactivation row, on the npm source. **What these cells still do not close:** swap / torn-swap recovery on the npm source, the package-consumer proof, and the fan drawing a citizen as a ROW — that needs `placement.kind === "herdr-pane"`, so the pi session must live in a herdr pane a session reference joins, and a headless container has no panes. **This smoke carries no test beside behavior and no mutant**, and that is a property of its class rather than an omission: it is a real-live container journey whose oracle is the container's own end state, so there is no pure seam to assert against and nothing a source mutation could kill deterministically. Like the other two herdr LIVE lanes it is **on-demand and not an aggregate MUST** — herdr is optional, a cut on a host without it must not block, and first evidence precedes promotion. Without `LIVE=1`, or without a usable Docker daemon, it reports a named SKIP; `ENTWURF_REQUIRE_DOCKER=1` turns the Docker absence red. Neither skip is a pass.
100
100
 
101
101
  > **The Herdr plugin's install-time build has two axes, and one of them needs the network (#116 M3-b3).** `check-herdr-plugin-build` is deterministic and rides `check:hermetic`: the listing reaches the runner through its own spawn seam (there is no fake `herdr` binary in this repo), and the one cell that drives the REAL binary reports a named SKIP on a host without it — `ENTWURF_REQUIRE_HERDR=1` makes that absence red. What it cannot have is the real acquisition: `npm pack git+https://github.com/junghan0611/entwurf.git#<full sha>` builds the bridge through `prepare`, which installs devDependencies from the registry. So `LIVE=1 ./run.sh smoke-herdr-plugin-build-live` owns that axis, and its cells are **two kinds of evidence, labelled as such**: three are the real Herdr journey — a real `herdr plugin install` driving the real `[[build]]`, with the UNCHANGED product remote redirected to a local bare clone by git `insteadOf` (the one substitution, which is what makes an unpushed candidate testable) — covering remote-commit available, the `{pi}` → `{pi, claude-code}` reinstall with OpenCode as a zero-write negative control and exactly ONE counted vendor MCP owner entry, and the post-build Herdr-commit gap (where the ledger has already rebound to the gap commit, so the honest assertion is that transition and not unchanged bytes). The fourth cell — remote-commit **unavailable** — calls the shipped runtime leaf directly with the fixed product argv and is therefore **acquisition-leaf evidence, not a journey**; routing it through Herdr would add a second explanation for one red. Its oracle is the honest one too: active runtime, ledger and harness bytes preserved, while the journal is left as the certified `installing` retry authority the transaction deliberately writes before acquiring, and a normal install afterwards is green. Its Claude cell is where the vendor question the deterministic gates refuse to claim gets answered — whether Claude Code accepts an ABSOLUTE executable as an MCP command — and it reports a named skip on a host without `claude`, never a pass. Like the herdr fresh-call rail, this smoke is **on-demand and not an aggregate MUST**. The release aggregate does not require the plugin build smoke: herdr is optional, a cut on a host without it must not block, and first evidence precedes promotion.
102
102
 
@@ -203,10 +203,21 @@ Do not collapse source, package, fixture, and native-host evidence into one “g
203
203
  **Native durable contact (#129, #130 carrier).** `check-pi-durable-beside` is the deterministic
204
204
  beside-test/mutant coordinate in `check:contracts`: the adapter, the installed carrier/SDK-set
205
205
  verifier, the preflight, the owner join, the carrier's relocation grammar, resolver and
206
- maintainer emitter (its source reads refuse replace refs), and the bootstrap's explicit
207
- `--native-module` ingress (#130 P2), driven through the packaged `main`'s own seams: initialization
208
- before the TUI and the open, named refusals, the identity and directory guards, the contact-first
209
- order by reference, and the bridge spawn *spec* (not the child's inheritance). `check-pi-runtime-version` asks the
206
+ maintainer emitter (its source reads refuse replace refs; its one patch carries exactly one
207
+ section per patched carrier file, #134), and the bootstrap's explicit `--native-module` ingress
208
+ (#130 P2) with its paired `--native-module-dir` (#134), driven through the packaged `main`'s own
209
+ seams: initialization before the TUI and the open, named refusals, the identity and directory
210
+ guards, the top-level `*.extension.mjs` filter and byte-wise order, cross-module name refusals, the
211
+ contact-first order by reference, and the bridge spawn *spec* (not the child's inheritance). The
212
+ carrier's task footer (#134) is observed in ONE sandboxed child that loads the committed carrier
213
+ through its own resolver: the real carrier runtime opened and closed with no model (graph observed
214
+ for the session, `/tasks` toggling only the panel), then continued after one native background task
215
+ was committed into that session through the SDK's public `Conversation.commit` (the carrier's graph
216
+ holds that node and the badge counts it, `pending` while recovered), and the carrier TUI's badge and
217
+ `durableLayout` drawn by the published pi-tui renderer on a fake terminal across rows 1–8 × cols
218
+ 1–12, count changes and resizes, against the pinned upstream screen for the zero-task case. Its
219
+ editor is a fixed stand-in: the real `CustomEditor`, overlays, emoji width and a consumer's own task
220
+ are the visible-terminal and consumer subjects. `check-pi-runtime-version` asks the
210
221
  same verifier whether this checkout resolves exactly the pinned SDK set (the native set; the PATH
211
222
  host is setup's subject), and `check-pi-import-surface` asserts the carrier fence. The separate
212
223
  `check-pi-durable-contact`, `check-pi-durable-send` and `check-pi-durable-receive` are
@@ -227,7 +238,9 @@ same-sandbox neighbour as the rejected control), every reachable physical SDK ed
227
238
  version and declared name, a mixed-version tree refused by name although it still imports, a
228
239
  constructed depth-3 mismatch refused by the verifier and seen by the oracle, the public launch reaching the
229
240
  app's own model refusal before any birth — also with one installed `--native-module` initialized
230
- first, and a reserved extension name refused before anything opens — and the HOST kept a separate subject — a real pi 1.0.4
241
+ first, a reserved extension name refused before anything opens, one installed `--native-module-dir`
242
+ initializing only its top-level `*.extension.mjs` (#134) and a directory without a module refused
243
+ before anything opens — and the HOST kept a separate subject — a real pi 1.0.4
231
244
  binary (the previous generation) beside the pinned native set is a named pi FAIL with zero Pi wiring while pi-durable still
232
245
  PASSes — not native admission. The maintainer's rebuild
233
246
  equality is `emit-pi-durable-carrier --check` against a supplied upstream clone; without one it is
@@ -179,6 +179,24 @@ packageRoot↔installerRoot coupling mismatch and a package/provider managed-pat
179
179
  | source checkout | the checkout dir | `dev-bin` symlinks (`setup`) | `<project>/.pi/settings.json` | shared entry + owner state | `takeover-user-scope` from the new checkout | `remove` (project) / `remove-user-scope` (global, same-owner-only) | `doctor-pi-package` → `missing-owner`; normal install still refuses |
180
180
  | global npm (`npm i -g`) | the global `node_modules/@junghanacs/entwurf` | npm bin linking | same | same shared entry | same explicit action | same; a LIVE foreign owner always refuses | same |
181
181
  | project-local npm | that project's `node_modules/@junghanacs/entwurf` | `node_modules/.bin` | same | same shared entry | same explicit action | same | same; a deleted root becomes the aligned `remove-user-scope` orphan cleanup (entry + package state + provider installerRoot must all name that missing root) |
182
+ | Herdr plugin | `$XDG_DATA_HOME/entwurf/herdr-plugin/runtime/active/node_modules/@junghanacs/entwurf` | none on PATH — Pi provider and Claude MCP/statusLine record ABSOLUTE commands under that runtime | none | same shared entry, plus the plugin's activation ledger (`$XDG_STATE_HOME/entwurf/herdr-plugin/activation.json`) | none — the plugin never takes a registration over; release it from its owner first | the runtime's own absolute `…/node_modules/.bin/entwurf herdr-plugin-deactivate` | — |
183
+
184
+ The four installation paths keep separate records: Herdr's plugin registry (changed only by `herdr
185
+ plugin install` / `unlink` / `uninstall`), the plugin's runtime journal and activation ledger, the shared Pi
186
+ package/provider ownership above, and the Claude meta-bridge install state
187
+ (`${CLAUDE_CONFIG_DIR:-~/.claude}/entwurf.install-state.json`). Moving between them is explicit: release the
188
+ shared registration from its owner (`remove-user-scope`, plus `uninstall-meta-bridge` for Claude) before
189
+ `herdr plugin install` — a foreign Pi owner refuses the plugin build before anything is fetched (#135) — and
190
+ run the plugin's `herdr-plugin-deactivate` before going back to `setup`. Do not `takeover-user-scope` a
191
+ registration the plugin owns, even though a refused `setup` suggests it: it splits ownership (the plugin's
192
+ provider key becomes an unowned override while its ledger still records pi) and the plugin's deactivate
193
+ then refuses. Limits that stay as they are: the Claude install state records the LAST installer and is not
194
+ root-bound — an install from another root rewrites it rather than refusing; nothing covers roots (XDG,
195
+ `PI_CODING_AGENT_DIR`, Claude config) changed between runs or a host that changes between the plugin's
196
+ early owner check and its installed recheck, which can still refuse after the download; and the plugin's
197
+ deactivate, finding no activation ledger, reports what is left at its addresses and neither reclaims nor
198
+ reuses it. The no-ledger diagnostic is not in the published 0.34.0 runtime; a plugin host receives it only
199
+ from a later published runtime the plugin pins.
182
200
 
183
201
  ## 2. Optional pi adapter / ACP plugin
184
202
 
@@ -358,12 +376,33 @@ tree (for example an override that binds pi-durable's `pi-ai` to an older publis
358
376
  (`native-module-path-*`), a module that throws (`native-module-import-failed`), a function or
359
377
  nameless default (`native-module-export-invalid`), an extension named like one the app installs
360
378
  itself (`entwurf`, `coding-tools`, `pi-prompt`, `subagent`) or a tool named like one of Entwurf's
361
- contact tools (`native-module-name-reserved`; any other duplicate is the native registry's to
362
- judge), and a module whose initialization changes
379
+ contact tools (`native-module-name-reserved`; within one module, any other duplicate is the native
380
+ registry's to judge), and a module whose initialization changes
363
381
  `PI_SESSION_ID`, `PI_CODING_AGENT_DIR`, `HOME` or any `ENTWURF_*` (`native-module-env-identity-changed`)
364
382
  or the process directory (`native-module-cwd-changed`). Your module is trusted code; those
365
- checks catch mistakes, they do not sandbox it. Nothing else names a module — no directory,
366
- setting or profile — and a sibling opened by `entwurf_fresh_call` starts without one.
383
+ checks catch mistakes, they do not sandbox it. Nothing else names a module — no default
384
+ directory, setting or profile — and a sibling opened by `entwurf_fresh_call` starts without one.
385
+
386
+ - **A module directory instead (#134, after 0.33.0).** `--native-module-dir
387
+ <absolute directory>` replaces `--native-module` (naming both is `native-module-ingress-exclusive`).
388
+ Entwurf reads that one directory once, at its top level: every `*.extension.mjs` that is not a
389
+ dotfile is a module; helpers such as `env.mjs` or `index.mjs`, other suffixes, subdirectories and
390
+ directory links are never opened. The modules initialize one by one in byte-wise name order
391
+ (`00-env.extension.mjs` before `10-tools.extension.mjs`), each under the same checks as above,
392
+ and install in that order after the contact. Also refused by name: a directory without a module
393
+ (`native-module-dir-empty`), a matching entry that is not a regular file
394
+ (`native-module-path-invalid`), two names for one file (`native-module-dir-duplicate-target`), and
395
+ a module repeating an earlier module's extension or tool name (`native-module-name-duplicate` —
396
+ the native registry would replace that extension or keep both tools). A refusal at a later module
397
+ leaves the earlier ones already evaluated; nothing is rolled back.
398
+
399
+ - **Background-task badge (#134, after 0.33.0).** While a native background task is live, the durable
400
+ TUI shows `⏳ n tasks` as the first line under the transcript, whatever kind of task it is, with
401
+ the ones not running named (`aborting`, `completing`, `waiting`, `pending`). It never shrinks: a
402
+ short terminal loses the editor and footer before it, a one-row terminal shows only the badge, and
403
+ a narrow one shortens it down to `*`. `/tasks` only shows or hides the task panel; the badge stays.
404
+ With no background task the screen is unchanged. A task that has already finished leaves the
405
+ count — its outcome is reported by the task's own extension.
367
406
 
368
407
  - **Known warning.** Ordinary Pi may print `Host-provided extension packages must be declared in
369
408
  peerDependencies with a "*" range, not dependencies: @earendil-works/…` for Entwurf's
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@junghanacs/entwurf",
3
- "version": "0.33.0",
3
+ "version": "0.34.1",
4
4
  "description": "Garden-citizen dispatch substrate and meta-bridge for pi, Claude Code, Copilot CLI, OMP, Antigravity, and Codex CLI; Claude and Snowflake Cortex Code ACP backends.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -119,7 +119,7 @@
119
119
  "vitest": "4.1.9"
120
120
  },
121
121
  "scripts": {
122
- "prepare": "husky 1>&2 || true; npm run --silent build-bridge 1>&2",
122
+ "prepare": "npm run --silent build-bridge 1>&2",
123
123
  "postinstall": "node scripts/postinstall-chmod.cjs",
124
124
  "build-bridge": "bash scripts/with-dist-lock.sh bash scripts/build-bridge.sh",
125
125
  "prepack": "pnpm run build-bridge 1>&2",