@junghanacs/entwurf 0.22.0 → 0.23.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.
Files changed (135) hide show
  1. package/AGENTS.md +4 -1
  2. package/BASELINE.md +1 -0
  3. package/CHANGELOG.md +514 -0
  4. package/DELIVERY.md +10 -2
  5. package/README.md +43 -4
  6. package/VERIFY.md +47 -0
  7. package/docs/acp-backend-rail.md +96 -17
  8. package/docs/setup-clean-host.md +11 -1
  9. package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +39 -40
  10. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +2 -2
  11. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/tool-surface.js +2 -1
  12. package/mcp/entwurf-bridge/dist/pi-extensions/lib/codex-declaration.js +609 -0
  13. package/mcp/entwurf-bridge/dist/pi-extensions/lib/codex-fresh-preflight.js +95 -56
  14. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-control-rpc.js +40 -0
  15. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-fact-provider.js +6 -2
  16. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-facts.js +7 -1
  17. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-peer-observe.js +67 -2
  18. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-peers-render.js +9 -1
  19. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-self-address.js +24 -0
  20. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +2 -2
  21. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-lock.js +1 -1
  22. package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-composition.js +423 -0
  23. package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-dispatch.js +98 -0
  24. package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-fresh-call.js +1018 -0
  25. package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-placement.js +213 -0
  26. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +46 -273
  27. package/mcp/entwurf-bridge/dist/scripts/peer-facts.js +109 -0
  28. package/mcp/entwurf-bridge/src/index.ts +41 -41
  29. package/mcp/entwurf-bridge/tsconfig.build.json +1 -0
  30. package/package.json +9 -8
  31. package/pi-extensions/entwurf-control.ts +91 -52
  32. package/pi-extensions/lib/acp/acp-client.ts +3 -3
  33. package/pi-extensions/lib/acp/backend-adapter.ts +3 -3
  34. package/pi-extensions/lib/acp/backend.ts +4 -4
  35. package/pi-extensions/lib/acp/event-mapper.ts +5 -5
  36. package/pi-extensions/lib/acp/tool-surface.ts +2 -1
  37. package/pi-extensions/lib/codex-declaration.js +612 -0
  38. package/pi-extensions/lib/codex-fresh-preflight.ts +107 -51
  39. package/pi-extensions/lib/entwurf-control-rpc.ts +46 -0
  40. package/pi-extensions/lib/entwurf-fact-provider.ts +13 -2
  41. package/pi-extensions/lib/entwurf-facts.ts +14 -1
  42. package/pi-extensions/lib/entwurf-peer-observe.ts +72 -2
  43. package/pi-extensions/lib/entwurf-peers-render.ts +9 -1
  44. package/pi-extensions/lib/entwurf-self-address.ts +33 -0
  45. package/pi-extensions/lib/entwurf-v2-contract.ts +2 -2
  46. package/pi-extensions/lib/entwurf-v2-lock.ts +1 -1
  47. package/pi-extensions/lib/entwurf-v2-runner.ts +1 -1
  48. package/pi-extensions/lib/fresh-call-composition.ts +493 -0
  49. package/pi-extensions/lib/fresh-call-dispatch.ts +142 -0
  50. package/pi-extensions/lib/herdr-fresh-call.ts +1378 -0
  51. package/pi-extensions/lib/herdr-placement.ts +276 -0
  52. package/pi-extensions/lib/mux-fresh-call.ts +95 -301
  53. package/run.sh +588 -204
  54. package/scripts/check-acp-usage-accounting.ts +9 -9
  55. package/scripts/check-codex-birth-hook.ts +187 -0
  56. package/scripts/check-entwurf-control-rpc.ts +138 -0
  57. package/scripts/check-entwurf-fact-provider.ts +4 -2
  58. package/scripts/check-entwurf-peers-surface.ts +3 -1
  59. package/scripts/check-entwurf-self-address.ts +68 -0
  60. package/scripts/check-fresh-call-dispatch.ts +301 -0
  61. package/scripts/check-gate-qualification.ts +17 -4
  62. package/scripts/check-herdr-activation.ts +1293 -0
  63. package/scripts/check-herdr-fresh-call.ts +1250 -0
  64. package/scripts/check-herdr-placement.ts +383 -0
  65. package/scripts/check-herdr-plugin-build.ts +842 -0
  66. package/scripts/check-herdr-plugin-profile.ts +371 -0
  67. package/scripts/check-herdr-plugin.ts +523 -0
  68. package/scripts/check-herdr-runtime-bootstrap.ts +1264 -0
  69. package/scripts/check-herdr-sandbox.ts +456 -0
  70. package/scripts/check-herdr-supply.ts +184 -0
  71. package/scripts/check-mux-launcher-fence.ts +138 -0
  72. package/scripts/check-peer-facts.ts +331 -0
  73. package/scripts/check-pi-launch.ts +358 -0
  74. package/scripts/check-release-gate-outcomes.ts +19 -0
  75. package/scripts/check-typing-call-fence.ts +296 -0
  76. package/scripts/codex-birth-doctor.sh +159 -16
  77. package/scripts/codex-birth-install.sh +178 -72
  78. package/scripts/codex-birth-uninstall.sh +162 -7
  79. package/scripts/fixtures/herdr-supply.json +29 -0
  80. package/scripts/herdr-activation.mjs +536 -0
  81. package/scripts/herdr-plugin-activate.mjs +270 -0
  82. package/scripts/herdr-plugin-deactivate.mjs +193 -0
  83. package/scripts/herdr-runtime.mjs +1203 -0
  84. package/scripts/install-herdr-ci.sh +96 -0
  85. package/scripts/inventory-verification-surface.ts +86 -10
  86. package/scripts/lib/codex-fresh-live-protocol.ts +2 -2
  87. package/scripts/lib/tree-digest.ts +97 -0
  88. package/scripts/meta-bridge-install.sh +19 -2
  89. package/scripts/meta-bridge-state.py +167 -11
  90. package/scripts/mutants/codex-caller-seat.json +6 -8
  91. package/scripts/mutants/codex-native.json +195 -29
  92. package/scripts/mutants/control-socket-disconnect.json +57 -0
  93. package/scripts/mutants/fresh-call-dispatch.json +154 -0
  94. package/scripts/mutants/herdr-activation.json +305 -0
  95. package/scripts/mutants/herdr-fresh-call.json +448 -0
  96. package/scripts/mutants/herdr-placement.json +161 -0
  97. package/scripts/mutants/herdr-plugin-build.json +160 -0
  98. package/scripts/mutants/herdr-plugin-profile.json +202 -0
  99. package/scripts/mutants/herdr-plugin.json +133 -0
  100. package/scripts/mutants/herdr-runtime-bootstrap.json +411 -0
  101. package/scripts/mutants/herdr-supply.json +133 -0
  102. package/scripts/mutants/meta-hook-session-switch.json +2 -2
  103. package/scripts/mutants/mux-fresh-call.json +104 -17
  104. package/scripts/mutants/mux-launcher-fence.json +13 -0
  105. package/scripts/mutants/omp-fresh.json +4 -4
  106. package/scripts/mutants/peer-facts.json +98 -0
  107. package/scripts/mutants/pi-launch.json +113 -0
  108. package/scripts/mutants/resume-args.json +6 -6
  109. package/scripts/mutants/self-address.json +33 -0
  110. package/scripts/mutants/typing-call-fence.json +17 -0
  111. package/scripts/mutants/v2-surface.json +2 -2
  112. package/scripts/mutants/v2-visible-resume.json +17 -17
  113. package/scripts/peer-facts.ts +120 -0
  114. package/scripts/register-pi-package.py +8 -0
  115. package/scripts/register-pi-provider.py +88 -12
  116. package/scripts/smoke-acp-raw-turn-live.ts +1 -1
  117. package/scripts/smoke-codex-birth.sh +281 -12
  118. package/scripts/smoke-entwurf-v2-matrix-live.ts +1 -1
  119. package/scripts/smoke-herdr-fresh-call-live.ts +1027 -0
  120. package/scripts/smoke-herdr-plugin-build-live.ts +543 -0
  121. package/scripts/smoke-herdr-raw-install-live.sh +849 -0
  122. package/scripts/tsconfig.json +5 -0
  123. package/scripts/check-entwurf-facts.ts +0 -385
  124. package/scripts/check-entwurf-resume-args.ts +0 -122
  125. package/scripts/check-entwurf-v2-decider.ts +0 -657
  126. package/scripts/check-entwurf-v2-lock.ts +0 -371
  127. package/scripts/check-entwurf-v2-mailbox.ts +0 -261
  128. package/scripts/check-entwurf-v2-matrix.ts +0 -401
  129. package/scripts/check-entwurf-v2-native-push.ts +0 -221
  130. package/scripts/check-entwurf-v2-release.ts +0 -151
  131. package/scripts/check-entwurf-v2-runner.ts +0 -448
  132. package/scripts/check-entwurf-v2-send-fallback.ts +0 -350
  133. package/scripts/check-entwurf-v2-send.ts +0 -458
  134. package/scripts/check-entwurf-v2-visible-resume.ts +0 -445
  135. package/scripts/check-socket-discovery.ts +0 -401
@@ -0,0 +1,160 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "lane": "herdr-plugin-build",
4
+ "mutants": [
5
+ {
6
+ "claim": "HPB-MANIFEST-ONE-RUNNER",
7
+ "title": "handing the build to a shell instead of one argv reintroduces a quoting surface this plugin does not need, and a manifest that can grow a second command is a build order nobody reviewed",
8
+ "subject": "plugins/herdr/herdr-plugin.toml",
9
+ "find": ["command = [\"node\", \"lib/build.mjs\"]"],
10
+ "replace": ["command = [\"bash\", \"-c\", \"node lib/build.mjs\"]"],
11
+ "gate": ["bash", "run.sh", "check-herdr-plugin-build"],
12
+ "timeoutSeconds": 300,
13
+ "signature": "[QK:HPB-MANIFEST-ONE-RUNNER]",
14
+ "signatureSource": "scripts/check-herdr-plugin-build.ts"
15
+ },
16
+ {
17
+ "claim": "HPB-BINARY-REQUIRED",
18
+ "title": "collapsing 'there is no herdr here' into 'herdr could not answer' loses the distinction an operator acts on, and both must stay refusals rather than an invented empty listing that reads as 'nothing is integrated'",
19
+ "subject": "plugins/herdr/lib/build.mjs",
20
+ "find": ["\t\t\t\"herdr-build-binary-missing\","],
21
+ "replace": ["\t\t\t\"herdr-build-status-unavailable\","],
22
+ "gate": ["bash", "run.sh", "check-herdr-plugin-build"],
23
+ "timeoutSeconds": 300,
24
+ "signature": "[QK:HPB-BINARY-REQUIRED]",
25
+ "signatureSource": "scripts/check-herdr-plugin-build.ts"
26
+ },
27
+ {
28
+ "claim": "HPB-SELECTED-FAIL-BEFORE-BOOTSTRAP",
29
+ "title": "proceeding past a selected atom herdr calls outdated or needs-repair installs a runtime for a harness whose integration cannot run it — and `outdated (legacy < vN)` is the measured catch-all for empty and unreadable integration files",
30
+ "subject": "plugins/herdr/lib/build.mjs",
31
+ "find": ["\tif (profile.fail.length > 0) {"],
32
+ "replace": ["\tif (false && profile.fail.length > 0) {"],
33
+ "gate": ["bash", "run.sh", "check-herdr-plugin-build"],
34
+ "timeoutSeconds": 300,
35
+ "signature": "[QK:HPB-SELECTED-FAIL-BEFORE-BOOTSTRAP]",
36
+ "signatureSource": "scripts/check-herdr-plugin-build.ts"
37
+ },
38
+ {
39
+ "claim": "HPB-EMPTY-A-ZERO-WRITE",
40
+ "title": "installing a runtime on a host where nothing is integrated writes megabytes for nobody and leaves this plugin's state on a machine that asked for none of it",
41
+ "subject": "plugins/herdr/lib/build.mjs",
42
+ "find": ["\tif (profile.activate.length === 0) {"],
43
+ "replace": ["\tif (false && profile.activate.length === 0) {"],
44
+ "gate": ["bash", "run.sh", "check-herdr-plugin-build"],
45
+ "timeoutSeconds": 300,
46
+ "signature": "[QK:HPB-EMPTY-A-ZERO-WRITE]",
47
+ "signatureSource": "scripts/check-herdr-plugin-build.ts"
48
+ },
49
+ {
50
+ "claim": "HPB-INSTALLED-ENTRY-ONLY",
51
+ "title": "skipping the capability probe lets a file that exits 0 on an empty request stand in for the activation verb — the shape that already bit this package once, when a main-module guard made every call a silent success",
52
+ "subject": "plugins/herdr/lib/build.mjs",
53
+ "find": ["\tcertifyActivationCapability(entry, env, { spawn, nodeBin });"],
54
+ "replace": ["\tvoid certifyActivationCapability;"],
55
+ "gate": ["bash", "run.sh", "check-herdr-plugin-build"],
56
+ "timeoutSeconds": 300,
57
+ "signature": "[QK:HPB-INSTALLED-ENTRY-ONLY]",
58
+ "signatureSource": "scripts/check-herdr-plugin-build.ts"
59
+ },
60
+ {
61
+ "claim": "HPB-SEQUENTIAL-TWO-STAGE",
62
+ "title": "handing down only the first backend of A leaves the widened set half-activated on exactly the reinstall Herdr's refresh trigger is built on — the operator installs Claude Code, reinstalls the plugin, and nothing points at it",
63
+ "subject": "plugins/herdr/lib/build.mjs",
64
+ "find": ["\tconst activated = spawn(nodeBin, [entry, ...profile.activate], {"],
65
+ "replace": ["\tconst activated = spawn(nodeBin, [entry, profile.activate[0]], {"],
66
+ "gate": ["bash", "run.sh", "check-herdr-plugin-build"],
67
+ "timeoutSeconds": 300,
68
+ "signature": "[QK:HPB-SEQUENTIAL-TWO-STAGE]",
69
+ "signatureSource": "scripts/check-herdr-plugin-build.ts"
70
+ },
71
+ {
72
+ "claim": "HPB-POST-BUILD-GAP",
73
+ "title": "claiming Herdr's registration in this runner's own receipt states a step that has not run: herdr commits its checkout and registry only AFTER the build returns, so the next reconcile would be arguing with a lie",
74
+ "subject": "plugins/herdr/lib/build.mjs",
75
+ "find": ["\t\t`[herdr-plugin-build] activated ${profile.activate.join(\",\")} through the INSTALLED package` +"],
76
+ "replace": [
77
+ "\t\t`[herdr-plugin-build] activated ${profile.activate.join(\",\")} and registered with the herdr registry` +"
78
+ ],
79
+ "gate": ["bash", "run.sh", "check-herdr-plugin-build"],
80
+ "timeoutSeconds": 300,
81
+ "signature": "[QK:HPB-POST-BUILD-GAP]",
82
+ "signatureSource": "scripts/check-herdr-plugin-build.ts"
83
+ },
84
+ {
85
+ "claim": "HPB-PRE-BOOTSTRAP-AUTHORITY",
86
+ "title": "asking the activation authority AFTER the bootstrap is the state the first cut actually produced: the stable root already holds the new artifact, the ledger still names the old one, the run reports failure — and the harness wiring, which names the root and not the version, resolves to bytes no record accounts for",
87
+ "subject": "plugins/herdr/lib/build.mjs",
88
+ "find": ["\tconst disposition = certifyArtifactForForward(ledger, requested, requestedBackends);"],
89
+ "replace": ["\tconst disposition = ledger === null ? \"fresh\" : \"match\";"],
90
+ "gate": ["bash", "run.sh", "check-herdr-plugin-build"],
91
+ "timeoutSeconds": 300,
92
+ "signature": "[QK:HPB-PRE-BOOTSTRAP-AUTHORITY]",
93
+ "signatureSource": "scripts/check-herdr-plugin-build.ts"
94
+ },
95
+ {
96
+ "claim": "HPB-BOOTSTRAP-IDENTITY-EXACT",
97
+ "title": "skipping the post-bootstrap identity comparison hands the installed activation verb a different artifact than the one this build was admitted for, so it re-judges and passes or fails on a question nobody asked",
98
+ "subject": "plugins/herdr/lib/build.mjs",
99
+ "find": ["\tif (!sameArtifactRequest(result.journal.artifactIdentity, plan.requested)) {"],
100
+ "replace": ["\tif (false && !sameArtifactRequest(result.journal.artifactIdentity, plan.requested)) {"],
101
+ "gate": ["bash", "run.sh", "check-herdr-plugin-build"],
102
+ "timeoutSeconds": 300,
103
+ "signature": "[QK:HPB-BOOTSTRAP-IDENTITY-EXACT]",
104
+ "signatureSource": "scripts/check-herdr-plugin-build.ts"
105
+ },
106
+ {
107
+ "claim": "HPB-PROGRESS-NAMED-SEQUENCE",
108
+ "title": "narrating the acquisition without saying what it is reaching for or that silence is expected leaves the operator in front of a terminal that says nothing for minutes while `npm pack` runs — herdr discards this process's streams on success, so a bland step is the same as no step on the one phase that legitimately takes the longest and reads exactly like a hang",
109
+ "subject": "plugins/herdr/lib/build.mjs",
110
+ "find": [
111
+ "\tprogress.step(",
112
+ "\t\t`fetching and installing the Entwurf runtime from ${describeRequest(plan.requested)} — this is the long step ` +",
113
+ "\t\t\t\"(npm acquires the locked artifact; expect minutes of silence)\",",
114
+ "\t);"
115
+ ],
116
+ "replace": ["\tprogress.step(\"working\");"],
117
+ "gate": ["bash", "run.sh", "check-herdr-plugin-build"],
118
+ "timeoutSeconds": 300,
119
+ "signature": "[QK:HPB-PROGRESS-NAMED-SEQUENCE]",
120
+ "signatureSource": "scripts/check-herdr-plugin-build.ts"
121
+ },
122
+ {
123
+ "claim": "HPB-PROGRESS-TTY-OPTIONAL",
124
+ "title": "mirroring only when a terminal was opened throws the failure trail away on exactly the hosts that cannot watch it happen — CI and any piped run then report the error with nothing in front of it, and the stderr herdr prints on failure is the only place those steps could still have been read",
125
+ "subject": "plugins/herdr/lib/build-progress.mjs",
126
+ "find": ["\t\t}", "\t\tmirror(line);", "\t};"],
127
+ "replace": ["\t\t\tmirror(line);", "\t\t}", "\t};"],
128
+ "gate": ["bash", "run.sh", "check-herdr-plugin-build"],
129
+ "timeoutSeconds": 300,
130
+ "signature": "[QK:HPB-PROGRESS-TTY-OPTIONAL]",
131
+ "signatureSource": "scripts/check-herdr-plugin-build.ts"
132
+ },
133
+ {
134
+ "claim": "HPB-USAGE-NOTE-NAMES-A-MODEL",
135
+ "title": "the install names a model id in the wrong harness's grammar, so the first fresh call an operator copies is refused",
136
+ "subject": "plugins/herdr/lib/build.mjs",
137
+ "find": [
138
+ "\t\tnotes.push(\"claude-code: a model id looks like `claude-sonnet-5` — the vendor id, not a provider path.\");"
139
+ ],
140
+ "replace": [
141
+ "\t\tnotes.push(\"claude-code: a model id looks like `anthropic/claude-sonnet-5` — provider-qualified.\");"
142
+ ],
143
+ "gate": ["bash", "run.sh", "check-herdr-plugin-build"],
144
+ "timeoutSeconds": 180,
145
+ "signature": "[QK:HPB-USAGE-NOTE-NAMES-A-MODEL]",
146
+ "signatureSource": "scripts/check-herdr-plugin-build.ts"
147
+ },
148
+ {
149
+ "claim": "HPB-INSTALL-USAGE-NOTE",
150
+ "title": "dropping the pi line leaves the one backend whose wiring is invisible from the operator's chair unexplained: claude-code picks the tools up through MCP with nothing added, while a plain pi loads the extension on every session of the host and is still not a citizen — a green install followed by a silent, tool-less pi is indistinguishable from an install that did nothing",
151
+ "subject": "plugins/herdr/lib/build.mjs",
152
+ "find": ["\tif (activated.includes(\"pi\")) {"],
153
+ "replace": ["\tif (false && activated.includes(\"pi\")) {"],
154
+ "gate": ["bash", "run.sh", "check-herdr-plugin-build"],
155
+ "timeoutSeconds": 300,
156
+ "signature": "[QK:HPB-INSTALL-USAGE-NOTE]",
157
+ "signatureSource": "scripts/check-herdr-plugin-build.ts"
158
+ }
159
+ ]
160
+ }
@@ -0,0 +1,202 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "lane": "herdr-plugin-profile",
4
+ "mutants": [
5
+ {
6
+ "claim": "HIP-CLOSED-MAP",
7
+ "title": "widening the table by one atom is how an unsupported backend becomes supported without a decision: OpenCode is installed and `current` on real hosts, GLG froze P at {pi, claude-code} (#116 issuecomment-5690300559), and the plugin that admits it writes Entwurf state into a harness this lane never agreed to own",
8
+ "subject": "plugins/herdr/lib/integration-profile.mjs",
9
+ "find": ["export const SELECTED_ATOMS = Object.freeze({ pi: \"pi\", claude: \"claude-code\" });"],
10
+ "replace": [
11
+ "export const SELECTED_ATOMS = Object.freeze({ pi: \"pi\", claude: \"claude-code\", opencode: \"opencode\" });"
12
+ ],
13
+ "gate": ["bash", "run.sh", "check-herdr-plugin-profile"],
14
+ "timeoutSeconds": 180,
15
+ "signature": "[QK:HIP-CLOSED-MAP]",
16
+ "signatureSource": "scripts/check-herdr-plugin-profile.ts"
17
+ },
18
+ {
19
+ "claim": "HIP-ROW-MISSING-REFUSED",
20
+ "title": "an absent selected row that is not refused by name leaves the caller holding whatever the next line of code makes of `declared[0]` — an unnamed crash, or worse a default, for a question herdr never answered (#116 M3-a)",
21
+ "subject": "plugins/herdr/lib/integration-profile.mjs",
22
+ "find": ["\tif (declared.length === 0) {"],
23
+ "replace": ["\tif (false) {"],
24
+ "gate": ["bash", "run.sh", "check-herdr-plugin-profile"],
25
+ "timeoutSeconds": 180,
26
+ "signature": "[QK:HIP-ROW-MISSING-REFUSED]",
27
+ "signatureSource": "scripts/check-herdr-plugin-profile.ts"
28
+ },
29
+ {
30
+ "claim": "HIP-ROW-DUPLICATE-REFUSED",
31
+ "title": "taking the first match when a selected atom has two rows resolves a contradiction into a guess — the listing that says both `current` and `not installed` for pi is not a listing we understand, and the quiet winner decides whether an integration gets installed over (#116 M3-a)",
32
+ "subject": "plugins/herdr/lib/integration-profile.mjs",
33
+ "find": ["\tif (declared.length > 1) {"],
34
+ "replace": ["\tif (false) {"],
35
+ "gate": ["bash", "run.sh", "check-herdr-plugin-profile"],
36
+ "timeoutSeconds": 180,
37
+ "signature": "[QK:HIP-ROW-DUPLICATE-REFUSED]",
38
+ "signatureSource": "scripts/check-herdr-plugin-profile.ts"
39
+ },
40
+ {
41
+ "claim": "HIP-SELECTED-DELIMITER-FRAMED",
42
+ "title": "claiming a line only at `<atom>: ` lets a broken declaration by the SAME atom pass as unrelated text: `pi:current (v1) …` sitting beside a valid `pi: ` row contradicts it and changes nothing, and standing alone it reads as a missing row rather than the malformed one it is (#116 M3-a cross-review defect 3)",
43
+ "subject": "plugins/herdr/lib/integration-profile.mjs",
44
+ "find": ["\tconst declared = lines.filter((line) => line.startsWith(`${atom}:`));"],
45
+ "replace": ["\tconst declared = lines.filter((line) => line.startsWith(prefix));"],
46
+ "gate": ["bash", "run.sh", "check-herdr-plugin-profile"],
47
+ "timeoutSeconds": 180,
48
+ "signature": "[QK:HIP-SELECTED-DELIMITER-FRAMED]",
49
+ "signatureSource": "scripts/check-herdr-plugin-profile.ts"
50
+ },
51
+ {
52
+ "claim": "HIP-ROW-MALFORMED-REFUSED",
53
+ "title": "defaulting an unreadable state to `not installed` turns a phrase nobody has interpreted into a clean zero-write SKIP — the next state herdr ships would be walked past and reported as success (#116 M3-a)",
54
+ "subject": "plugins/herdr/lib/integration-profile.mjs",
55
+ "find": [
56
+ "\tthrow new IntegrationProfileError(",
57
+ "\t\t\"herdr-status-row-malformed\",",
58
+ "\t\t`unreadable \\`${prefix}\\` row: ${JSON.stringify(remainder)}`,",
59
+ "\t);"
60
+ ],
61
+ "replace": [
62
+ "\treturn Object.freeze({ atom, backend: SELECTED_ATOMS[atom], state: \"not-installed\", installedVersion: null, floorVersion: null });"
63
+ ],
64
+ "gate": ["bash", "run.sh", "check-herdr-plugin-profile"],
65
+ "timeoutSeconds": 180,
66
+ "signature": "[QK:HIP-ROW-MALFORMED-REFUSED]",
67
+ "signatureSource": "scripts/check-herdr-plugin-profile.ts"
68
+ },
69
+ {
70
+ "claim": "HIP-SUFFIX-FRAMING",
71
+ "title": "reading only the head of a row and trusting the rest is the hole cross-review found (Terra, reproduced by the coordinator 2026-09-16): `current (v8)` with no suffix, with a relative path, with the closing paren missing, with `()`, and with trailing bytes after the `)` ALL activated — a row nobody finished reading decided that a harness was installed (#116 M3-a blocker 1)",
72
+ "subject": "plugins/herdr/lib/integration-profile.mjs",
73
+ "find": [
74
+ "\t{ state: \"current\", re: /^current \\(v(\\d+)\\) \\(\\/.*\\)$/, installed: (m) => Number(m[1]), floor: () => null },"
75
+ ],
76
+ "replace": [
77
+ "\t{ state: \"current\", re: /^current \\(v(\\d+)\\)(?: \\(|$)/, installed: (m) => Number(m[1]), floor: () => null },"
78
+ ],
79
+ "gate": ["bash", "run.sh", "check-herdr-plugin-profile"],
80
+ "timeoutSeconds": 180,
81
+ "signature": "[QK:HIP-SUFFIX-FRAMING]",
82
+ "signatureSource": "scripts/check-herdr-plugin-profile.ts"
83
+ },
84
+ {
85
+ "claim": "HIP-VERSION-U32",
86
+ "title": "herdr's integration version is a Rust u32, so a 400-digit capture becomes Infinity and serialises to null: the plan would carry 'no version' for a row that loudly stated one, and every downstream comparison against it is then made against a value the listing never contained (#116 M3-a observation 4)",
87
+ "subject": "plugins/herdr/lib/integration-profile.mjs",
88
+ "find": ["\t\tif (m.slice(1).some((captured) => !isU32(captured))) break;"],
89
+ "replace": ["\t\tif (false) break;"],
90
+ "gate": ["bash", "run.sh", "check-herdr-plugin-profile"],
91
+ "timeoutSeconds": 180,
92
+ "signature": "[QK:HIP-VERSION-U32]",
93
+ "signatureSource": "scripts/check-herdr-plugin-profile.ts"
94
+ },
95
+ {
96
+ "claim": "HIP-STATE-FRONT-ANCHORED",
97
+ "title": "dropping the `^` anchor lets the operator's own directory name answer the question: with an unanchored pattern `pi: outdated (legacy < v8) (/srv/current (v9) (/x)/…)` reads as CURRENT, so a broken integration is activated because someone named a folder `current (v9)` (#116 M3-a)",
98
+ "subject": "plugins/herdr/lib/integration-profile.mjs",
99
+ "find": [
100
+ "\t{ state: \"current\", re: /^current \\(v(\\d+)\\) \\(\\/.*\\)$/, installed: (m) => Number(m[1]), floor: () => null },"
101
+ ],
102
+ "replace": [
103
+ "\t{ state: \"current\", re: /current \\(v(\\d+)\\) \\(\\/.*\\)$/, installed: (m) => Number(m[1]), floor: () => null },"
104
+ ],
105
+ "gate": ["bash", "run.sh", "check-herdr-plugin-profile"],
106
+ "timeoutSeconds": 180,
107
+ "signature": "[QK:HIP-STATE-FRONT-ANCHORED]",
108
+ "signatureSource": "scripts/check-herdr-plugin-profile.ts"
109
+ },
110
+ {
111
+ "claim": "HIP-PATH-IS-NOT-AUTHORITY",
112
+ "title": "recovering the display path into the plan puts an address in the one module that must not carry one — the same bytes read `current` or `not installed` depending only on PI_CODING_AGENT_DIR (measured 2026-09-16), so a path resolved HERE and a path installed against THERE are two answers nobody reconciles (#116 M3-a)",
113
+ "subject": "plugins/herdr/lib/integration-profile.mjs",
114
+ "find": [
115
+ "\t\treturn Object.freeze({",
116
+ "\t\t\tatom,",
117
+ "\t\t\tbackend: SELECTED_ATOMS[atom],",
118
+ "\t\t\tstate: entry.state,",
119
+ "\t\t\tinstalledVersion: entry.installed(m),",
120
+ "\t\t\tfloorVersion: entry.floor(m),",
121
+ "\t\t});"
122
+ ],
123
+ "replace": [
124
+ "\t\treturn Object.freeze({",
125
+ "\t\t\tatom,",
126
+ "\t\t\tbackend: SELECTED_ATOMS[atom],",
127
+ "\t\t\tstate: entry.state,",
128
+ "\t\t\tinstalledVersion: entry.installed(m),",
129
+ "\t\t\tfloorVersion: entry.floor(m),",
130
+ "\t\t\tdisplayPath: /\\(([^)]*)\\)\\s*$/.exec(row)?.[1] ?? null,",
131
+ "\t\t});"
132
+ ],
133
+ "gate": ["bash", "run.sh", "check-herdr-plugin-profile"],
134
+ "timeoutSeconds": 180,
135
+ "signature": "[QK:HIP-PATH-IS-NOT-AUTHORITY]",
136
+ "signatureSource": "scripts/check-herdr-plugin-profile.ts"
137
+ },
138
+ {
139
+ "claim": "HIP-OUTDATED-IS-FAIL",
140
+ "title": "classifying `outdated` as a skip hides the exact case the M3 contract exists to stop: measured, an empty file, a chmod-000 file and a marker-less hand-written file ALL print `outdated (legacy < vN)`, so a skip there walks past a broken integration and reports a clean run (#116 issuecomment-5689767140)",
141
+ "subject": "plugins/herdr/lib/integration-profile.mjs",
142
+ "find": ["\toutdated: \"fail\","],
143
+ "replace": ["\toutdated: \"skip\","],
144
+ "gate": ["bash", "run.sh", "check-herdr-plugin-profile"],
145
+ "timeoutSeconds": 180,
146
+ "signature": "[QK:HIP-OUTDATED-IS-FAIL]",
147
+ "signatureSource": "scripts/check-herdr-plugin-profile.ts"
148
+ },
149
+ {
150
+ "claim": "HIP-ABSENT-IS-SKIP-ZERO-WRITE",
151
+ "title": "turning `not installed` into a failure makes every host that simply has not added that Herdr integration look broken, and a red is the pressure that invites the plugin to go install the harness it must never install (AGENTS.md Hard Rule 17, #116 M3-a)",
152
+ "subject": "plugins/herdr/lib/integration-profile.mjs",
153
+ "find": ["\t\"not-installed\": \"skip\","],
154
+ "replace": ["\t\"not-installed\": \"fail\","],
155
+ "gate": ["bash", "run.sh", "check-herdr-plugin-profile"],
156
+ "timeoutSeconds": 180,
157
+ "signature": "[QK:HIP-ABSENT-IS-SKIP-ZERO-WRITE]",
158
+ "signatureSource": "scripts/check-herdr-plugin-profile.ts"
159
+ },
160
+ {
161
+ "claim": "HIP-CURRENT-IS-ADMISSION-ONLY",
162
+ "title": "inventing a second version floor rejects hosts herdr already admitted while proving nothing about the bytes — `current` is one marker line (measured: bytes appended after it still read current, and v999 reads current), so a floor here is a check that looks like integrity and is not (#116 M3-a)",
163
+ "subject": "plugins/herdr/lib/integration-profile.mjs",
164
+ "find": ["\t\tconst verdict = VERDICT[fact.state];"],
165
+ "replace": [
166
+ "\t\tconst verdict = fact.state === \"current\" && (fact.installedVersion ?? 0) < 8 ? \"fail\" : VERDICT[fact.state];"
167
+ ],
168
+ "gate": ["bash", "run.sh", "check-herdr-plugin-profile"],
169
+ "timeoutSeconds": 180,
170
+ "signature": "[QK:HIP-CURRENT-IS-ADMISSION-ONLY]",
171
+ "signatureSource": "scripts/check-herdr-plugin-profile.ts"
172
+ },
173
+ {
174
+ "claim": "HIP-UNSELECTED-NEVER-ACTIVATES",
175
+ "title": "building the plan from whatever atoms the listing happens to contain makes set P the listing instead of our decision: OpenCode enters the plan, and one unreadable row for an atom nobody supports refuses the whole listing and blocks Pi and Claude Code with it (#116 issuecomment-5690300559)",
176
+ "subject": "plugins/herdr/lib/integration-profile.mjs",
177
+ "find": ["\tconst selected = Object.keys(SELECTED_ATOMS).map((atom) => readSelectedRow(lines, atom));"],
178
+ "replace": [
179
+ "\tconst selected = observeOtherAtoms(lines)\n\t\t.concat(Object.keys(SELECTED_ATOMS))\n\t\t.map((atom) => readSelectedRow(lines, atom));"
180
+ ],
181
+ "gate": ["bash", "run.sh", "check-herdr-plugin-profile"],
182
+ "timeoutSeconds": 180,
183
+ "signature": "[QK:HIP-UNSELECTED-NEVER-ACTIVATES]",
184
+ "signatureSource": "scripts/check-herdr-plugin-profile.ts"
185
+ },
186
+ {
187
+ "claim": "HIP-PURE-LEAF",
188
+ "title": "one import is all it takes for the plan to stop being a property of the listing — a leaf that can reach the filesystem or the environment can disagree with the bootstrap that acts on its answer about which host they are both standing on, and that disagreement is unobservable from the plan (#116 M3-a)",
189
+ "subject": "plugins/herdr/lib/integration-profile.mjs",
190
+ "find": ["export const SELECTED_ATOMS = Object.freeze({ pi: \"pi\", claude: \"claude-code\" });"],
191
+ "replace": [
192
+ "import fs from \"node:fs\";",
193
+ "",
194
+ "export const SELECTED_ATOMS = Object.freeze({ pi: \"pi\", claude: \"claude-code\" });"
195
+ ],
196
+ "gate": ["bash", "run.sh", "check-herdr-plugin-profile"],
197
+ "timeoutSeconds": 180,
198
+ "signature": "[QK:HIP-PURE-LEAF]",
199
+ "signatureSource": "scripts/check-herdr-plugin-profile.ts"
200
+ }
201
+ ]
202
+ }
@@ -0,0 +1,133 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "lane": "herdr-plugin",
4
+ "mutants": [
5
+ {
6
+ "claim": "HPL-ONE-OPEN-ONE-READ",
7
+ "title": "re-reading either listing turns one answered question into a poll — peer-facts observes every citizen unbounded (measured 4.1s on a 1,150-record store) and herdr publishes no 'the session reference landed' event, so a second read is both the slow path and the discovery watcher docs/mux-launch-rail.md §7 refuses by name (#116 M2-b)",
8
+ "subject": "plugins/herdr/lib/status.mjs",
9
+ "find": ["\t\t\tconst agents = readJson(herdrBin, [\"agent\", \"list\"], {"],
10
+ "replace": [
11
+ "\t\t\treadJson(herdrBin, [\"agent\", \"list\"], { failed: \"x\", unparsable: \"y\" });\n\t\t\tconst agents = readJson(herdrBin, [\"agent\", \"list\"], {"
12
+ ],
13
+ "gate": ["bash", "run.sh", "check-herdr-plugin"],
14
+ "timeoutSeconds": 180,
15
+ "signature": "[QK:HPL-ONE-OPEN-ONE-READ]",
16
+ "signatureSource": "scripts/check-herdr-plugin.ts"
17
+ },
18
+ {
19
+ "claim": "HPL-SKIP-NOT-FAILURE",
20
+ "title": "turning a missing Entwurf into a red makes every host that simply has not installed it look broken, and invites the plugin to 'fix' it — installing Entwurf is not a plugin's business (AGENTS.md Hard Rule 17, #116 M2-b)",
21
+ "subject": "plugins/herdr/lib/status.mjs",
22
+ "find": ["\tfinish([\"entwurf-not-found\"], OK);"],
23
+ "replace": ["\tfinish([\"entwurf-not-found\"], RED);"],
24
+ "gate": ["bash", "run.sh", "check-herdr-plugin"],
25
+ "timeoutSeconds": 180,
26
+ "signature": "[QK:HPL-SKIP-NOT-FAILURE]",
27
+ "signatureSource": "scripts/check-herdr-plugin.ts"
28
+ },
29
+ {
30
+ "claim": "HPL-FAILED-READ-IS-RED",
31
+ "title": "treating a nonzero listing exit as success renders an empty table for a listing that was never read — a host whose store could not be opened becomes indistinguishable from a host with no citizens (#116 M2-b)",
32
+ "subject": "plugins/herdr/lib/status.mjs",
33
+ "find": [
34
+ "\tif (run.status !== 0) return { error: names.failed, detail: `exit ${run.status}: ${(run.stderr ?? \"\").trim()}` };"
35
+ ],
36
+ "replace": ["\tif (false) return { error: names.failed, detail: `exit ${run.status}` };"],
37
+ "gate": ["bash", "run.sh", "check-herdr-plugin"],
38
+ "timeoutSeconds": 180,
39
+ "signature": "[QK:HPL-FAILED-READ-IS-RED]",
40
+ "signatureSource": "scripts/check-herdr-plugin.ts"
41
+ },
42
+ {
43
+ "claim": "HPL-AMBIGUOUS-NOT-GUESSED",
44
+ "title": "resolving two agent rows on one pane to whichever came first is a guess wearing a value's clothes — the only worth of this column is that it never guesses (#116 M2-b)",
45
+ "subject": "plugins/herdr/lib/status.mjs",
46
+ "find": ["\t\tbyPane.set(paneId, byPane.has(paneId) ? \"ambiguous\" : status);"],
47
+ "replace": ["\t\tif (!byPane.has(paneId)) byPane.set(paneId, status);"],
48
+ "gate": ["bash", "run.sh", "check-herdr-plugin"],
49
+ "timeoutSeconds": 180,
50
+ "signature": "[QK:HPL-AMBIGUOUS-NOT-GUESSED]",
51
+ "signatureSource": "scripts/check-herdr-plugin.ts"
52
+ },
53
+ {
54
+ "claim": "HPL-ACTIVITY-IS-SEPARATE",
55
+ "title": "labelling herdr's agent_status as anything but herdr's own report starts the drift that ends with a screen judgement being read as Entwurf delivery liveness (docs/herdr-launch-rail.md §9, #116 M2-b)",
56
+ "subject": "plugins/herdr/lib/status.mjs",
57
+ "find": ["\tconst head = [\"garden id\", \"backend\", \"cwd\", \"placement\", \"herdr-reported activity\"];"],
58
+ "replace": ["\tconst head = [\"garden id\", \"backend\", \"cwd\", \"placement\", \"liveness\"];"],
59
+ "gate": ["bash", "run.sh", "check-herdr-plugin"],
60
+ "timeoutSeconds": 180,
61
+ "signature": "[QK:HPL-ACTIVITY-IS-SEPARATE]",
62
+ "signatureSource": "scripts/check-herdr-plugin.ts"
63
+ },
64
+ {
65
+ "claim": "HPL-DIAGNOSTICS-SHOWN",
66
+ "title": "dropping the provider's diagnostics shows a store carrying hazards as a clean listing — the one lie a status pane can tell while looking green (#116 M2-b)",
67
+ "subject": "plugins/herdr/lib/status.mjs",
68
+ "find": ["\t\t\t\t\t\t...renderDiagnostics(diagnostics),"],
69
+ "replace": ["\t\t\t\t\t\t...renderDiagnostics([]),"],
70
+ "gate": ["bash", "run.sh", "check-herdr-plugin"],
71
+ "timeoutSeconds": 180,
72
+ "signature": "[QK:HPL-DIAGNOSTICS-SHOWN]",
73
+ "signatureSource": "scripts/check-herdr-plugin.ts"
74
+ },
75
+ {
76
+ "claim": "HPL-JOIN-PANE-ID-ONLY",
77
+ "title": "joining on a native session id forks the vendor floor Entwurf owns in pi-extensions/lib/herdr-placement.ts into a file no gate and no document covers — the whole join a plugin may make is one opaque pane-id string equality (#116 M2-b)",
78
+ "subject": "plugins/herdr/lib/status.mjs",
79
+ "find": ["\t\tconst paneId = kind === \"herdr-pane\" ? placement.paneId : null;"],
80
+ "replace": ["\t\tconst paneId = typeof peer?.nativeSessionId === \"string\" ? peer.nativeSessionId : null;"],
81
+ "gate": ["bash", "run.sh", "check-herdr-plugin"],
82
+ "timeoutSeconds": 180,
83
+ "signature": "[QK:HPL-JOIN-PANE-ID-ONLY]",
84
+ "signatureSource": "scripts/check-herdr-plugin.ts"
85
+ },
86
+ {
87
+ "claim": "HPL-NO-WRITE",
88
+ "title": "keeping state in HERDR_PLUGIN_STATE_DIR gives a status pane something that can be stale — this one is only ever as old as the keypress that opened it, and that is the whole guarantee (#116 M2-b)",
89
+ "subject": "plugins/herdr/lib/status.mjs",
90
+ "find": ["\tholdOpen().then(() => process.exit(code));"],
91
+ "replace": [
92
+ "\tif (process.env.HERDR_PLUGIN_STATE_DIR) fs.writeFileSync(path.join(process.env.HERDR_PLUGIN_STATE_DIR, \"last.json\"), lines.join(\"\\n\"));\n\tholdOpen().then(() => process.exit(code));"
93
+ ],
94
+ "gate": ["bash", "run.sh", "check-herdr-plugin"],
95
+ "timeoutSeconds": 180,
96
+ "signature": "[QK:HPL-NO-WRITE]",
97
+ "signatureSource": "scripts/check-herdr-plugin.ts"
98
+ },
99
+ {
100
+ "claim": "HPL-MANIFEST-NO-FORBIDDEN-SECTIONS",
101
+ "title": "[[build]] became this plugin's ONE door in #116 M3-b3 and left the forbidden list, so the defect this claim must still catch is the volunteering kind: a [[startup]] hook runs on every herdr server start, in the operator's real HOME, without anyone asking — which is exactly the watcher-shaped growth this lane refuses (#116 M2-b, M3-b3)",
102
+ "subject": "plugins/herdr/herdr-plugin.toml",
103
+ "find": ["[[panes]]"],
104
+ "replace": ["[[startup]]\ncommand = [\"node\", \"lib/status.mjs\"]\n\n[[panes]]"],
105
+ "gate": ["bash", "run.sh", "check-herdr-plugin"],
106
+ "timeoutSeconds": 180,
107
+ "signature": "[QK:HPL-MANIFEST-NO-FORBIDDEN-SECTIONS]",
108
+ "signatureSource": "scripts/check-herdr-plugin.ts"
109
+ },
110
+ {
111
+ "claim": "HPL-RENDER-IS-THE-VISIBLE-ONES",
112
+ "title": "drawing every retained citizen turns the status pane into the whole store — measured 2026-09-15 the operator's own store is 1,162 records, all unobserved, 17,437 lines of peer-facts output, so finding the two citizens actually in this Herdr session becomes a scrolling exercise and a status surface you must search has already failed (#116 M2-b)",
113
+ "subject": "plugins/herdr/lib/status.mjs",
114
+ "find": ["\t\tif (kind !== \"herdr-pane\" && kind !== \"ambiguous\") {"],
115
+ "replace": ["\t\tif (false) {"],
116
+ "gate": ["bash", "run.sh", "check-herdr-plugin"],
117
+ "timeoutSeconds": 180,
118
+ "signature": "[QK:HPL-RENDER-IS-THE-VISIBLE-ONES]",
119
+ "signatureSource": "scripts/check-herdr-plugin.ts"
120
+ },
121
+ {
122
+ "claim": "HPL-DIAGNOSTIC-SUBJECTS",
123
+ "title": "printing a diagnostic's kind and prose without its subject tells an operator that a hazard exists and not where — `record-less-socket` without its garden id and `meta-record-read-error` without its filename are lines nobody can act on (#116 M2-b)",
124
+ "subject": "plugins/herdr/lib/status.mjs",
125
+ "find": ["\t\t\tconst fields = Object.keys(d ?? {})"],
126
+ "replace": ["\t\t\tconst fields = [] || Object.keys(d ?? {})"],
127
+ "gate": ["bash", "run.sh", "check-herdr-plugin"],
128
+ "timeoutSeconds": 180,
129
+ "signature": "[QK:HPL-DIAGNOSTIC-SUBJECTS]",
130
+ "signatureSource": "scripts/check-herdr-plugin.ts"
131
+ }
132
+ ]
133
+ }