@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.
@@ -25,6 +25,7 @@ import fs from "node:fs";
25
25
  import path from "node:path";
26
26
  import { fileURLToPath } from "node:url";
27
27
  import {
28
+ ActivationError,
28
29
  certifyPhaseForInverse,
29
30
  certifyRootsAgainstLedger,
30
31
  componentStates,
@@ -49,6 +50,31 @@ function fail(code, detail) {
49
50
  process.exit(1);
50
51
  }
51
52
 
53
+ /**
54
+ * What stands at this host's fixed plugin addresses — journal, runtime root, our npm cache — by
55
+ * lstat, without reading or following anything. Only consulted when there is no ledger.
56
+ */
57
+ function leftoversAtPluginAddresses(runtime) {
58
+ const found = [];
59
+ for (const [label, target] of [
60
+ ["journal", runtime.journalPath],
61
+ ["runtime", runtime.runtimeRoot],
62
+ ["cache", runtime.cacheDir],
63
+ ]) {
64
+ const stat = fs.lstatSync(target, { throwIfNoEntry: false });
65
+ if (stat === undefined) continue;
66
+ const kind = stat.isSymbolicLink()
67
+ ? "symlink"
68
+ : stat.isDirectory()
69
+ ? "directory"
70
+ : stat.isFile()
71
+ ? "file"
72
+ : "other";
73
+ found.push(`${label}=${kind} (${target})`);
74
+ }
75
+ return found;
76
+ }
77
+
52
78
  export function deactivate(env) {
53
79
  const activation = resolveActivationLayout(env);
54
80
  const runtime = resolveRuntimeLayout(env);
@@ -56,8 +82,21 @@ export function deactivate(env) {
56
82
 
57
83
  const ledger = readCertifiedLedger(activation);
58
84
  if (ledger === null) {
59
- process.stdout.write("[herdr-plugin-deactivate] no certified activation ledger — nothing to undo\n");
60
- return 0;
85
+ // NO LEDGER IS NOT "NOTHING HERE" (#135). An install that stopped before its activation was
86
+ // recorded leaves a journal, a runtime or a cache behind, and saying "nothing to undo" over them
87
+ // was a false success. Without a ledger nothing proves what they belong to or that no harness
88
+ // wiring names them, so this verb removes nothing: it names what it found and exits non-zero.
89
+ const leftovers = leftoversAtPluginAddresses(runtime);
90
+ if (leftovers.length === 0) {
91
+ process.stdout.write("[herdr-plugin-deactivate] no certified activation ledger — nothing to undo\n");
92
+ return 0;
93
+ }
94
+ throw new ActivationError(
95
+ "deactivate-unattributed-leftovers",
96
+ `no certified activation ledger, yet ${leftovers.join(", ")} — NOTHING was removed. Without an activation ` +
97
+ "ledger nothing proves what these belong to or that no harness wiring still names them; inspect them and " +
98
+ "remove them by hand only if they are yours",
99
+ );
61
100
  }
62
101
  certifyRootsAgainstLedger(ledger, roots, runtime.activeDir);
63
102
  certifyPhaseForInverse(ledger);
@@ -95,6 +95,30 @@
95
95
  "timeoutSeconds": 120,
96
96
  "signature": "[QK:QUALIFY-PRIOR-RUN-EVIDENCE-KEPT]",
97
97
  "signatureSource": "scripts/check-gate-qualification.ts"
98
+ },
99
+ {
100
+ "claim": "QUALIFY-DOOR-FORWARDS-ARGV",
101
+ "title": "run.sh check-gate-qualification drops its arguments again — a requested --group/--receipt PARTIAL reaches the runner as a bare call and starts the whole ~900-mutant body nobody asked for (#133: the wrapper defect that made every development PARTIAL a whole run)",
102
+ "subject": "run.sh",
103
+ "find": [" run_ts scripts/check-gate-qualification.ts \"$@\""],
104
+ "replace": [" run_ts scripts/check-gate-qualification.ts"],
105
+ "gate": ["bash", "run.sh", "check-gate-manifests"],
106
+ "timeoutSeconds": 120,
107
+ "signature": "[QK:QUALIFY-DOOR-FORWARDS-ARGV]",
108
+ "signatureSource": "scripts/check-gate-qualification.ts"
109
+ },
110
+ {
111
+ "claim": "QUALIFY-DOOR-REFUSES-BEFORE-BODY",
112
+ "title": "run.sh check-gate-qualification retries bare when the runner refuses — a malformed, missing or repeated flag is named, then the whole body starts anyway in place of the PARTIAL that was asked for (#133)",
113
+ "subject": "run.sh",
114
+ "find": [" run_ts scripts/check-gate-qualification.ts \"$@\""],
115
+ "replace": [
116
+ " run_ts scripts/check-gate-qualification.ts \"$@\" || run_ts scripts/check-gate-qualification.ts"
117
+ ],
118
+ "gate": ["bash", "run.sh", "check-gate-manifests"],
119
+ "timeoutSeconds": 120,
120
+ "signature": "[QK:QUALIFY-DOOR-REFUSES-BEFORE-BODY]",
121
+ "signatureSource": "scripts/check-gate-qualification.ts"
98
122
  }
99
123
  ]
100
124
  }
@@ -316,6 +316,41 @@
316
316
  "timeoutSeconds": 420,
317
317
  "signature": "[QK:HAC-REBIND-ATOMIC-CHECKPOINT]",
318
318
  "signatureSource": "scripts/check-herdr-activation.ts"
319
+ },
320
+ {
321
+ "claim": "HAC-PI-STATE-PATHS-SHARED",
322
+ "title": "resolving the provider owner record with `??` turns an empty XDG_DATA_HOME into a relative path no writer uses, so the preflight sees no owner and the ledger lands on a host the provider writer then refuses",
323
+ "subject": "scripts/herdr-plugin-activate.mjs",
324
+ "find": ["\t\tconst piState = piStatePaths(env);"],
325
+ "replace": [
326
+ "\t\tconst piState = { packageState: piStatePaths(env).packageState, providerState: path.join(env.XDG_DATA_HOME ?? path.join(env.HOME, \".local\", \"share\"), \"entwurf\", \"pi-provider\", \"install-state.json\") };"
327
+ ],
328
+ "gate": ["bash", "run.sh", "check-herdr-activation"],
329
+ "timeoutSeconds": 300,
330
+ "signature": "[QK:HAC-PI-STATE-PATHS-SHARED]",
331
+ "signatureSource": "scripts/check-herdr-activation.ts"
332
+ },
333
+ {
334
+ "claim": "HAC-NO-LEDGER-LEFTOVERS-REPORTED",
335
+ "title": "answering 'nothing to undo' whenever the ledger is absent is a false success over the journal, runtime or cache an aborted install left behind",
336
+ "subject": "scripts/herdr-plugin-deactivate.mjs",
337
+ "find": ["\t\tif (leftovers.length === 0) {"],
338
+ "replace": ["\t\tif (true) {"],
339
+ "gate": ["bash", "run.sh", "check-herdr-activation"],
340
+ "timeoutSeconds": 300,
341
+ "signature": "[QK:HAC-NO-LEDGER-LEFTOVERS-REPORTED]",
342
+ "signatureSource": "scripts/check-herdr-activation.ts"
343
+ },
344
+ {
345
+ "claim": "HAC-LEDGER-PATH-KIND",
346
+ "title": "reading the ledger without lstat lets a dangling symlink answer ENOENT, i.e. 'no ledger', and every verb then acts as if nothing had ever been activated",
347
+ "subject": "scripts/herdr-activation.mjs",
348
+ "find": ["\tif (!kind.isFile()) {"],
349
+ "replace": ["\tif (false) {"],
350
+ "gate": ["bash", "run.sh", "check-herdr-activation"],
351
+ "timeoutSeconds": 300,
352
+ "signature": "[QK:HAC-LEDGER-PATH-KIND]",
353
+ "signatureSource": "scripts/check-herdr-activation.ts"
319
354
  }
320
355
  ]
321
356
  }
@@ -162,6 +162,72 @@
162
162
  "timeoutSeconds": 300,
163
163
  "signature": "[QK:HPB-INSTALL-USAGE-NOTE]",
164
164
  "signatureSource": "scripts/check-herdr-plugin-build.ts"
165
+ },
166
+ {
167
+ "claim": "HPB-OWNERSHIP-BEFORE-ACQUISITION",
168
+ "title": "not asking the selected components' owners before bootstrap fetches and installs a runtime for a registration another root owns — the refusal only arrives after the bytes are on disk, which is the #135 ordering defect",
169
+ "subject": "plugins/herdr/lib/build.mjs",
170
+ "find": ["\tcertifyComponentOwners(env, {"],
171
+ "replace": ["\t((_env, _args) => {})(env, {"],
172
+ "gate": ["bash", "run.sh", "check-herdr-plugin-build"],
173
+ "timeoutSeconds": 300,
174
+ "signature": "[QK:HPB-OWNERSHIP-BEFORE-ACQUISITION]",
175
+ "signatureSource": "scripts/check-herdr-plugin-build.ts"
176
+ },
177
+ {
178
+ "claim": "HPB-OWNERSHIP-PROVIDER-HALF",
179
+ "title": "dropping the provider half lets a host whose provider key another root owns pass the early check, so the runtime is fetched for an install its own provider writer was always going to refuse",
180
+ "subject": "scripts/herdr-plugin-activate.mjs",
181
+ "find": ["\t\tpreflights.push([", "\t\t\t\"pi-provider\","],
182
+ "replace": ["\t\tif (false) preflights.push([", "\t\t\t\"pi-provider\","],
183
+ "gate": ["bash", "run.sh", "check-herdr-plugin-build"],
184
+ "timeoutSeconds": 300,
185
+ "signature": "[QK:HPB-OWNERSHIP-PROVIDER-HALF]",
186
+ "signatureSource": "scripts/check-herdr-plugin-build.ts"
187
+ },
188
+ {
189
+ "claim": "HPB-OWNERSHIP-CLAUDE-PLAN",
190
+ "title": "skipping Claude's plan preflight lets a selected Claude whose settings its own state owner cannot plan reach acquisition, and the refusal lands only after the runtime is installed",
191
+ "subject": "scripts/herdr-plugin-activate.mjs",
192
+ "find": ["\tif (selected.includes(\"claude-code\")) {"],
193
+ "replace": ["\tif (false) {"],
194
+ "gate": ["bash", "run.sh", "check-herdr-plugin-build"],
195
+ "timeoutSeconds": 300,
196
+ "signature": "[QK:HPB-OWNERSHIP-CLAUDE-PLAN]",
197
+ "signatureSource": "scripts/check-herdr-plugin-build.ts"
198
+ },
199
+ {
200
+ "claim": "HPB-OWNERSHIP-SELECTED-ONLY",
201
+ "title": "asking every backend instead of the selected ones lets a harness Herdr has not integrated veto the install of one it has",
202
+ "subject": "plugins/herdr/lib/build.mjs",
203
+ "find": ["\t\tselected: profile.activate,", "\t\tspawn,"],
204
+ "replace": ["\t\tselected: [\"pi\", \"claude-code\"],", "\t\tspawn,"],
205
+ "gate": ["bash", "run.sh", "check-herdr-plugin-build"],
206
+ "timeoutSeconds": 300,
207
+ "signature": "[QK:HPB-OWNERSHIP-SELECTED-ONLY]",
208
+ "signatureSource": "scripts/check-herdr-plugin-build.ts"
209
+ },
210
+ {
211
+ "claim": "HPB-OWNERSHIP-PHYSICAL-ROOT",
212
+ "title": "asking the owners with the lexical prospective root makes a same-owner reinstall under a symlinked XDG_DATA_HOME look like a foreign root, because the installed verb records its own root in physical form",
213
+ "subject": "plugins/herdr/lib/build.mjs",
214
+ "find": ["\t\t\treturn path.join(fs.realpathSync(probe), ...rest);"],
215
+ "replace": ["\t\t\treturn path.join(probe, ...rest);"],
216
+ "gate": ["bash", "run.sh", "check-herdr-plugin-build"],
217
+ "timeoutSeconds": 300,
218
+ "signature": "[QK:HPB-OWNERSHIP-PHYSICAL-ROOT]",
219
+ "signatureSource": "scripts/check-herdr-plugin-build.ts"
220
+ },
221
+ {
222
+ "claim": "HPB-PLANNED-ROOT-TIE",
223
+ "title": "without the tie the owners can have been asked about one root while the installed verb runs from another, so the early answer is about bytes nobody installed",
224
+ "subject": "plugins/herdr/lib/build.mjs",
225
+ "find": ["\tif (completeness.name !== plannedName || landedRoot !== plannedRoot) {"],
226
+ "replace": ["\tif (false) {"],
227
+ "gate": ["bash", "run.sh", "check-herdr-plugin-build"],
228
+ "timeoutSeconds": 300,
229
+ "signature": "[QK:HPB-PLANNED-ROOT-TIE]",
230
+ "signatureSource": "scripts/check-herdr-plugin-build.ts"
165
231
  }
166
232
  ]
167
233
  }
@@ -398,10 +398,10 @@
398
398
  },
399
399
  {
400
400
  "claim": "HRB-PREPARE-STDOUT-CLEAN",
401
- "title": "silencing stderr instead of stdout is the exact byte that broke the first clean-host install: husky writes `.git can't be found` to STDOUT when npm packs our GitHub git spec (no `.git` in a codeload tarball), npm merges that line into `npm pack --json`, and a perfectly good artifact is refused as `runtime-checkout-source-unavailable` — invisible to both the fake-npm cells here and to the LIVE gate, whose `insteadOf` mirror is cloned and therefore HAS a `.git`",
401
+ "title": "silencing stderr instead of routing stdout to it lets the `prepare` bridge build's stdout into the `npm pack --json` stream this rail parses — the class of byte that broke the first clean-host install, when husky (then part of `prepare`) wrote `.git can't be found` to STDOUT while npm packed our GitHub git spec (no `.git` in a codeload tarball) and a perfectly good artifact was refused as `runtime-checkout-source-unavailable`, invisible to both the fake-npm cells here and to the LIVE gate, whose `insteadOf` mirror is cloned and therefore HAS a `.git`",
402
402
  "subject": "package.json",
403
- "find": ["\t\t\"prepare\": \"husky 1>&2 || true; npm run --silent build-bridge 1>&2\","],
404
- "replace": ["\t\t\"prepare\": \"husky 2>/dev/null || true; npm run --silent build-bridge\","],
403
+ "find": ["\t\t\"prepare\": \"npm run --silent build-bridge 1>&2\","],
404
+ "replace": ["\t\t\"prepare\": \"npm run --silent build-bridge 2>/dev/null\","],
405
405
  "gate": ["bash", "run.sh", "check-herdr-runtime-bootstrap"],
406
406
  "timeoutSeconds": 300,
407
407
  "signature": "[QK:HRB-PREPARE-STDOUT-CLEAN]",
@@ -53,12 +53,8 @@
53
53
  "claim": "PI-DURABLE-BOOTSTRAP-ARGV-NO-BRIDGE-ENTRY",
54
54
  "title": "the launch argv accepts --bridge-entry, so a caller can point the contact at a bridge outside this package",
55
55
  "subject": "pi/pi-durable/bootstrap.mjs",
56
- "find": [
57
- "const VALUE_FLAGS = [\"--provider\", \"--model\", \"--width\", \"--entwurf-bootstrap\", \"--native-module\"];"
58
- ],
59
- "replace": [
60
- "const VALUE_FLAGS = [\"--bridge-entry\", \"--provider\", \"--model\", \"--width\", \"--entwurf-bootstrap\", \"--native-module\"];"
61
- ],
56
+ "find": ["const VALUE_FLAGS = ["],
57
+ "replace": ["const VALUE_FLAGS = [\n\t\"--bridge-entry\","],
62
58
  "gate": ["bash", "run.sh", "check-pi-durable-beside"],
63
59
  "timeoutSeconds": 300,
64
60
  "signature": "[QK:PI-DURABLE-BOOTSTRAP-ARGV-NO-BRIDGE-ENTRY]",
@@ -345,7 +341,7 @@
345
341
  "claim": "PI-DURABLE-CARRIER-PIN-MATCHES",
346
342
  "title": "the pin records a digest the committed carrier file does not carry, so a carrier edited after emission would still be the declared one",
347
343
  "subject": "pi/pi-durable/overlay/upstream-pin.json",
348
- "find": ["\"sha256\": \"ca0ee10dc904a57d46bb33b37fcc4efe48dfa08b2f0d10e79e7bc22136871f0a\""],
344
+ "find": ["\"sha256\": \"bfd88844fae61e8bd32d57b5b986f48056c64eebb5466d9826318a0783ac9005\""],
349
345
  "replace": ["\"sha256\": \"0000000000000000000000000000000000000000000000000000000000000000\""],
350
346
  "gate": ["bash", "run.sh", "check-pi-durable-beside"],
351
347
  "timeoutSeconds": 300,
@@ -588,8 +584,8 @@
588
584
  "claim": "PI-DURABLE-MODULE-ABSENT-UNCHANGED",
589
585
  "title": "the module phase runs without a --native-module, so a launch that names no module still tries to import one",
590
586
  "subject": "pi/pi-durable/bootstrap.mjs",
591
- "find": ["\tconst extensions = nativeModule === undefined ? [] : [await loadNativeModule(nativeModule)];"],
592
- "replace": ["\tconst extensions = false ? [] : [await loadNativeModule(nativeModule)];"],
587
+ "find": ["\tconst { nativeModules = [], ...args } = parseArgs(argv);"],
588
+ "replace": ["\tconst { nativeModules = [undefined], ...args } = parseArgs(argv);"],
593
589
  "gate": ["bash", "run.sh", "check-pi-durable-beside"],
594
590
  "timeoutSeconds": 300,
595
591
  "signature": "[QK:PI-DURABLE-MODULE-ABSENT-UNCHANGED]",
@@ -619,7 +615,7 @@
619
615
  "title": "the module initializes after the TUI is imported, so its environment arrives after the SDK modules have evaluated",
620
616
  "subject": "pi/pi-durable/bootstrap.mjs",
621
617
  "find": [
622
- "\tconst extensions = nativeModule === undefined ? [] : [await loadNativeModule(nativeModule)];",
618
+ "\tconst extensions = await loadNativeModules(nativeModules);",
623
619
  "\t// Loaded before anything opens, as upstream main.ts imports it statically: a TUI that cannot",
624
620
  "\t// load opens no session, mints no citizen and leaves nothing to clean up.",
625
621
  "\tconst { runDurableTui } = await loadTui();"
@@ -628,7 +624,7 @@
628
624
  "\t// Loaded before anything opens, as upstream main.ts imports it statically: a TUI that cannot",
629
625
  "\t// load opens no session, mints no citizen and leaves nothing to clean up.",
630
626
  "\tconst { runDurableTui } = await loadTui();",
631
- "\tconst extensions = nativeModule === undefined ? [] : [await loadNativeModule(nativeModule)];"
627
+ "\tconst extensions = await loadNativeModules(nativeModules);"
632
628
  ],
633
629
  "gate": ["bash", "run.sh", "check-pi-durable-beside"],
634
630
  "timeoutSeconds": 300,
@@ -718,6 +714,177 @@
718
714
  "argv": ["bash", "scripts/build-bridge.sh"],
719
715
  "output": "mcp/entwurf-bridge/dist"
720
716
  }
717
+ },
718
+ {
719
+ "claim": "PI-DURABLE-CARRIER-PATCH-SECTIONS",
720
+ "title": "a declared patched file with no section of its own passes the patch-shape check, so the emitter would ship that file unpatched",
721
+ "subject": "scripts/emit-pi-durable-carrier.ts",
722
+ "find": ["\tif (undeclared.length > 0 || missing.length > 0) {"],
723
+ "replace": ["\tif (undeclared.length > 0) {"],
724
+ "gate": ["bash", "run.sh", "check-pi-durable-beside"],
725
+ "timeoutSeconds": 300,
726
+ "signature": "[QK:PI-DURABLE-CARRIER-PATCH-SECTIONS]",
727
+ "signatureSource": "pi-extensions/lib/pi-durable-carrier.test.ts",
728
+ "build": {
729
+ "argv": ["bash", "scripts/build-bridge.sh"],
730
+ "output": "mcp/entwurf-bridge/dist"
731
+ }
732
+ },
733
+ {
734
+ "claim": "PI-DURABLE-FOOTER-GRAPH-LIFETIME",
735
+ "title": "/tasks closes the task-graph observation again, so hiding the panel also hides live background work from the badge",
736
+ "subject": "pi/pi-durable/carrier/runtime.js",
737
+ "find": ["\t\t\ttoggleTasks: () => command(async () => update({ tasksPanel: !state.tasksPanel })),"],
738
+ "replace": [
739
+ "\t\t\ttoggleTasks: () => command(async () => update({ tasksPanel: !state.tasksPanel, tasks: state.tasksPanel ? undefined : state.tasks })),"
740
+ ],
741
+ "gate": ["bash", "run.sh", "check-pi-durable-beside"],
742
+ "timeoutSeconds": 300,
743
+ "signature": "[QK:PI-DURABLE-FOOTER-GRAPH-LIFETIME]",
744
+ "signatureSource": "pi-extensions/lib/pi-durable-carrier.test.ts",
745
+ "build": {
746
+ "argv": ["bash", "scripts/build-bridge.sh"],
747
+ "output": "mcp/entwurf-bridge/dist"
748
+ }
749
+ },
750
+ {
751
+ "claim": "PI-DURABLE-FOOTER-BADGE-COUNT",
752
+ "title": "the badge counts every live task, foreground generation and tool tasks included, so an idle-but-turning session shows phantom background work",
753
+ "subject": "pi/pi-durable/carrier/tui.js",
754
+ "find": ["\t\tconst nodes = Object.values(graph?.tasks ?? {}).filter((node) => node.background);"],
755
+ "replace": ["\t\tconst nodes = Object.values(graph?.tasks ?? {});"],
756
+ "gate": ["bash", "run.sh", "check-pi-durable-beside"],
757
+ "timeoutSeconds": 300,
758
+ "signature": "[QK:PI-DURABLE-FOOTER-BADGE-COUNT]",
759
+ "signatureSource": "pi-extensions/lib/pi-durable-carrier.test.ts",
760
+ "build": {
761
+ "argv": ["bash", "scripts/build-bridge.sh"],
762
+ "output": "mcp/entwurf-bridge/dist"
763
+ }
764
+ },
765
+ {
766
+ "claim": "PI-DURABLE-FOOTER-BADGE-WIDTH",
767
+ "title": "the badge has no one- or two-cell form, so a very narrow terminal shows a bare marker with no count",
768
+ "subject": "pi/pi-durable/carrier/tui.js",
769
+ "find": ["\t\tconst forms = [...detailed, tasks, `⏳ ${n}`, `⏳${n}`, `*${n}`, \"*+\", \"*\"];"],
770
+ "replace": ["\t\tconst forms = [...detailed, tasks, `⏳ ${n}`, `⏳${n}`, \"*\"];"],
771
+ "gate": ["bash", "run.sh", "check-pi-durable-beside"],
772
+ "timeoutSeconds": 300,
773
+ "signature": "[QK:PI-DURABLE-FOOTER-BADGE-WIDTH]",
774
+ "signatureSource": "pi-extensions/lib/pi-durable-carrier.test.ts",
775
+ "build": {
776
+ "argv": ["bash", "scripts/build-bridge.sh"],
777
+ "output": "mcp/entwurf-bridge/dist"
778
+ }
779
+ },
780
+ {
781
+ "claim": "PI-DURABLE-FOOTER-BADGE-PROTECTED",
782
+ "title": "the badge sits last in the dock, so the renderer's bottom clip drops it first on a short terminal",
783
+ "subject": "pi/pi-durable/carrier/tui.js",
784
+ "find": [
785
+ "\t\t{ component: new VStack([{ component: badge, shrink: 0 }, ...dock]), basis: \"auto\", grow: 0, shrink: 1, minSize: 1 },"
786
+ ],
787
+ "replace": [
788
+ "\t\t{ component: new VStack([...dock, { component: badge, shrink: 0 }]), basis: \"auto\", grow: 0, shrink: 1, minSize: 1 },"
789
+ ],
790
+ "gate": ["bash", "run.sh", "check-pi-durable-beside"],
791
+ "timeoutSeconds": 300,
792
+ "signature": "[QK:PI-DURABLE-FOOTER-BADGE-PROTECTED]",
793
+ "signatureSource": "pi-extensions/lib/pi-durable-carrier.test.ts",
794
+ "build": {
795
+ "argv": ["bash", "scripts/build-bridge.sh"],
796
+ "output": "mcp/entwurf-bridge/dist"
797
+ }
798
+ },
799
+ {
800
+ "claim": "PI-DURABLE-FOOTER-ZERO-UPSTREAM",
801
+ "title": "a one-row terminal hides the transcript even with no background work, so the idle screen is no longer upstream's",
802
+ "subject": "pi/pi-durable/carrier/tui.js",
803
+ "find": ["\t\t\tvisible: (viewport) => badge.count === 0 || viewport.height > 1,"],
804
+ "replace": ["\t\t\tvisible: (viewport) => viewport.height > 1,"],
805
+ "gate": ["bash", "run.sh", "check-pi-durable-beside"],
806
+ "timeoutSeconds": 300,
807
+ "signature": "[QK:PI-DURABLE-FOOTER-ZERO-UPSTREAM]",
808
+ "signatureSource": "pi-extensions/lib/pi-durable-carrier.test.ts",
809
+ "build": {
810
+ "argv": ["bash", "scripts/build-bridge.sh"],
811
+ "output": "mcp/entwurf-bridge/dist"
812
+ }
813
+ },
814
+ {
815
+ "claim": "PI-DURABLE-MODULE-DIR-TOPLEVEL-FILTER",
816
+ "title": "a dotfile ending in .extension.mjs counts as a module, so a hidden file in the named directory is evaluated as operator code",
817
+ "subject": "pi/pi-durable/bootstrap.mjs",
818
+ "find": ["const MODULE_ENTRY = /^[^.].*\\.extension\\.mjs$/;"],
819
+ "replace": ["const MODULE_ENTRY = /\\.extension\\.mjs$/;"],
820
+ "gate": ["bash", "run.sh", "check-pi-durable-beside"],
821
+ "timeoutSeconds": 300,
822
+ "signature": "[QK:PI-DURABLE-MODULE-DIR-TOPLEVEL-FILTER]",
823
+ "signatureSource": "pi-extensions/lib/pi-durable-native-module.test.ts",
824
+ "build": {
825
+ "argv": ["bash", "scripts/build-bridge.sh"],
826
+ "output": "mcp/entwurf-bridge/dist"
827
+ }
828
+ },
829
+ {
830
+ "claim": "PI-DURABLE-MODULE-DIR-ORDER-BYTEWISE",
831
+ "title": "modules are ordered by the locale's collation, so the initialization order depends on the host's language settings",
832
+ "subject": "pi/pi-durable/bootstrap.mjs",
833
+ "find": ["\t\t.sort((a, b) => Buffer.compare(Buffer.from(a), Buffer.from(b)));"],
834
+ "replace": ["\t\t.sort((a, b) => a.localeCompare(b));"],
835
+ "gate": ["bash", "run.sh", "check-pi-durable-beside"],
836
+ "timeoutSeconds": 300,
837
+ "signature": "[QK:PI-DURABLE-MODULE-DIR-ORDER-BYTEWISE]",
838
+ "signatureSource": "pi-extensions/lib/pi-durable-native-module.test.ts",
839
+ "build": {
840
+ "argv": ["bash", "scripts/build-bridge.sh"],
841
+ "output": "mcp/entwurf-bridge/dist"
842
+ }
843
+ },
844
+ {
845
+ "claim": "PI-DURABLE-MODULE-DIR-REFUSED",
846
+ "title": "a named directory without a module launches with none, so a wrong path or an unrenamed plain index.mjs goes unnoticed",
847
+ "subject": "pi/pi-durable/bootstrap.mjs",
848
+ "find": ["\tif (names.length === 0) {"],
849
+ "replace": ["\tif (false) {"],
850
+ "gate": ["bash", "run.sh", "check-pi-durable-beside"],
851
+ "timeoutSeconds": 300,
852
+ "signature": "[QK:PI-DURABLE-MODULE-DIR-REFUSED]",
853
+ "signatureSource": "pi-extensions/lib/pi-durable-native-module.test.ts",
854
+ "build": {
855
+ "argv": ["bash", "scripts/build-bridge.sh"],
856
+ "output": "mcp/entwurf-bridge/dist"
857
+ }
858
+ },
859
+ {
860
+ "claim": "PI-DURABLE-MODULE-DIR-EXCLUSIVE",
861
+ "title": "naming both the file and the directory silently keeps only the file",
862
+ "subject": "pi/pi-durable/bootstrap.mjs",
863
+ "find": ["\tif (moduleArg !== undefined && dirArg !== undefined) {"],
864
+ "replace": ["\tif (false) {"],
865
+ "gate": ["bash", "run.sh", "check-pi-durable-beside"],
866
+ "timeoutSeconds": 300,
867
+ "signature": "[QK:PI-DURABLE-MODULE-DIR-EXCLUSIVE]",
868
+ "signatureSource": "pi-extensions/lib/pi-durable-native-module.test.ts",
869
+ "build": {
870
+ "argv": ["bash", "scripts/build-bridge.sh"],
871
+ "output": "mcp/entwurf-bridge/dist"
872
+ }
873
+ },
874
+ {
875
+ "claim": "PI-DURABLE-MODULE-DIR-NAME-DUPLICATE",
876
+ "title": "a second module's same-named extension or tool is accepted, so the native registry replaces the first extension or keeps both tools without a word",
877
+ "subject": "pi/pi-durable/bootstrap.mjs",
878
+ "find": ["\t\t\tif (owners.has(name)) {"],
879
+ "replace": ["\t\t\tif (false) {"],
880
+ "gate": ["bash", "run.sh", "check-pi-durable-beside"],
881
+ "timeoutSeconds": 300,
882
+ "signature": "[QK:PI-DURABLE-MODULE-DIR-NAME-DUPLICATE]",
883
+ "signatureSource": "pi-extensions/lib/pi-durable-native-module.test.ts",
884
+ "build": {
885
+ "argv": ["bash", "scripts/build-bridge.sh"],
886
+ "output": "mcp/entwurf-bridge/dist"
887
+ }
721
888
  }
722
889
  ]
723
890
  }