@junghanacs/entwurf 0.35.1 → 0.36.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 +2 -2
- package/CHANGELOG.md +403 -0
- package/CONTRIBUTING.md +1 -1
- package/DELIVERY.md +17 -0
- package/FAQ.md +3 -3
- package/README.md +30 -16
- package/VERIFY.md +12 -10
- package/demo/README.md +1 -1
- package/docs/acp-backend-rail.md +69 -5
- package/docs/setup-clean-host.md +30 -22
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/codex-fresh-preflight.js +4 -3
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/native-push/codex-ws-client.js +99 -18
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-runtime.js +3 -3
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-pi-durable.js +59 -14
- package/mcp/entwurf-bridge/dist/scripts/codex-socket-classify.js +44 -0
- package/mcp/entwurf-bridge/tsconfig.build.json +5 -0
- package/package.json +21 -16
- package/pi/pi-durable/bootstrap.mjs +12 -8
- package/pi/pi-durable/carrier/runtime.js +52 -2
- package/pi/pi-durable/carrier/subagent.js +6 -1
- package/pi/pi-durable/carrier/tui.js +31 -4
- package/pi/pi-durable/carrier-relocation.mjs +2 -2
- package/pi/pi-durable/overlay/upstream-pin.json +79 -36
- package/pi-extensions/lib/codex-fresh-preflight.ts +4 -3
- package/pi-extensions/lib/native-push/codex-ws-client.ts +129 -18
- package/pi-extensions/lib/pi-durable-runtime.ts +4 -4
- package/pi-extensions/meta-bridge-pi-durable.ts +77 -21
- package/run.sh +116 -39
- package/scripts/check-acp-prompt-lifecycle.ts +4 -2
- package/scripts/check-acp-session-reuse.ts +4 -2
- package/scripts/check-acp-stop-reason.ts +4 -2
- package/scripts/check-acp-stream-hooks.ts +4 -2
- package/scripts/check-acp-usage-accounting.ts +4 -2
- package/scripts/check-codex-app-server-launch.ts +55 -12
- package/scripts/check-codex-native-push.ts +231 -12
- package/scripts/check-gate-qualification.ts +4 -4
- package/scripts/check-herdr-supply.ts +32 -7
- package/scripts/codex-app-server-launch.sh +54 -29
- package/scripts/codex-socket-classify.ts +46 -0
- package/scripts/emit-pi-durable-carrier.ts +159 -168
- package/scripts/fixtures/herdr-supply.json +10 -9
- package/scripts/lib/acp-gate-home.ts +75 -0
- package/scripts/lib/codex-socket-listener.ts +429 -0
- package/scripts/mutants/acp-stop-reason.json +11 -0
- package/scripts/mutants/codex-app-server-launch.json +39 -12
- package/scripts/mutants/codex-native.json +67 -0
- package/scripts/mutants/control-send-receipt.json +1 -1
- package/scripts/mutants/herdr-supply.json +6 -6
- package/scripts/mutants/pack-install.json +3 -3
- package/scripts/mutants/pi-durable.json +165 -16
- package/scripts/mutants/pi-floor.json +5 -5
- package/scripts/pi-durable-contact-host.mjs +1 -1
- package/scripts/smoke-codex-fresh-live.ts +31 -7
- package/scripts/smoke-pi-durable-fresh-live.ts +22 -3
- package/pi/pi-durable/overlay/runtime-contacts.patch +0 -283
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"subject": "scripts/check-herdr-sandbox.ts",
|
|
9
9
|
"find": ["const SUPPLY_MANIFEST = \"scripts/fixtures/herdr-supply.json\";"],
|
|
10
10
|
"replace": [
|
|
11
|
-
"const SUPPLY_MANIFEST = \"scripts/fixtures/herdr-supply.json\";\nconst PINNED_FALLBACK = \"0.9.
|
|
11
|
+
"const SUPPLY_MANIFEST = \"scripts/fixtures/herdr-supply.json\";\nconst PINNED_FALLBACK = \"0.9.4\";\nvoid PINNED_FALLBACK;"
|
|
12
12
|
],
|
|
13
13
|
"gate": ["bash", "run.sh", "check-herdr-supply"],
|
|
14
14
|
"timeoutSeconds": 120,
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
"claim": "HSUP-DIGEST-SHAPE",
|
|
20
20
|
"title": "a pinned digest is truncated, so the check that is supposed to catch a swapped binary can no longer fail",
|
|
21
21
|
"subject": "scripts/fixtures/herdr-supply.json",
|
|
22
|
-
"find": ["\"sha256\": \"
|
|
23
|
-
"replace": ["\"sha256\": \"
|
|
22
|
+
"find": ["\"sha256\": \"493304fbefb0ed0ea0455f373b47f48d24e8774bc7974f4c916ad2e937675b23\""],
|
|
23
|
+
"replace": ["\"sha256\": \"493304fbefb0ed0ea0455f373b47\""],
|
|
24
24
|
"gate": ["bash", "run.sh", "check-herdr-supply"],
|
|
25
25
|
"timeoutSeconds": 120,
|
|
26
26
|
"signature": "[QK:HSUP-DIGEST-SHAPE]",
|
|
@@ -119,14 +119,14 @@
|
|
|
119
119
|
"signatureSource": "scripts/check-herdr-supply.ts"
|
|
120
120
|
},
|
|
121
121
|
{
|
|
122
|
-
"claim": "HSUP-
|
|
123
|
-
"title": "the x86_64 asset is recorded as executed
|
|
122
|
+
"claim": "HSUP-EXECUTION-RECEIPT-BOUND",
|
|
123
|
+
"title": "the x86_64 asset is recorded as executed with no receipt bound to this pin's version and its own full digest",
|
|
124
124
|
"subject": "scripts/fixtures/herdr-supply.json",
|
|
125
125
|
"find": ["\"executed\": false"],
|
|
126
126
|
"replace": ["\"executed\": true"],
|
|
127
127
|
"gate": ["bash", "run.sh", "check-herdr-supply"],
|
|
128
128
|
"timeoutSeconds": 120,
|
|
129
|
-
"signature": "[QK:HSUP-
|
|
129
|
+
"signature": "[QK:HSUP-EXECUTION-RECEIPT-BOUND]",
|
|
130
130
|
"signatureSource": "scripts/check-herdr-supply.ts"
|
|
131
131
|
}
|
|
132
132
|
]
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"claim": "PACK-INSTALL-PIN-MATCHER-BOUNDED",
|
|
7
7
|
"title": "the pin-leak matcher loses its version boundary and blesses substring lookalikes — @0.85.10 reads as the pinned 0.85.1 and the gate announces a verified tree it never verified (independent review, 2026-08-25)",
|
|
8
8
|
"subject": "run.sh",
|
|
9
|
-
"find": [" grep '^@earendil-works+' | grep -Ev '@1\\.
|
|
9
|
+
"find": [" grep '^@earendil-works+' | grep -Ev '@1\\.2\\.0(_|$)' || true"],
|
|
10
10
|
"replace": [" grep '^@earendil-works+' | grep -v '@0\\.87\\.1' || true"],
|
|
11
11
|
"gate": ["bash", "run.sh", "check-pack-pin-matcher"],
|
|
12
12
|
"timeoutSeconds": 120,
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"claim": "PACK-INSTALL-PIN-MATCHER-COVERS-CLOSURE",
|
|
18
18
|
"title": "the pin-leak matcher narrows back to the pi-* families and goes blind to a non-pi member of the same runtime closure — @earendil-works/chord (new in pi 0.85.0, a runtime dependency of pi-coding-agent/pi-agent-core/pi-client/pi-protocol) floats unpinned while the gate prints a verified pin (measured 2026-09-06 on a real 0.85.1 install tree)",
|
|
19
19
|
"subject": "run.sh",
|
|
20
|
-
"find": [" grep '^@earendil-works+' | grep -Ev '@1\\.
|
|
21
|
-
"replace": [" grep '^@earendil-works+pi-' | grep -Ev '@1\\.
|
|
20
|
+
"find": [" grep '^@earendil-works+' | grep -Ev '@1\\.2\\.0(_|$)' || true"],
|
|
21
|
+
"replace": [" grep '^@earendil-works+pi-' | grep -Ev '@1\\.2\\.0(_|$)' || true"],
|
|
22
22
|
"gate": ["bash", "run.sh", "check-pack-pin-matcher"],
|
|
23
23
|
"timeoutSeconds": 120,
|
|
24
24
|
"signature": "[QK:PACK-INSTALL-PIN-MATCHER-COVERS-CLOSURE]",
|
|
@@ -458,7 +458,7 @@
|
|
|
458
458
|
"claim": "PI-DURABLE-CARRIER-PIN-MATCHES",
|
|
459
459
|
"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",
|
|
460
460
|
"subject": "pi/pi-durable/overlay/upstream-pin.json",
|
|
461
|
-
"find": ["\"sha256\": \"
|
|
461
|
+
"find": ["\"sha256\": \"60c2965eadbdbbe78fce1b7eeb89d97eb3104a30b7d58ebca87009d2e9118403\""],
|
|
462
462
|
"replace": ["\"sha256\": \"0000000000000000000000000000000000000000000000000000000000000000\""],
|
|
463
463
|
"gate": ["bash", "run.sh", "check-pi-durable-beside"],
|
|
464
464
|
"timeoutSeconds": 300,
|
|
@@ -833,19 +833,40 @@
|
|
|
833
833
|
}
|
|
834
834
|
},
|
|
835
835
|
{
|
|
836
|
-
"claim": "PI-DURABLE-CARRIER-
|
|
837
|
-
"title": "
|
|
836
|
+
"claim": "PI-DURABLE-CARRIER-EMIT-SOURCE-EQUAL",
|
|
837
|
+
"title": "the owned source changes and the carrier is not re-emitted, so the shipped app is no longer the emission of the source the repository reviews",
|
|
838
|
+
"subject": "pi/pi-durable/src/experimental/durable/tui.ts",
|
|
839
|
+
"find": ["\t\tconst forms = [...detailed, tasks, `⏳ ${n}`, `⏳${n}`, `*${n}`, \"*+\", \"*\"];"],
|
|
840
|
+
"replace": ["\t\tconst forms = [...detailed, tasks, `⏳ ${n}`, `⏳${n}`, `*${n}`, \"*\"];"],
|
|
841
|
+
"gate": ["bash", "run.sh", "check-pi-durable-beside"],
|
|
842
|
+
"timeoutSeconds": 300,
|
|
843
|
+
"signature": "[QK:PI-DURABLE-CARRIER-EMIT-SOURCE-EQUAL]",
|
|
844
|
+
"signatureSource": "pi-extensions/lib/pi-durable-carrier.test.ts",
|
|
845
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
846
|
+
},
|
|
847
|
+
{
|
|
848
|
+
"claim": "PI-DURABLE-CARRIER-EMIT-NODE-EXACT",
|
|
849
|
+
"title": "the emitter stops comparing its own Node with the pinned one, so a carrier emitted under any Node reads as the pinned emission",
|
|
838
850
|
"subject": "scripts/emit-pi-durable-carrier.ts",
|
|
839
|
-
"find": ["\tif (
|
|
840
|
-
"replace": ["\tif (
|
|
851
|
+
"find": ["\tif (pinned !== running) {"],
|
|
852
|
+
"replace": ["\tif (false) {"],
|
|
841
853
|
"gate": ["bash", "run.sh", "check-pi-durable-beside"],
|
|
842
854
|
"timeoutSeconds": 300,
|
|
843
|
-
"signature": "[QK:PI-DURABLE-CARRIER-
|
|
855
|
+
"signature": "[QK:PI-DURABLE-CARRIER-EMIT-NODE-EXACT]",
|
|
844
856
|
"signatureSource": "pi-extensions/lib/pi-durable-carrier.test.ts",
|
|
845
|
-
"build": {
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
857
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
858
|
+
},
|
|
859
|
+
{
|
|
860
|
+
"claim": "PI-DURABLE-SOURCE-FENCE-RESOLVES",
|
|
861
|
+
"title": "the source fence inherits the root program's exclusion of pi/pi-durable/src, so the owned source sits in no typecheck program at all",
|
|
862
|
+
"subject": "pi/pi-durable/tsconfig.json",
|
|
863
|
+
"find": ["\t\"exclude\": []"],
|
|
864
|
+
"replace": ["\t\"files\": []"],
|
|
865
|
+
"gate": ["bash", "run.sh", "check-pi-durable-beside"],
|
|
866
|
+
"timeoutSeconds": 300,
|
|
867
|
+
"signature": "[QK:PI-DURABLE-SOURCE-FENCE-RESOLVES]",
|
|
868
|
+
"signatureSource": "pi-extensions/lib/pi-durable-carrier.test.ts",
|
|
869
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
849
870
|
},
|
|
850
871
|
{
|
|
851
872
|
"claim": "PI-DURABLE-FOOTER-GRAPH-LIFETIME",
|
|
@@ -898,12 +919,8 @@
|
|
|
898
919
|
"claim": "PI-DURABLE-FOOTER-BADGE-PROTECTED",
|
|
899
920
|
"title": "the badge sits last in the dock, so the renderer's bottom clip drops it first on a short terminal",
|
|
900
921
|
"subject": "pi/pi-durable/carrier/tui.js",
|
|
901
|
-
"find": [
|
|
902
|
-
|
|
903
|
-
],
|
|
904
|
-
"replace": [
|
|
905
|
-
"\t\t{ component: new VStack([...dock, { component: badge, shrink: 0 }]), basis: \"auto\", grow: 0, shrink: 1, minSize: 1 },"
|
|
906
|
-
],
|
|
922
|
+
"find": ["\t\t\tcomponent: new VStack([{ component: badge, shrink: 0 }, ...dock]),"],
|
|
923
|
+
"replace": ["\t\t\tcomponent: new VStack([...dock, { component: badge, shrink: 0 }]),"],
|
|
907
924
|
"gate": ["bash", "run.sh", "check-pi-durable-beside"],
|
|
908
925
|
"timeoutSeconds": 300,
|
|
909
926
|
"signature": "[QK:PI-DURABLE-FOOTER-BADGE-PROTECTED]",
|
|
@@ -1002,6 +1019,138 @@
|
|
|
1002
1019
|
"argv": ["bash", "scripts/build-bridge.sh"],
|
|
1003
1020
|
"output": "mcp/entwurf-bridge/dist"
|
|
1004
1021
|
}
|
|
1022
|
+
},
|
|
1023
|
+
{
|
|
1024
|
+
"claim": "PI-DURABLE-SOURCE-NOT-SHIPPED",
|
|
1025
|
+
"title": "the files allowlist admits pi/pi-durable/src, so the owned TypeScript source rides the tarball beside the carrier an installed consumer runs",
|
|
1026
|
+
"subject": "package.json",
|
|
1027
|
+
"find": ["\t\t\"pi/pi-durable/carrier/\",\n"],
|
|
1028
|
+
"replace": ["\t\t\"pi/pi-durable/carrier/\",\n\t\t\"pi/pi-durable/src/\",\n"],
|
|
1029
|
+
"gate": ["bash", "run.sh", "check-pi-durable-beside"],
|
|
1030
|
+
"timeoutSeconds": 300,
|
|
1031
|
+
"signature": "[QK:PI-DURABLE-SOURCE-NOT-SHIPPED]",
|
|
1032
|
+
"signatureSource": "pi-extensions/lib/pi-durable-carrier.test.ts",
|
|
1033
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
1034
|
+
},
|
|
1035
|
+
{
|
|
1036
|
+
"claim": "PI-DURABLE-EMITTER-NODE-CI-BOUND",
|
|
1037
|
+
"title": "the CI check job floats its Node major again, so the floor that proves the carrier emission runs under a Node other than the pinned emitter",
|
|
1038
|
+
"subject": ".github/workflows/ci.yml",
|
|
1039
|
+
"find": [" node-version: 24.18.1\n"],
|
|
1040
|
+
"replace": [" node-version: 24\n"],
|
|
1041
|
+
"gate": ["bash", "run.sh", "check-node-floor-coherence"],
|
|
1042
|
+
"timeoutSeconds": 120,
|
|
1043
|
+
"signature": "[QK:PI-DURABLE-EMITTER-NODE-CI-BOUND]",
|
|
1044
|
+
"signatureSource": "run.sh"
|
|
1045
|
+
},
|
|
1046
|
+
{
|
|
1047
|
+
"claim": "PI-DURABLE-EMITTER-NODE-CI-REQUIRED",
|
|
1048
|
+
"title": "the check:full step's ENTWURF_REQUIRE_EMITTER_NODE line is commented out, so an emitter-identity mismatch on the CI floor becomes a skip while the workflow text still names the variable",
|
|
1049
|
+
"subject": ".github/workflows/ci.yml",
|
|
1050
|
+
"find": [" ENTWURF_REQUIRE_EMITTER_NODE: \"1\"\n"],
|
|
1051
|
+
"replace": [" # ENTWURF_REQUIRE_EMITTER_NODE: \"1\"\n"],
|
|
1052
|
+
"gate": ["bash", "run.sh", "check-node-floor-coherence"],
|
|
1053
|
+
"timeoutSeconds": 120,
|
|
1054
|
+
"signature": "[QK:PI-DURABLE-EMITTER-NODE-CI-REQUIRED]",
|
|
1055
|
+
"signatureSource": "run.sh"
|
|
1056
|
+
},
|
|
1057
|
+
{
|
|
1058
|
+
"claim": "PI-DURABLE-SUBAGENT-OUTPUT-MODEL-VISIBLE",
|
|
1059
|
+
"title": "the emitted subagent answers in `content`, the 1.1 field: on 1.2 the result still type-checks against the all-optional result and the parent model reads nothing of the child's answer",
|
|
1060
|
+
"subject": "pi/pi-durable/carrier/subagent.js",
|
|
1061
|
+
"find": ["\t\t\t\t\toutput: [{ type: \"text\", text }],"],
|
|
1062
|
+
"replace": ["\t\t\t\t\tcontent: [{ type: \"text\", text }],"],
|
|
1063
|
+
"gate": ["bash", "run.sh", "check-pi-durable-beside"],
|
|
1064
|
+
"timeoutSeconds": 300,
|
|
1065
|
+
"signature": "[QK:PI-DURABLE-SUBAGENT-OUTPUT-MODEL-VISIBLE]",
|
|
1066
|
+
"signatureSource": "pi-extensions/lib/pi-durable-carrier.test.ts",
|
|
1067
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
1068
|
+
},
|
|
1069
|
+
{
|
|
1070
|
+
"claim": "PI-DURABLE-BRIDGE-TOOL-OUTPUT-MODEL-VISIBLE",
|
|
1071
|
+
"title": "the contact's bridge tools answer in `content`, the 1.1 field, past the declared result type: the bridge still builds and the model reads none of the bridge's text",
|
|
1072
|
+
"subject": "pi-extensions/meta-bridge-pi-durable.ts",
|
|
1073
|
+
"find": ["\t\t\treturn { output: [{ type: \"text\", text: result.text }] };"],
|
|
1074
|
+
"replace": ["\t\t\treturn { content: [{ type: \"text\", text: result.text }] } as unknown as DurableToolResult;"],
|
|
1075
|
+
"gate": ["bash", "run.sh", "check-pi-durable-beside"],
|
|
1076
|
+
"timeoutSeconds": 300,
|
|
1077
|
+
"signature": "[QK:PI-DURABLE-BRIDGE-TOOL-OUTPUT-MODEL-VISIBLE]",
|
|
1078
|
+
"signatureSource": "pi-extensions/meta-bridge-pi-durable.test.ts",
|
|
1079
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
1080
|
+
},
|
|
1081
|
+
{
|
|
1082
|
+
"claim": "PI-DURABLE-FATAL-SESSION-VIEW",
|
|
1083
|
+
"title": "the close listener stops recording the failure: a Session that failed and closed itself looks open, and the host learns of it only if `closed` ever settles, which a tool ignoring its abort can hold off for good",
|
|
1084
|
+
"subject": "pi/pi-durable/carrier/runtime.js",
|
|
1085
|
+
"find": ["\t\t\tupdate({ failure: {} });"],
|
|
1086
|
+
"replace": ["\t\t\t// the failure is not recorded"],
|
|
1087
|
+
"gate": ["bash", "run.sh", "check-pi-durable-beside"],
|
|
1088
|
+
"timeoutSeconds": 300,
|
|
1089
|
+
"signature": "[QK:PI-DURABLE-FATAL-SESSION-VIEW]",
|
|
1090
|
+
"signatureSource": "pi-extensions/lib/pi-durable-carrier.test.ts",
|
|
1091
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
1092
|
+
},
|
|
1093
|
+
{
|
|
1094
|
+
"claim": "PI-DURABLE-FATAL-SESSION-SHOWN",
|
|
1095
|
+
"title": "the TUI stops drawing the failed session's line: the view knows, the screen keeps the last live frame and every later keystroke looks accepted",
|
|
1096
|
+
"subject": "pi/pi-durable/carrier/tui.js",
|
|
1097
|
+
"find": ["\tif (view.failure !== undefined) {"],
|
|
1098
|
+
"replace": ["\tif (view.failure === null) {"],
|
|
1099
|
+
"gate": ["bash", "run.sh", "check-pi-durable-beside"],
|
|
1100
|
+
"timeoutSeconds": 300,
|
|
1101
|
+
"signature": "[QK:PI-DURABLE-FATAL-SESSION-SHOWN]",
|
|
1102
|
+
"signatureSource": "pi-extensions/lib/pi-durable-carrier.test.ts",
|
|
1103
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
1104
|
+
},
|
|
1105
|
+
{
|
|
1106
|
+
"claim": "PI-DURABLE-FATAL-SUBMIT-REFUSED",
|
|
1107
|
+
"title": "input to a failed session is handed to the SDK instead of being refused by name: it fails later with the SDK's generic error and the user is not told the session takes no input",
|
|
1108
|
+
"subject": "pi/pi-durable/carrier/runtime.js",
|
|
1109
|
+
"find": ["\t\t\t\t\tif (state.failure !== undefined) {"],
|
|
1110
|
+
"replace": ["\t\t\t\t\tif (state.failure === null) {"],
|
|
1111
|
+
"gate": ["bash", "run.sh", "check-pi-durable-beside"],
|
|
1112
|
+
"timeoutSeconds": 300,
|
|
1113
|
+
"signature": "[QK:PI-DURABLE-FATAL-SUBMIT-REFUSED]",
|
|
1114
|
+
"signatureSource": "pi-extensions/lib/pi-durable-carrier.test.ts",
|
|
1115
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
1116
|
+
},
|
|
1117
|
+
{
|
|
1118
|
+
"claim": "PI-DURABLE-RECEIVER-RETIRE-ON-FATAL",
|
|
1119
|
+
"title": "the doorbell ignores the session's failure: its marker stays and senders keep waking a host that can admit nothing, until a ring happens to be rejected",
|
|
1120
|
+
"subject": "pi-extensions/meta-bridge-pi-durable.ts",
|
|
1121
|
+
"find": ["const problems = retire(\"session-failed\", \"the durable session failed and closed itself\");"],
|
|
1122
|
+
"replace": ["const problems: string[] = [];"],
|
|
1123
|
+
"gate": ["bash", "run.sh", "check-pi-durable-beside"],
|
|
1124
|
+
"timeoutSeconds": 300,
|
|
1125
|
+
"signature": "[QK:PI-DURABLE-RECEIVER-RETIRE-ON-FATAL]",
|
|
1126
|
+
"signatureSource": "pi-extensions/lib/pi-durable-carrier.test.ts",
|
|
1127
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
1128
|
+
},
|
|
1129
|
+
{
|
|
1130
|
+
"claim": "PI-DURABLE-SELF-CLOSE-NOT-FAILURE",
|
|
1131
|
+
"title": "close() keeps its close listener subscribed: every normal exit reports a failed session and retires its listeners as if the storage had failed",
|
|
1132
|
+
"subject": "pi/pi-durable/carrier/runtime.js",
|
|
1133
|
+
"find": [
|
|
1134
|
+
"\t\t\t\t\t// First: the Session runs its close listeners inside its own close call.\n\t\t\t\t\tunsubscribeClose();"
|
|
1135
|
+
],
|
|
1136
|
+
"replace": ["\t\t\t\t\t// First: the Session runs its close listeners inside its own close call."],
|
|
1137
|
+
"gate": ["bash", "run.sh", "check-pi-durable-beside"],
|
|
1138
|
+
"timeoutSeconds": 300,
|
|
1139
|
+
"signature": "[QK:PI-DURABLE-SELF-CLOSE-NOT-FAILURE]",
|
|
1140
|
+
"signatureSource": "pi-extensions/lib/pi-durable-carrier.test.ts",
|
|
1141
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
1142
|
+
},
|
|
1143
|
+
{
|
|
1144
|
+
"claim": "PI-DURABLE-COMPACTION-FAILURE-SHOWN",
|
|
1145
|
+
"title": "the TUI drops the conversation's retained compaction failure: a failed background compaction vanishes from the screen once its status is removed",
|
|
1146
|
+
"subject": "pi/pi-durable/carrier/tui.js",
|
|
1147
|
+
"find": ["\tif (compaction !== undefined) {\n\t\tlines.push"],
|
|
1148
|
+
"replace": ["\tif (compaction === null) {\n\t\tlines.push"],
|
|
1149
|
+
"gate": ["bash", "run.sh", "check-pi-durable-beside"],
|
|
1150
|
+
"timeoutSeconds": 300,
|
|
1151
|
+
"signature": "[QK:PI-DURABLE-COMPACTION-FAILURE-SHOWN]",
|
|
1152
|
+
"signatureSource": "pi-extensions/lib/pi-durable-carrier.test.ts",
|
|
1153
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
1005
1154
|
}
|
|
1006
1155
|
]
|
|
1007
1156
|
}
|
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
"title": "one row of the pnpm workspace closure is left on the previous floor while every other surface moved — the shape a version bump actually fails in. Nothing else reads that file, so the stranded row surfaces only at install time, in a different command, far from the change that stranded it",
|
|
8
8
|
"subject": "pnpm-workspace.yaml",
|
|
9
9
|
"find": [
|
|
10
|
-
" - '@earendil-works/pi-telemetry@0.85.0 || 0.85.1 || 0.86.0 || 0.87.0 || 0.87.1 || 0.99.1 || 0.99.2 || 1.0.0 || 1.0.2 || 1.0.4 || 1.1.0'"
|
|
10
|
+
" - '@earendil-works/pi-telemetry@0.85.0 || 0.85.1 || 0.86.0 || 0.87.0 || 0.87.1 || 0.99.1 || 0.99.2 || 1.0.0 || 1.0.2 || 1.0.4 || 1.1.0 || 1.2.0'"
|
|
11
11
|
],
|
|
12
12
|
"replace": [
|
|
13
|
-
" - '@earendil-works/pi-telemetry@0.85.0 || 0.85.1 || 0.86.0 || 0.87.0 || 0.87.1 || 0.99.1 || 0.99.2 || 1.0.0 || 1.0.2 || 1.0.4'"
|
|
13
|
+
" - '@earendil-works/pi-telemetry@0.85.0 || 0.85.1 || 0.86.0 || 0.87.0 || 0.87.1 || 0.99.1 || 0.99.2 || 1.0.0 || 1.0.2 || 1.0.4 || 1.1.0'"
|
|
14
14
|
],
|
|
15
15
|
"gate": ["bash", "run.sh", "check-dep-versions"],
|
|
16
16
|
"timeoutSeconds": 300,
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"claim": "PI-SDK-PRODUCTION-SET",
|
|
22
22
|
"title": "the durable library's production pin falls one release behind the SDK set the pin declares, so the carrier would bind a pi-durable nobody verified it against",
|
|
23
23
|
"subject": "package.json",
|
|
24
|
-
"find": ["\"@earendil-works/pi-durable\": \"1.
|
|
25
|
-
"replace": ["\"@earendil-works/pi-durable\": \"1.0
|
|
24
|
+
"find": ["\"@earendil-works/pi-durable\": \"1.2.0\","],
|
|
25
|
+
"replace": ["\"@earendil-works/pi-durable\": \"1.1.0\","],
|
|
26
26
|
"gate": ["bash", "run.sh", "check-dep-versions"],
|
|
27
27
|
"timeoutSeconds": 300,
|
|
28
28
|
"signature": "[QK:PI-SDK-PRODUCTION-SET]",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"title": "an @earendil-works package returns as a dev dependency beside its production pin, one name in two dependency classes",
|
|
34
34
|
"subject": "package.json",
|
|
35
35
|
"find": ["\"@biomejs/biome\": \"^2.4.13\","],
|
|
36
|
-
"replace": ["\"@biomejs/biome\": \"^2.4.13\",\n\t\t\"@earendil-works/pi-ai\": \"1.
|
|
36
|
+
"replace": ["\"@biomejs/biome\": \"^2.4.13\",\n\t\t\"@earendil-works/pi-ai\": \"1.2.0\","],
|
|
37
37
|
"gate": ["bash", "run.sh", "check-dep-versions"],
|
|
38
38
|
"timeoutSeconds": 300,
|
|
39
39
|
"signature": "[QK:PI-SDK-NO-DUAL-DECLARATION]",
|
|
@@ -68,7 +68,7 @@ try {
|
|
|
68
68
|
const tool = citizen.contact.extension.tools.find((t) => t.name === name);
|
|
69
69
|
if (tool === undefined) throw new Error(`the contact registers no ${name}`);
|
|
70
70
|
const result = await tool.execute({}, undefined, {});
|
|
71
|
-
return result.
|
|
71
|
+
return result.output.map((block) => block.text).join("\n");
|
|
72
72
|
};
|
|
73
73
|
const view = citizen.durable.view.current();
|
|
74
74
|
const report = {
|
|
@@ -68,6 +68,7 @@ import {
|
|
|
68
68
|
} from "../pi-extensions/lib/meta-session.ts";
|
|
69
69
|
import { FRESH_CALL_BACKENDS } from "../pi-extensions/lib/mux-fresh-call.ts";
|
|
70
70
|
import {
|
|
71
|
+
checkCodexSocketFile,
|
|
71
72
|
readCodexThread,
|
|
72
73
|
realCodexProtocolOpener,
|
|
73
74
|
resolveCodexDefaultSocketPath,
|
|
@@ -95,6 +96,12 @@ import {
|
|
|
95
96
|
sourceCleanupWindows,
|
|
96
97
|
validateFinalMailboxBody,
|
|
97
98
|
} from "./lib/codex-fresh-source-receipts.ts";
|
|
99
|
+
import {
|
|
100
|
+
describeCodexSocketOwnership,
|
|
101
|
+
judgeCodexSocketOwnership,
|
|
102
|
+
observeCodexSocketSubtree,
|
|
103
|
+
parseProcStat,
|
|
104
|
+
} from "./lib/codex-socket-listener.ts";
|
|
98
105
|
import { skipLive } from "./lib/live-skip.ts";
|
|
99
106
|
import { parseTmuxRow, TMUX_COORDINATE_FIELDS, TMUX_COORDINATE_FORMAT } from "./lib/tmux-coordinate-row.ts";
|
|
100
107
|
|
|
@@ -697,7 +704,8 @@ async function run(): Promise<void> {
|
|
|
697
704
|
// `--cut` still refuses it as BLOCKED (MUST SKIP). Nothing is
|
|
698
705
|
// excused — a cut without this receipt remains impossible.
|
|
699
706
|
// PRESENT BUT WRONG -> FAIL, unchanged: a malformed or dead PID, a process whose
|
|
700
|
-
// argv is not a Codex app-server, a foreign uid,
|
|
707
|
+
// argv is not a Codex app-server, a foreign uid, a subtree that
|
|
708
|
+
// does not hold the endpoint's LISTEN socket, A == S. The
|
|
701
709
|
// operator answered, and the answer is a defect.
|
|
702
710
|
// Nothing is inferred either way, and no default is invented for a value the operator
|
|
703
711
|
// did not give. The reproducible green is the invocation VERIFY and the release skill's
|
|
@@ -747,6 +755,10 @@ async function run(): Promise<void> {
|
|
|
747
755
|
fs.existsSync(procDir),
|
|
748
756
|
`missing ${procDir}`,
|
|
749
757
|
);
|
|
758
|
+
// Read BEFORE the uid/argv/environment checks below and handed to the socket-ownership
|
|
759
|
+
// observer, so the process those checks read and the process that owns the socket are the
|
|
760
|
+
// same one: a pid reused in between changes this start time and fails closed (#128).
|
|
761
|
+
const namedStartTime = parseProcStat(fs.readFileSync(path.join(procDir, "stat"), "utf8")).startTime;
|
|
750
762
|
const procStat = fs.statSync(procDir);
|
|
751
763
|
if (typeof process.getuid === "function") {
|
|
752
764
|
ok("the named app-server process belongs to this operator", procStat.uid === process.getuid());
|
|
@@ -774,15 +786,27 @@ async function run(): Promise<void> {
|
|
|
774
786
|
// cannot be borrowed into a claim about what a Codex MCP child inherits.
|
|
775
787
|
appServerEnv = { ...procEnv };
|
|
776
788
|
codexSocketPath = resolveCodexDefaultSocketPath(appServerEnv);
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
789
|
+
// Ownership is read from the kernel, not from argv (#128). The endpoint is the one delivery
|
|
790
|
+
// itself connects to — the production classifier's verified physical socket — and the claim
|
|
791
|
+
// is that the named pid's OWN subtree holds its LISTEN inode. The argv `--listen` spelling
|
|
792
|
+
// is recorded in the receipt below and decides nothing: a wrapper whose child listens and a
|
|
793
|
+
// listener started with the vendor-default `--listen unix://` are both read as what they are.
|
|
794
|
+
const socketFile = checkCodexSocketFile(codexSocketPath);
|
|
795
|
+
ok(
|
|
796
|
+
"the default control path is a live socket or Codex's exact rendezvous alias — the endpoint delivery connects to",
|
|
797
|
+
socketFile.ok,
|
|
798
|
+
socketFile.ok ? "" : `${codexSocketPath}: ${socketFile.reason}`,
|
|
799
|
+
);
|
|
800
|
+
const socketOwnership = judgeCodexSocketOwnership(
|
|
801
|
+
observeCodexSocketSubtree(appServerPid, socketFile.connectPath, namedStartTime),
|
|
780
802
|
);
|
|
781
803
|
ok(
|
|
782
|
-
"the explicit app-server PID
|
|
783
|
-
|
|
784
|
-
|
|
804
|
+
"the explicit app-server PID's own process subtree holds the LISTEN socket at that verified endpoint",
|
|
805
|
+
socketOwnership.verdict === "owned",
|
|
806
|
+
`${describeCodexSocketOwnership(socketOwnership)}; argv=${JSON.stringify(argv)}`,
|
|
785
807
|
);
|
|
808
|
+
receipts["1a-codex-socket-ownership"] =
|
|
809
|
+
`advertised=${codexSocketPath}\nendpoint=${socketFile.connectPath}\n${describeCodexSocketOwnership(socketOwnership)}`;
|
|
786
810
|
const appServerCoordinate = inspectTmuxCoordinate("Codex app-server/MCP inherited seat", appServerEnv);
|
|
787
811
|
const fixtureCoordinate = inspectTmuxCoordinate("fixture/initial-Pi caller seat", process.env);
|
|
788
812
|
ok(
|
|
@@ -538,9 +538,29 @@ ok(
|
|
|
538
538
|
fs.existsSync(liveDb),
|
|
539
539
|
liveDb,
|
|
540
540
|
);
|
|
541
|
+
const sqlite = await import("node:sqlite");
|
|
542
|
+
// A doorbell input stays `placed` while the run that consumed it is active and turns `done` only when that run
|
|
543
|
+
// ends (pi-durable's SubmissionRecord). Pi's relay can land before pd's final generation commits, so the snapshot
|
|
544
|
+
// waits for THIS message's submission to settle (`done` or `unanswered`); the assertions below judge what settled.
|
|
545
|
+
const msgName = /meta-mailbox → enqueued \(([^)]+)\)/.exec(piToPd.text)?.[1] ?? "";
|
|
546
|
+
const doorbellRequestId = `entwurf-doorbell:${pdGid}:${msgName}`;
|
|
547
|
+
if (msgName !== "") {
|
|
548
|
+
receipts["6-native-doorbell-settled"] = await until(`pd's doorbell submission to settle`, TURN_WAIT_MS, () => {
|
|
549
|
+
const live = new sqlite.DatabaseSync(liveDb, { readOnly: true });
|
|
550
|
+
try {
|
|
551
|
+
const rows = live.prepare("SELECT status, record FROM submissions").all() as Array<{
|
|
552
|
+
status: string;
|
|
553
|
+
record: string;
|
|
554
|
+
}>;
|
|
555
|
+
const hit = rows.find((r) => (JSON.parse(r.record) as { requestId?: unknown }).requestId === doorbellRequestId);
|
|
556
|
+
return hit?.status === "done" || hit?.status === "unanswered" ? hit.status : null;
|
|
557
|
+
} finally {
|
|
558
|
+
live.close();
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
}
|
|
541
562
|
const snapshot = path.join(receiptsDir, "pd-session.snapshot.sqlite");
|
|
542
563
|
fs.mkdirSync(receiptsDir, { recursive: true });
|
|
543
|
-
const sqlite = await import("node:sqlite");
|
|
544
564
|
{
|
|
545
565
|
const source = new sqlite.DatabaseSync(liveDb, { readOnly: true });
|
|
546
566
|
try {
|
|
@@ -599,8 +619,7 @@ ok(
|
|
|
599
619
|
JSON.stringify(pdToPi),
|
|
600
620
|
);
|
|
601
621
|
receipts["6-pd-to-pi-settled"] = JSON.stringify(pdToPi);
|
|
602
|
-
const
|
|
603
|
-
const doorbell = pdDoorbellSubmission(nativeSubmissions, rootId, `entwurf-doorbell:${pdGid}:${msgName}`);
|
|
622
|
+
const doorbell = pdDoorbellSubmission(nativeSubmissions, rootId, doorbellRequestId);
|
|
604
623
|
receipts["6-native-doorbell-submission"] = JSON.stringify(doorbell);
|
|
605
624
|
ok(
|
|
606
625
|
"the doorbell admitted THIS message's notice to the root conversation and the submission is done",
|