@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.
- package/AGENTS.md +4 -1
- package/BASELINE.md +1 -0
- package/CHANGELOG.md +514 -0
- package/DELIVERY.md +10 -2
- package/README.md +43 -4
- package/VERIFY.md +47 -0
- package/docs/acp-backend-rail.md +96 -17
- package/docs/setup-clean-host.md +11 -1
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +39 -40
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +2 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/tool-surface.js +2 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/codex-declaration.js +609 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/codex-fresh-preflight.js +95 -56
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-control-rpc.js +40 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-fact-provider.js +6 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-facts.js +7 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-peer-observe.js +67 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-peers-render.js +9 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-self-address.js +24 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +2 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-lock.js +1 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-composition.js +423 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-dispatch.js +98 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-fresh-call.js +1018 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-placement.js +213 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +46 -273
- package/mcp/entwurf-bridge/dist/scripts/peer-facts.js +109 -0
- package/mcp/entwurf-bridge/src/index.ts +41 -41
- package/mcp/entwurf-bridge/tsconfig.build.json +1 -0
- package/package.json +9 -8
- package/pi-extensions/entwurf-control.ts +91 -52
- package/pi-extensions/lib/acp/acp-client.ts +3 -3
- package/pi-extensions/lib/acp/backend-adapter.ts +3 -3
- package/pi-extensions/lib/acp/backend.ts +4 -4
- package/pi-extensions/lib/acp/event-mapper.ts +5 -5
- package/pi-extensions/lib/acp/tool-surface.ts +2 -1
- package/pi-extensions/lib/codex-declaration.js +612 -0
- package/pi-extensions/lib/codex-fresh-preflight.ts +107 -51
- package/pi-extensions/lib/entwurf-control-rpc.ts +46 -0
- package/pi-extensions/lib/entwurf-fact-provider.ts +13 -2
- package/pi-extensions/lib/entwurf-facts.ts +14 -1
- package/pi-extensions/lib/entwurf-peer-observe.ts +72 -2
- package/pi-extensions/lib/entwurf-peers-render.ts +9 -1
- package/pi-extensions/lib/entwurf-self-address.ts +33 -0
- package/pi-extensions/lib/entwurf-v2-contract.ts +2 -2
- package/pi-extensions/lib/entwurf-v2-lock.ts +1 -1
- package/pi-extensions/lib/entwurf-v2-runner.ts +1 -1
- package/pi-extensions/lib/fresh-call-composition.ts +493 -0
- package/pi-extensions/lib/fresh-call-dispatch.ts +142 -0
- package/pi-extensions/lib/herdr-fresh-call.ts +1378 -0
- package/pi-extensions/lib/herdr-placement.ts +276 -0
- package/pi-extensions/lib/mux-fresh-call.ts +95 -301
- package/run.sh +588 -204
- package/scripts/check-acp-usage-accounting.ts +9 -9
- package/scripts/check-codex-birth-hook.ts +187 -0
- package/scripts/check-entwurf-control-rpc.ts +138 -0
- package/scripts/check-entwurf-fact-provider.ts +4 -2
- package/scripts/check-entwurf-peers-surface.ts +3 -1
- package/scripts/check-entwurf-self-address.ts +68 -0
- package/scripts/check-fresh-call-dispatch.ts +301 -0
- package/scripts/check-gate-qualification.ts +17 -4
- package/scripts/check-herdr-activation.ts +1293 -0
- package/scripts/check-herdr-fresh-call.ts +1250 -0
- package/scripts/check-herdr-placement.ts +383 -0
- package/scripts/check-herdr-plugin-build.ts +842 -0
- package/scripts/check-herdr-plugin-profile.ts +371 -0
- package/scripts/check-herdr-plugin.ts +523 -0
- package/scripts/check-herdr-runtime-bootstrap.ts +1264 -0
- package/scripts/check-herdr-sandbox.ts +456 -0
- package/scripts/check-herdr-supply.ts +184 -0
- package/scripts/check-mux-launcher-fence.ts +138 -0
- package/scripts/check-peer-facts.ts +331 -0
- package/scripts/check-pi-launch.ts +358 -0
- package/scripts/check-release-gate-outcomes.ts +19 -0
- package/scripts/check-typing-call-fence.ts +296 -0
- package/scripts/codex-birth-doctor.sh +159 -16
- package/scripts/codex-birth-install.sh +178 -72
- package/scripts/codex-birth-uninstall.sh +162 -7
- package/scripts/fixtures/herdr-supply.json +29 -0
- package/scripts/herdr-activation.mjs +536 -0
- package/scripts/herdr-plugin-activate.mjs +270 -0
- package/scripts/herdr-plugin-deactivate.mjs +193 -0
- package/scripts/herdr-runtime.mjs +1203 -0
- package/scripts/install-herdr-ci.sh +96 -0
- package/scripts/inventory-verification-surface.ts +86 -10
- package/scripts/lib/codex-fresh-live-protocol.ts +2 -2
- package/scripts/lib/tree-digest.ts +97 -0
- package/scripts/meta-bridge-install.sh +19 -2
- package/scripts/meta-bridge-state.py +167 -11
- package/scripts/mutants/codex-caller-seat.json +6 -8
- package/scripts/mutants/codex-native.json +195 -29
- package/scripts/mutants/control-socket-disconnect.json +57 -0
- package/scripts/mutants/fresh-call-dispatch.json +154 -0
- package/scripts/mutants/herdr-activation.json +305 -0
- package/scripts/mutants/herdr-fresh-call.json +448 -0
- package/scripts/mutants/herdr-placement.json +161 -0
- package/scripts/mutants/herdr-plugin-build.json +160 -0
- package/scripts/mutants/herdr-plugin-profile.json +202 -0
- package/scripts/mutants/herdr-plugin.json +133 -0
- package/scripts/mutants/herdr-runtime-bootstrap.json +411 -0
- package/scripts/mutants/herdr-supply.json +133 -0
- package/scripts/mutants/meta-hook-session-switch.json +2 -2
- package/scripts/mutants/mux-fresh-call.json +104 -17
- package/scripts/mutants/mux-launcher-fence.json +13 -0
- package/scripts/mutants/omp-fresh.json +4 -4
- package/scripts/mutants/peer-facts.json +98 -0
- package/scripts/mutants/pi-launch.json +113 -0
- package/scripts/mutants/resume-args.json +6 -6
- package/scripts/mutants/self-address.json +33 -0
- package/scripts/mutants/typing-call-fence.json +17 -0
- package/scripts/mutants/v2-surface.json +2 -2
- package/scripts/mutants/v2-visible-resume.json +17 -17
- package/scripts/peer-facts.ts +120 -0
- package/scripts/register-pi-package.py +8 -0
- package/scripts/register-pi-provider.py +88 -12
- package/scripts/smoke-acp-raw-turn-live.ts +1 -1
- package/scripts/smoke-codex-birth.sh +281 -12
- package/scripts/smoke-entwurf-v2-matrix-live.ts +1 -1
- package/scripts/smoke-herdr-fresh-call-live.ts +1027 -0
- package/scripts/smoke-herdr-plugin-build-live.ts +543 -0
- package/scripts/smoke-herdr-raw-install-live.sh +849 -0
- package/scripts/tsconfig.json +5 -0
- package/scripts/check-entwurf-facts.ts +0 -385
- package/scripts/check-entwurf-resume-args.ts +0 -122
- package/scripts/check-entwurf-v2-decider.ts +0 -657
- package/scripts/check-entwurf-v2-lock.ts +0 -371
- package/scripts/check-entwurf-v2-mailbox.ts +0 -261
- package/scripts/check-entwurf-v2-matrix.ts +0 -401
- package/scripts/check-entwurf-v2-native-push.ts +0 -221
- package/scripts/check-entwurf-v2-release.ts +0 -151
- package/scripts/check-entwurf-v2-runner.ts +0 -448
- package/scripts/check-entwurf-v2-send-fallback.ts +0 -350
- package/scripts/check-entwurf-v2-send.ts +0 -458
- package/scripts/check-entwurf-v2-visible-resume.ts +0 -445
- package/scripts/check-socket-discovery.ts +0 -401
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* check-herdr-plugin-profile — deterministic gate for the #116 M3-a pure leaf
|
|
3
|
+
* (`plugins/herdr/lib/integration-profile.mjs`), the function that turns ONE
|
|
4
|
+
* `herdr integration status` listing into this plugin's activation plan.
|
|
5
|
+
*
|
|
6
|
+
* WHAT IS AND IS NOT UNDER TEST. The subject is a CONSUMER of a listing, so every listing
|
|
7
|
+
* here is an input string, never a claim about herdr. The rows are verbatim herdr 0.9.0
|
|
8
|
+
* output measured 2026-09-16 on the shipped binary in a sandboxed HOME/XDG, plus two forms
|
|
9
|
+
* (`outdated (vM < vN)`, `needs repair (vN)`) the coordinator cross-checked in herdr source
|
|
10
|
+
* at `c77af189`. No herdr binary is spawned, nothing is installed, no environment is read,
|
|
11
|
+
* and nothing is written — if this gate ever needs any of those, the leaf stopped being pure.
|
|
12
|
+
*
|
|
13
|
+
* WHY THE HAZARD CASES LOOK ABSURD. `/srv/current (v9)/…` is a legal directory name, and the
|
|
14
|
+
* trailing `(<path>)` on every row is operator-chosen text. A parser that scans the whole row
|
|
15
|
+
* for a state can be told any answer by a directory name, and the row it misreads is the one
|
|
16
|
+
* that decides whether a broken integration gets activated. So the ugly paths are the point.
|
|
17
|
+
*
|
|
18
|
+
* FRAMING AND STATE ARE TWO DIFFERENT PROOFS. Cross-review found the first cut reading only
|
|
19
|
+
* the HEAD of a row and trusting the rest: `current (v8)` with no suffix at all, with a
|
|
20
|
+
* relative path, with the closing paren missing, with `()`, and with trailing bytes after
|
|
21
|
+
* the `)` all activated. So the framing cells and the front-anchoring cells are separate
|
|
22
|
+
* claims with separate mutants — one is "we read the whole row", the other is "the path may
|
|
23
|
+
* not spell the state", and a single token over both would have covered the hole.
|
|
24
|
+
*
|
|
25
|
+
* ORDER IS LOAD-BEARING. Each [QK:HIP-*] token appears exactly once, on the assertion that
|
|
26
|
+
* fails for that claim, and the assertions are ordered so that a mutant of one claim cannot
|
|
27
|
+
* trip an earlier claim's assertion first — that is what makes an attributed KILL mean the
|
|
28
|
+
* thing it names. In particular the front-anchoring cell asserts STATES, never verdicts,
|
|
29
|
+
* because the verdict table is what two later mutants move.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import assert from "node:assert/strict";
|
|
33
|
+
import fs from "node:fs";
|
|
34
|
+
import path from "node:path";
|
|
35
|
+
import { pathToFileURL } from "node:url";
|
|
36
|
+
|
|
37
|
+
let passed = 0;
|
|
38
|
+
function ok(label: string, cond: boolean): void {
|
|
39
|
+
assert.ok(cond, label);
|
|
40
|
+
console.log(` ok ${label}`);
|
|
41
|
+
passed++;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const REPO = path.resolve(path.dirname(new URL(import.meta.url).pathname), "..");
|
|
45
|
+
const LEAF = path.join(REPO, "plugins", "herdr", "lib", "integration-profile.mjs");
|
|
46
|
+
|
|
47
|
+
interface SelectedFact {
|
|
48
|
+
atom: string;
|
|
49
|
+
backend: string;
|
|
50
|
+
state: string;
|
|
51
|
+
installedVersion: number | null;
|
|
52
|
+
floorVersion: number | null;
|
|
53
|
+
}
|
|
54
|
+
interface PlanEntry {
|
|
55
|
+
backend: string;
|
|
56
|
+
atom: string;
|
|
57
|
+
reason: string | null;
|
|
58
|
+
state: string;
|
|
59
|
+
}
|
|
60
|
+
interface Profile {
|
|
61
|
+
schemaVersion: number;
|
|
62
|
+
selected: SelectedFact[];
|
|
63
|
+
activate: string[];
|
|
64
|
+
skip: PlanEntry[];
|
|
65
|
+
fail: PlanEntry[];
|
|
66
|
+
observedOtherAtoms: string[];
|
|
67
|
+
}
|
|
68
|
+
interface Leaf {
|
|
69
|
+
SELECTED_ATOMS: Record<string, string>;
|
|
70
|
+
buildActivationProfile: (listing: string) => Profile;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// A non-literal specifier: `plugins/` is outside both typecheck fences (it is plain ESM
|
|
74
|
+
// JavaScript herdr runs directly), so the module is loaded, not type-resolved.
|
|
75
|
+
const leaf = (await import(pathToFileURL(LEAF).href)) as Leaf;
|
|
76
|
+
const { SELECTED_ATOMS, buildActivationProfile } = leaf;
|
|
77
|
+
|
|
78
|
+
/** Build a listing from rows, the way herdr prints it: one atom per line, newline-terminated. */
|
|
79
|
+
function listing(...rows: string[]): string {
|
|
80
|
+
return `${rows.join("\n")}\n`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** A well-formed row for the OTHER selected atom, so a cell tests exactly the row it means to. */
|
|
84
|
+
const CLAUDE_OK = "claude: current (v9) (/home/u/.claude/hooks/herdr-agent-state.sh)";
|
|
85
|
+
const PI_OK = "pi: current (v8) (/home/u/.pi/agent/extensions/herdr-agent-state.ts)";
|
|
86
|
+
|
|
87
|
+
/** Return the refusal code for a listing, or null if it produced a profile instead. */
|
|
88
|
+
function refusalCode(text: string): string | null {
|
|
89
|
+
try {
|
|
90
|
+
buildActivationProfile(text);
|
|
91
|
+
return null;
|
|
92
|
+
} catch (err) {
|
|
93
|
+
return (err as { code?: string }).code ?? `not-an-IntegrationProfileError:${String(err)}`;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** The refusal code for a single hostile `pi:` row standing beside a well-formed claude row. */
|
|
98
|
+
function piRowCode(row: string): string | null {
|
|
99
|
+
return refusalCode(listing(row, CLAUDE_OK));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ── 1. the table is closed, written out, and ours ───────────────────────────
|
|
103
|
+
{
|
|
104
|
+
ok(
|
|
105
|
+
"[QK:HIP-CLOSED-MAP] the Herdr-atom → Entwurf-backend table is EXACTLY {pi→pi, claude→claude-code} and frozen — " +
|
|
106
|
+
"set P is frozen at {pi, claude-code} (#116 issuecomment-5690300559), herdr never emits the string " +
|
|
107
|
+
"`claude-code`, and any rule that DERIVES a backend from an atom name (identity, lowercasing, suffixing) " +
|
|
108
|
+
"silently admits whichever atom herdr adds next: OpenCode is installed on real hosts and is the negative " +
|
|
109
|
+
`control this lane must keep outside the product (got ${JSON.stringify(SELECTED_ATOMS)})`,
|
|
110
|
+
JSON.stringify(SELECTED_ATOMS) === '{"pi":"pi","claude":"claude-code"}' && Object.isFrozen(SELECTED_ATOMS),
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ── 2. an absent selected atom is refused by name ───────────────────────────
|
|
115
|
+
{
|
|
116
|
+
const missing = refusalCode(listing(CLAUDE_OK));
|
|
117
|
+
ok(
|
|
118
|
+
"[QK:HIP-ROW-MISSING-REFUSED] a selected atom with NO row refuses by name — a real listing prints EVERY atom " +
|
|
119
|
+
"every time (17 rows on 0.9.0, 18 on 0.9.1: `[측정 2026-09-17]` the added row is `letta (experimental)` and the " +
|
|
120
|
+
"other 17 are byte-identical), so a listing with no `pi:` line at all is not the protocol we think we are reading, and " +
|
|
121
|
+
"the plausible-looking alternative (treat absence as 'not installed') would report a clean zero-write SKIP " +
|
|
122
|
+
`for a question that was never actually asked (got ${missing})`,
|
|
123
|
+
missing === "herdr-status-row-missing",
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ── 3. a doubled selected atom is refused by name, never resolved ───────────
|
|
128
|
+
{
|
|
129
|
+
const doubled = refusalCode(
|
|
130
|
+
listing(PI_OK, CLAUDE_OK, "pi: not installed (/home/u/.pi/agent/extensions/herdr-agent-state.ts)"),
|
|
131
|
+
);
|
|
132
|
+
ok(
|
|
133
|
+
"[QK:HIP-ROW-DUPLICATE-REFUSED] two rows for one selected atom refuse by name — a listing that says both " +
|
|
134
|
+
"`current` and `not installed` for pi is a contradiction, and taking the first or the last match turns it " +
|
|
135
|
+
"into a silent guess about whether an integration is installed, which is exactly the question this leaf " +
|
|
136
|
+
`exists to answer (got ${doubled})`,
|
|
137
|
+
doubled === "herdr-status-row-duplicate",
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ── 4. `<atom>:` claims the line before its shape is known ──────────────────
|
|
142
|
+
{
|
|
143
|
+
const aloneNoSpace = piRowCode("pi:current (v8) (/home/u/.pi/agent/extensions/herdr-agent-state.ts)");
|
|
144
|
+
const aloneTab = piRowCode("pi:\tcurrent (v8) (/home/u/.pi/agent/extensions/herdr-agent-state.ts)");
|
|
145
|
+
const besideValid = refusalCode(
|
|
146
|
+
listing(PI_OK, "pi:current (v1) (/home/u/.pi/agent/extensions/herdr-agent-state.ts)", CLAUDE_OK),
|
|
147
|
+
);
|
|
148
|
+
ok(
|
|
149
|
+
"[QK:HIP-SELECTED-DELIMITER-FRAMED] a line is CLAIMED by the atom at `<atom>:`, before its shape is known — " +
|
|
150
|
+
"`pi:current …` and `pi:<TAB>current …` are broken declarations BY pi, not unrelated text, so alone they are " +
|
|
151
|
+
"malformed and beside a valid row they are a doubled declaration; matching on `pi: ` instead lets a row that " +
|
|
152
|
+
"contradicts the one we accepted sit in the same listing and change nothing " +
|
|
153
|
+
`(alone=${aloneNoSpace}/${aloneTab} beside-valid=${besideValid})`,
|
|
154
|
+
aloneNoSpace === "herdr-status-row-malformed" &&
|
|
155
|
+
aloneTab === "herdr-status-row-malformed" &&
|
|
156
|
+
besideValid === "herdr-status-row-duplicate",
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ── 5. an unreadable state is refused, never defaulted ──────────────────────
|
|
161
|
+
{
|
|
162
|
+
const unknown = piRowCode(
|
|
163
|
+
"pi: brand new state nobody has shipped yet (/home/u/.pi/agent/extensions/herdr-agent-state.ts)",
|
|
164
|
+
);
|
|
165
|
+
ok(
|
|
166
|
+
"[QK:HIP-ROW-MALFORMED-REFUSED] a selected row whose state this parser cannot read refuses by name and is never " +
|
|
167
|
+
"resolved to a default — a future herdr phrase quietly read as `not installed` would turn an unknown into a " +
|
|
168
|
+
"zero-write SKIP and let the run report success over a state nobody has interpreted " +
|
|
169
|
+
`(got ${unknown})`,
|
|
170
|
+
unknown === "herdr-status-row-malformed",
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ── 6. a row is accepted whole, or not at all ───────────────────────────────
|
|
175
|
+
{
|
|
176
|
+
const cells = {
|
|
177
|
+
"no suffix": "pi: current (v8)",
|
|
178
|
+
"relative path": "pi: current (v8) (relative/path/herdr-agent-state.ts)",
|
|
179
|
+
truncated: "pi: current (v8) (/home/u/.pi/agent/extensions/herdr-agent-state.ts",
|
|
180
|
+
"empty parens": "pi: current (v8) ()",
|
|
181
|
+
"trailing bytes": "pi: current (v8) (/home/u/.pi/agent/extensions/herdr-agent-state.ts) and then some",
|
|
182
|
+
};
|
|
183
|
+
const verdicts = Object.entries(cells).map(([name, row]) => `${name}=${piRowCode(row)}`);
|
|
184
|
+
const hostileButWellFormed = buildActivationProfile(
|
|
185
|
+
listing("pi: current (v8) (/srv/current (v9) (/x)/agent/extensions/herdr-agent-state.ts)", CLAUDE_OK),
|
|
186
|
+
);
|
|
187
|
+
ok(
|
|
188
|
+
"[QK:HIP-SUFFIX-FRAMING] every accepted grammar consumes the WHOLE remainder — state, then exactly one " +
|
|
189
|
+
"` (<absolute path>)`, then end of line — while the bytes INSIDE the path stay opaque. Reading only the head " +
|
|
190
|
+
"and trusting the rest is the hole cross-review found: no suffix, a relative path, a missing `)`, `()` and " +
|
|
191
|
+
"trailing bytes each ACTIVATED a row nobody had finished reading. Framing is checked; the path is still " +
|
|
192
|
+
`never promoted to an address (${verdicts.join(" ")} hostile-but-well-formed=${hostileButWellFormed.selected[0]?.state})`,
|
|
193
|
+
verdicts.every((v) => v.endsWith("=herdr-status-row-malformed")) &&
|
|
194
|
+
hostileButWellFormed.selected[0]?.state === "current" &&
|
|
195
|
+
hostileButWellFormed.selected[0]?.installedVersion === 8,
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// ── 7. a version outside herdr's u32 domain is malformed, not null ──────────
|
|
200
|
+
{
|
|
201
|
+
const huge = piRowCode(`pi: current (v${"4".repeat(400)}) (/home/u/.pi/agent/extensions/herdr-agent-state.ts)`);
|
|
202
|
+
const atMax = buildActivationProfile(
|
|
203
|
+
listing("pi: current (v4294967295) (/home/u/.pi/agent/extensions/herdr-agent-state.ts)", CLAUDE_OK),
|
|
204
|
+
);
|
|
205
|
+
ok(
|
|
206
|
+
"[QK:HIP-VERSION-U32] a captured version must land inside herdr's own u32 domain (0..4294967295) or the row is " +
|
|
207
|
+
"malformed — a 400-digit capture becomes `Infinity`, `Infinity` serialises to `null`, and the plan would then " +
|
|
208
|
+
"carry 'no version' for a row that loudly stated one; the boundary value itself must still parse, so this is " +
|
|
209
|
+
`a domain check and not a length heuristic (400-digit=${huge} at-max=${atMax.selected[0]?.installedVersion})`,
|
|
210
|
+
huge === "herdr-status-row-malformed" && atMax.selected[0]?.installedVersion === 4294967295,
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ── 8. the state is read front-anchored; the display path is never scanned ──
|
|
215
|
+
{
|
|
216
|
+
const hostile = buildActivationProfile(
|
|
217
|
+
listing(
|
|
218
|
+
"pi: outdated (legacy < v8) (/srv/current (v9) (/x)/agent/extensions/herdr-agent-state.ts)",
|
|
219
|
+
"claude: not installed (/home/u/My (v9) dir/hooks (outdated (legacy < v3))/herdr-agent-state.sh)",
|
|
220
|
+
),
|
|
221
|
+
);
|
|
222
|
+
const [pi, claude] = hostile.selected;
|
|
223
|
+
ok(
|
|
224
|
+
"[QK:HIP-STATE-FRONT-ANCHORED] the state is matched at the HEAD of the remainder and the path between the " +
|
|
225
|
+
"framing parens is never searched — `/srv/current (v9) (/x)/…` and `…/hooks (outdated (legacy < v3))/…` are " +
|
|
226
|
+
"legal directory names an operator chose, so a parser that looks for a state anywhere in the row lets a " +
|
|
227
|
+
`DIRECTORY NAME decide whether a broken integration reads as installed (pi=${pi?.state}/floor=${pi?.floorVersion} claude=${claude?.state})`,
|
|
228
|
+
pi?.state === "outdated" &&
|
|
229
|
+
pi?.floorVersion === 8 &&
|
|
230
|
+
pi?.installedVersion === null &&
|
|
231
|
+
claude?.state === "not-installed",
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// ── 9. no path ever becomes a field of the plan ─────────────────────────────
|
|
236
|
+
{
|
|
237
|
+
const profile = buildActivationProfile(
|
|
238
|
+
listing(
|
|
239
|
+
"pi: current (v9) (/home/u/.pi/agent/extensions/herdr-agent-state.ts)",
|
|
240
|
+
"claude: outdated (legacy < v9) (/home/u/.claude/hooks/herdr-agent-state.sh)",
|
|
241
|
+
),
|
|
242
|
+
);
|
|
243
|
+
const keysets = profile.selected.map((f) => Object.keys(f).sort().join(","));
|
|
244
|
+
const expected = ["atom", "backend", "floorVersion", "installedVersion", "state"].join(",");
|
|
245
|
+
ok(
|
|
246
|
+
"[QK:HIP-PATH-IS-NOT-AUTHORITY] the display path is opaque and leaves no trace in the plan — not a field, not a " +
|
|
247
|
+
"substring, not one `/` anywhere in the serialised profile. Checking that a row is FRAMED by a path is not " +
|
|
248
|
+
"the same as carrying one: the resolved path a later bootstrap installs against is owned by that bootstrap " +
|
|
249
|
+
"from the environment it controls (measured: the same bytes read `current` or `not installed` depending only " +
|
|
250
|
+
`on PI_CODING_AGENT_DIR), and two layers must not each hold their own answer (keysets=${JSON.stringify(keysets)})`,
|
|
251
|
+
keysets.every((k) => k === expected) && !JSON.stringify(profile).includes("/"),
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// ── 10. outdated and needs-repair are named FAILs ───────────────────────────
|
|
256
|
+
{
|
|
257
|
+
const profile = buildActivationProfile(
|
|
258
|
+
listing(
|
|
259
|
+
"pi: outdated (v7 < v8) (/home/u/.pi/agent/extensions/herdr-agent-state.ts)",
|
|
260
|
+
"claude: needs repair (v9) (/home/u/.claude/hooks/herdr-agent-state.sh)",
|
|
261
|
+
),
|
|
262
|
+
);
|
|
263
|
+
ok(
|
|
264
|
+
"[QK:HIP-OUTDATED-IS-FAIL] `outdated` and `needs repair` on a SELECTED atom are named FAILs, never a skip and " +
|
|
265
|
+
"never a cosmetic pass — measured 2026-09-16, an empty file, a chmod-000 file and a marker-less hand-written " +
|
|
266
|
+
"file ALL collapse into `outdated (legacy < vN)`, so that word means 'no readable marker at or above the " +
|
|
267
|
+
"floor', which is exactly the stale/broken case the M3 contract requires to stop the run " +
|
|
268
|
+
`(activate=${JSON.stringify(profile.activate)} fail=${JSON.stringify(profile.fail.map((f) => f.reason))})`,
|
|
269
|
+
profile.activate.length === 0 &&
|
|
270
|
+
profile.skip.length === 0 &&
|
|
271
|
+
profile.fail.map((f) => `${f.backend}:${f.reason}`).join("|") ===
|
|
272
|
+
"pi:herdr-integration-outdated|claude-code:herdr-integration-needs-repair",
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// ── 11. not-installed is a zero-write SKIP, not a failure ───────────────────
|
|
277
|
+
{
|
|
278
|
+
const profile = buildActivationProfile(
|
|
279
|
+
listing("pi: not installed (/home/u/.pi/agent/extensions/herdr-agent-state.ts)", CLAUDE_OK),
|
|
280
|
+
);
|
|
281
|
+
ok(
|
|
282
|
+
"[QK:HIP-ABSENT-IS-SKIP-ZERO-WRITE] `not installed` on a selected atom is a NAMED, zero-write SKIP and the rest " +
|
|
283
|
+
"of the plan proceeds — a host that simply has not added that Herdr integration is not broken, and turning " +
|
|
284
|
+
"its absence into a red is the shape that invites the plugin to go install the harness it must never install " +
|
|
285
|
+
`(AGENTS.md Hard Rule 17) (skip=${JSON.stringify(profile.skip)} activate=${JSON.stringify(profile.activate)})`,
|
|
286
|
+
profile.fail.length === 0 &&
|
|
287
|
+
profile.activate.join(",") === "claude-code" &&
|
|
288
|
+
profile.skip.map((s) => `${s.backend}:${s.reason}`).join("|") === "pi:herdr-integration-not-installed",
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// ── 12. `current` is herdr's admission, and we add no floor of our own ──────
|
|
293
|
+
{
|
|
294
|
+
const profile = buildActivationProfile(
|
|
295
|
+
listing(
|
|
296
|
+
"pi: current (v1) (/home/u/.pi/agent/extensions/herdr-agent-state.ts)",
|
|
297
|
+
"claude: current (v999) (/home/u/.claude/hooks/herdr-agent-state.sh)",
|
|
298
|
+
),
|
|
299
|
+
);
|
|
300
|
+
ok(
|
|
301
|
+
"[QK:HIP-CURRENT-IS-ADMISSION-ONLY] every `current` activates, at any version inside the domain, because " +
|
|
302
|
+
"`current` is HERDR's admission and not an integrity claim — measured, appending bytes after the marker still " +
|
|
303
|
+
"reads current and `v999` reads current too, so a second version floor invented here would reject hosts herdr " +
|
|
304
|
+
"already admitted while proving nothing about the bytes it was pretending to check " +
|
|
305
|
+
`(activate=${JSON.stringify(profile.activate)} versions=${JSON.stringify(profile.selected.map((f) => f.installedVersion))})`,
|
|
306
|
+
profile.activate.join(",") === "pi,claude-code" &&
|
|
307
|
+
profile.fail.length === 0 &&
|
|
308
|
+
profile.selected.map((f) => f.installedVersion).join(",") === "1,999",
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// ── 13. atoms outside P are observed, never planned ─────────────────────────
|
|
313
|
+
{
|
|
314
|
+
// "may not refuse the whole listing" is HALF of this claim, so the refusal is CAUGHT and judged
|
|
315
|
+
// by the assertion below. Calling straight through would let a leaf that started refusing kill
|
|
316
|
+
// this cell with its own error before the claim it violates is ever evaluated — a red attributed
|
|
317
|
+
// to nobody.
|
|
318
|
+
const crowded = listing(
|
|
319
|
+
PI_OK,
|
|
320
|
+
"omp: not installed (/home/u/.omp/agent/extensions/herdr-omp-agent-state.ts)",
|
|
321
|
+
CLAUDE_OK,
|
|
322
|
+
"codex: current (v8) (/home/u/.codex/herdr-agent-state.sh)",
|
|
323
|
+
"opencode: current (v11) (/home/u/.config/opencode/plugins/herdr-agent-state.js)",
|
|
324
|
+
"grok: outdated (legacy < v1) (/home/u/.grok/hooks/herdr-agent-state.sh)",
|
|
325
|
+
"weirdatom: a state this plugin has never heard of (/home/u/.weird/x)",
|
|
326
|
+
"kilo:broken declaration with no space (/home/u/.config/kilo/plugin/herdr-agent-state.js)",
|
|
327
|
+
);
|
|
328
|
+
let full: ReturnType<typeof buildActivationProfile> | null = null;
|
|
329
|
+
let refused: string | null = null;
|
|
330
|
+
try {
|
|
331
|
+
full = buildActivationProfile(crowded);
|
|
332
|
+
} catch (err) {
|
|
333
|
+
refused = `${(err as { code?: string }).code ?? "unknown"}: ${(err as Error).message}`;
|
|
334
|
+
}
|
|
335
|
+
const plan = full === null ? "<refused>" : JSON.stringify([full.activate, full.skip, full.fail]);
|
|
336
|
+
ok(
|
|
337
|
+
"[QK:HIP-UNSELECTED-NEVER-ACTIVATES] rows outside P are OBSERVED and never planned, and neither an unsupported " +
|
|
338
|
+
"atom nor an unparsable one disturbs the two verdicts that matter — OpenCode current v11 is the deliberate " +
|
|
339
|
+
"negative control (it must receive zero Entwurf state even while herdr reports it installed), Codex is out " +
|
|
340
|
+
"of this lane by GLG decision, and neither a future atom this parser cannot read nor a broken declaration " +
|
|
341
|
+
`belonging to one may refuse the whole listing (plan=${plan} refused=${JSON.stringify(refused)} ` +
|
|
342
|
+
`observed=${JSON.stringify(full === null ? null : full.observedOtherAtoms)})`,
|
|
343
|
+
refused === null &&
|
|
344
|
+
full !== null &&
|
|
345
|
+
full.activate.join(",") === "pi,claude-code" &&
|
|
346
|
+
full.skip.length === 0 &&
|
|
347
|
+
full.fail.length === 0 &&
|
|
348
|
+
!plan.includes("opencode") &&
|
|
349
|
+
!plan.includes("codex") &&
|
|
350
|
+
full.observedOtherAtoms.join(",") === "codex,grok,omp,opencode,weirdatom",
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// ── 14. the leaf is pure, and stays that way ────────────────────────────────
|
|
355
|
+
{
|
|
356
|
+
const source = fs.readFileSync(LEAF, "utf8");
|
|
357
|
+
const impurities = [/\bimport\s/, /\brequire\(/, /\bprocess\./, /node:/, /\bMath\.random\b/, /\bDate\.now\b/];
|
|
358
|
+
const found = impurities.filter((re) => re.test(source)).map((re) => re.source);
|
|
359
|
+
const twice = listing(PI_OK, "claude: not installed (/home/u/.claude/hooks/herdr-agent-state.sh)");
|
|
360
|
+
const a = JSON.stringify(buildActivationProfile(twice));
|
|
361
|
+
const b = JSON.stringify(buildActivationProfile(twice));
|
|
362
|
+
ok(
|
|
363
|
+
"[QK:HIP-PURE-LEAF] the leaf imports nothing and touches no process, clock, randomness or filesystem, and the " +
|
|
364
|
+
"same listing yields byte-identical plans — the whole worth of deciding activation HERE is that the answer " +
|
|
365
|
+
"is a property of the listing and of nothing else; the moment it can read an environment, the plan and the " +
|
|
366
|
+
`bootstrap that acts on it can disagree about which host they are on (found=${JSON.stringify(found)})`,
|
|
367
|
+
found.length === 0 && a === b,
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
console.log(`\ncheck-herdr-plugin-profile: ${passed} assertions passed`);
|