diff --git a/.github/workflows/docker-runner-check.yml b/.github/workflows/docker-runner-check.yml index ce66a8f1eab..c7011745989 100644 --- a/.github/workflows/docker-runner-check.yml +++ b/.github/workflows/docker-runner-check.yml @@ -32,7 +32,7 @@ on: type: boolean default: false diagnose_pi_startup: - description: "Instrument one retained S native Intel startup with unchanged deadlines (diagnostic only, no provider prompts)" + description: "Instrument one retained W native Intel startup with unchanged deadlines (diagnostic only, no provider prompts)" type: boolean default: false diagnose_ajv_pack: @@ -190,7 +190,7 @@ jobs: test "$SOURCE_E2E_SUPPORT_ONLY" != true test "$DIAGNOSE_AJV" != true test "$IMAGE_MODE" != true - test "$EXPECTED_SOURCE_SHA" = 586af4d7f3b07aa683338aedb4e5182603b70401 + test "$EXPECTED_SOURCE_SHA" = efe019a79f50440d7bd6c3bc6c75fb8f18953093 test "$EXPECTED_LOCK_SHA256" = 38338a6867358440c5ab5993eaeb85fb501bc7df65d24acf52fbd63c880ee4ba test "$(gh api "repos/$REPOSITORY" --jq '.visibility')" = public fi @@ -267,7 +267,7 @@ jobs: if-no-files-found: error manual_pi_startup_diagnostic: - name: Retained S native Intel startup timing diagnostic (not qualification) + name: Retained W native Intel startup timing diagnostic (not qualification) if: github.event_name == 'workflow_dispatch' && inputs.diagnose_pi_startup needs: authorize_manual runs-on: macos-15-intel @@ -288,16 +288,16 @@ jobs: GH_TOKEN: ${{ github.token }} run: | set -euo pipefail - gh api repos/paperclipai/paperclip/actions/artifacts/11200013599/zip \ - > "$RUNNER_TEMP/pi-startup-36940079040.zip" - printf '%s %s\n' 9e0209b69b2ee886c4cc8c9c74310a6c1a9010c81e41d618499b2a4d42f92830 \ - "$RUNNER_TEMP/pi-startup-36940079040.zip" | shasum -a 256 --check + gh api repos/paperclipai/paperclip/actions/artifacts/11204971146/zip \ + > "$RUNNER_TEMP/pi-startup-36952019178.zip" + printf '%s %s\n' 5f9bfabcb5a3d1b3180c2eb6f0eb8ebe58770b0c63a8b7dcbd379729b6c468f9 \ + "$RUNNER_TEMP/pi-startup-36952019178.zip" | shasum -a 256 --check - name: Run one instrumented startup with original assertions and deadlines timeout-minutes: 18 run: | set -euo pipefail python3 -B trusted-ci/scripts/ci/pi-intel/startup_diagnostic.py \ - --archive "$RUNNER_TEMP/pi-startup-36940079040.zip" \ + --archive "$RUNNER_TEMP/pi-startup-36952019178.zip" \ --output "$GITHUB_WORKSPACE/pi-startup-diagnostic-evidence" - name: Admit complete diagnostic evidence within the storage bound if: always() diff --git a/scripts/ci/pi-intel/startup-diagnostic-inputs.json b/scripts/ci/pi-intel/startup-diagnostic-inputs.json index dff12508b52..ffc48b2001c 100644 --- a/scripts/ci/pi-intel/startup-diagnostic-inputs.json +++ b/scripts/ci/pi-intel/startup-diagnostic-inputs.json @@ -1,48 +1,48 @@ { "schema": "paperclip.native-intel-startup-diagnostic-input/v1", - "sourceRevision": "586af4d7f3b07aa683338aedb4e5182603b70401", + "sourceRevision": "efe019a79f50440d7bd6c3bc6c75fb8f18953093", "resolvedLockSha256": "38338a6867358440c5ab5993eaeb85fb501bc7df65d24acf52fbd63c880ee4ba", - "artifactRunId": "36940079040", - "artifactId": "11200013599", - "artifactWorkflowRevision": "9b7a91e3fca55eba893bd62dd7b0ca9e94abdb4d", - "artifactZipBytes": 1128953890, - "artifactZipSha256": "9e0209b69b2ee886c4cc8c9c74310a6c1a9010c81e41d618499b2a4d42f92830", + "artifactRunId": "36952019178", + "artifactId": "11204971146", + "artifactWorkflowRevision": "31e7bc15b00ba651b9c2e321d425a7e6320a48f9", + "artifactZipBytes": 1129002006, + "artifactZipSha256": "5f9bfabcb5a3d1b3180c2eb6f0eb8ebe58770b0c63a8b7dcbd379729b6c468f9", "selectedFiles": { "receipt.json": { - "bytes": 28434, - "sha256": "6e67bf4b79806bde77287350994ee91086cce7574ea5ab893303b1bdf2f40fa4" + "bytes": 26708, + "sha256": "338d7d93ba07b09d55de9a54640eb57c498bb4b35bc7ab1ad02d8fdbb72eb44a" }, "provider-pack.tar.gz": { - "bytes": 1029171454, - "sha256": "825f95d44ae81ec2d382aa2ffc203cf023e3b9862f91b0fa6f592eed9ad96d4f" + "bytes": 1029188649, + "sha256": "680d5a7ed3cb7813fbce0a994d0b91b7ec4991b538da640ed9cc5175baf4b60e" }, "provider-pack.json": { "bytes": 3629, - "sha256": "0cac1cfee6b980601fcb6f34d06060f4e9f5746df06f458632d041b18c47b1a0" + "sha256": "ffb81f911990dc66558a277ab8ec20698af03e671f44bdd8a41fda0f392e4e6a" }, "pack-inventory.json": { - "bytes": 10633747, - "sha256": "c95c9cfbcbefb0e566add7ca70ba9bc115e3ff8c1b6a9d113b677583287b807e" + "bytes": 10633748, + "sha256": "5d2fbdb0976ae1f4431cf66ee3611c2104d23a3ce04910be8941f8a03e36d710" }, "paperclip-runnerd": { "bytes": 32035568, "sha256": "a0c7ace7ad0be9ac5b5f9c58749d9c452cd3a1ef143a38d5120d8e4f2bf82d6a" }, "source.tar": { - "bytes": 141598720, - "sha256": "87f48d5c0df9c4e9c30c880c5b1da862b063d9ba4c12afc4788ee5fd6adcfc6e" + "bytes": 141690880, + "sha256": "587c0f60a30f0ca4b43361830cb611fcaa4a83f492cb7a9fd66b7908b45f72be" }, "source-input-inventory.json": { - "bytes": 1032538, - "sha256": "88ccb84c519bfc99f4ac8d8801c93cf083b3ba5b836405a345697d2243f7b64d" + "bytes": 1033276, + "sha256": "1356cd9a81d4c94e477a50cf2879efc7002fd687337fdad121f0c2381689ab36" }, "resolved-pnpm-lock.yaml": { "bytes": 607788, "sha256": "38338a6867358440c5ab5993eaeb85fb501bc7df65d24acf52fbd63c880ee4ba" } }, - "originalSidecarSha256": "97eedf930681729c6a1654e6c54bcc9657154e5dd2761f9922f4b01c7358f9f7", - "originalPackDigest": "sha256:dd7e629c794dc8cdcd43cf1326b4d812bb3dd97949384f13ce97500f47fc4d01", + "originalSidecarSha256": "1f0ee222b2edaad997b898aec6580982afc179fc60e5abc87fa3e6f2386a691f", + "originalPackDigest": "sha256:ae455e79f07f189e6d5d2bb9eeb01d26040e4b19370bfcda47d288c7b1d60b24", "daemonSha256": "a0c7ace7ad0be9ac5b5f9c58749d9c452cd3a1ef143a38d5120d8e4f2bf82d6a", "nodeSha256": "7abcf39bd37ab251015337ff75304d7555f0d8e88c6e0fbf04bce8ce34636f49", "testPath": "packages/paperclip-runner/test/pi-closed-startup.test.mjs", @@ -51,7 +51,7 @@ "profileDigest": "sha256:5e276f48c8a87b3e6165369faac62d3925282c84b98934575b1b7b97ad50b309", "closureDigest": "sha256:8c6e7802d47da9410af56e367f6cf7af572fcbf3e361627cee026e8cffc9bdd3", "model": "openrouter/deepseek/deepseek-v4-flash-0731", - "originalFailure": "PRP command session.open timed out; settledMs30684; durationMs31720;44SDK contracts passed; cleanup verified", + "originalFailure": "Durable session.open failed: ACPX sidecar request timed out at session.open; drain issued 30.275s after open; secondary close could not durably suspend provider;44SDK contracts passed; cleanup verified; no report.json produced", "executionCount": 1, "providerCalls": 0, "timeoutChanges": false, diff --git a/scripts/ci/pi-intel/startup_diagnostic.py b/scripts/ci/pi-intel/startup_diagnostic.py index 76609f77bc4..106ced90af9 100644 --- a/scripts/ci/pi-intel/startup_diagnostic.py +++ b/scripts/ci/pi-intel/startup_diagnostic.py @@ -104,7 +104,7 @@ def execute(args): sidecar=pack/'dist/cli/acpx-runtime-sidecar.cjs';original_sidecar=sidecar.read_bytes();patched,patch=patch_sidecar(original_sidecar,identity) # Preserve original file mode; this one private copy now has an explicit diagnostic identity. sidecar.write_bytes(patched);atomic_json(out/'sidecar-patch.json',patch) - (out/'sidecar.diff').write_text(''.join(difflib.unified_diff(original_sidecar.decode().splitlines(True),patched.decode().splitlines(True),fromfile='original-S-sidecar',tofile='diagnostic-sidecar'))) + (out/'sidecar.diff').write_text(''.join(difflib.unified_diff(original_sidecar.decode().splitlines(True),patched.decode().splitlines(True),fromfile='original-W-sidecar',tofile='diagnostic-sidecar'))) command('diagnostic-sidecar-syntax',[node,'--check',sidecar]) proof['diagnosticPackVerification']=json.loads(command('diagnostic-pack-rebind',[node,HERE/'rebind-diagnostic-pack.mjs',pack,PIN['originalSidecarSha256'],PIN['sourceRevision']],180)) after_manifest=json.loads((pack/'provider-pack.json').read_text());expected=copy.deepcopy(before_manifest) diff --git a/scripts/ci/pi-intel/startup_timing_patch.py b/scripts/ci/pi-intel/startup_timing_patch.py index f60ed9e4ce5..b8874cec6ce 100644 --- a/scripts/ci/pi-intel/startup_timing_patch.py +++ b/scripts/ci/pi-intel/startup_timing_patch.py @@ -1,7 +1,7 @@ """Sparse additive instrumentation of one pinned bundle; never edits vendor closure bytes.""" import hashlib,json from source_guard import require -ORIGINAL_SHA='97eedf930681729c6a1654e6c54bcc9657154e5dd2761f9922f4b01c7358f9f7' +ORIGINAL_SHA='1f0ee222b2edaad997b898aec6580982afc179fc60e5abc87fa3e6f2386a691f' PREFIX='__pcStartupDiagnostic' def patch_sidecar(original,sink): @@ -31,13 +31,13 @@ def patch_sidecar(original,sink): mark('// src/drivers/acpx/runtime-host.ts',' const commandOwner = createAcpxCommandLeaseOwner(','command.acquire.end') mark('// src/drivers/acpx/runtime-host.ts',' runtime = await acquireAbortableAdmissionResource({','runtime.acquire.begin') mark('// src/drivers/acpx/pi-installation.ts','async function verifyPiInstallation(profile) {','pi.install.begin',True) - mark('// src/drivers/acpx/pi-installation.ts',' const verified = await verifyPiRuntimeManifest(runtimeRoot, manifest);','pi.manifest.begin') - mark('// src/drivers/acpx/pi-installation.ts',' if (verified.manifestDigest !== metadata.manifestDigest)','pi.manifest.end') + mark('// src/drivers/acpx/pi-installation.ts',' const entries = await readNativeAcpxDistributionEntries(declaration);','pi.native_manifest.begin') + mark('// src/drivers/acpx/pi-installation.ts',' const layout = await verifyPiRuntimeLayoutForNativeSnapshot(runtimeRoot, manifest, { entries }, expectedClosure);','pi.native_manifest.end') mark('// src/drivers/acpx/pi-installation.ts',' return Object.freeze({','pi.install.end') - mark('// src/drivers/acpx/pi-verified-runtime.ts','async function inventoryPiRuntimeFiles(root) {','pi.discovery.begin',True) + mark('// src/drivers/acpx/pi-verified-runtime.ts','async function discoverPiRuntimeFiles(root) {','pi.discovery.begin',True) mark('// src/drivers/acpx/pi-verified-runtime.ts',' await visit(physicalRoot);','pi.discovery.end',True) - mark('// src/drivers/acpx/pi-verified-runtime.ts',' for (let index = 0; index < regular.length;','pi.hash.begin') - mark('// src/drivers/acpx/pi-verified-runtime.ts',' return files;','pi.hash.end') + mark('// src/drivers/acpx/pi-verified-runtime.ts','async function verifyPiRuntimeLayoutForNativeSnapshot(root, manifest, nativeManifest, expectedClosureSha256) {','pi.layout.begin',True) + mark('// src/drivers/acpx/pi-verified-runtime.ts',' return { manifestDigest };','pi.layout.end') mark('// src/drivers/acpx/native-distribution-integrity.ts','async function createNativeAcpxDistributionSnapshot(input, entries) {','snapshot.begin',True) mark('// src/drivers/acpx/native-distribution-integrity.ts',' const copyEntry = async (entry) => {','snapshot.directories.end') mark('// src/drivers/acpx/native-distribution-integrity.ts',' for (let start = 0; start < entries.length; ) {','snapshot.copy.begin') diff --git a/scripts/ci/pi-intel/test_startup_diagnostic.py b/scripts/ci/pi-intel/test_startup_diagnostic.py index 1f50f596c95..f19d2148c27 100644 --- a/scripts/ci/pi-intel/test_startup_diagnostic.py +++ b/scripts/ci/pi-intel/test_startup_diagnostic.py @@ -1,7 +1,7 @@ import copy,hashlib,json,os,subprocess,tempfile,unittest from pathlib import Path from startup_timing_patch import patch_sidecar,ORIGINAL_SHA -from startup_diagnostic import validate_pack_delta,validate_timing_sink,reject_sink_failure +from startup_diagnostic import validate_pack_delta,validate_timing_sink,reject_sink_failure,PIN from diagnostic_lifecycle import DiagnosticChild,DiagnosticInspection from owned_processes import process_table,command_tokens,ProcessInspectionUnavailable from unittest.mock import patch @@ -40,6 +40,35 @@ class DiagnosticTests(unittest.TestCase): self.assertEqual(len(proof['phases']),46) def test_wrong_original_rejected(self): with self.assertRaises(RuntimeError):patch_sidecar(self.original+b' ',{'path':'unused','dev':'1','ino':'2','uid':'3'}) + def test_retained_w_pack_identity_and_original_test_contract(self): + self.assertEqual(PIN['sourceRevision'],'efe019a79f50440d7bd6c3bc6c75fb8f18953093') + self.assertEqual(PIN['artifactRunId'],'36952019178') + self.assertEqual(PIN['artifactId'],'11204971146') + self.assertEqual(PIN['originalSidecarSha256'],ORIGINAL_SHA) + self.assertEqual(PIN['selectedFiles']['paperclip-runnerd']['sha256'],PIN['daemonSha256']) + self.assertEqual(PIN['selectedFiles']['resolved-pnpm-lock.yaml']['sha256'],PIN['resolvedLockSha256']) + self.assertEqual(PIN['testSha256'],'8967cf9c8cd130b68bf9d64abef8cb8d352af00646e2288b341d8c6ae758b47a') + self.assertEqual((PIN['executionCount'],PIN['providerCalls'],PIN['timeoutChanges']),(1,0,False)) + self.assertEqual((PIN['archiveBytesMaximum'],PIN['retentionDays']),(268435456,7)) + def test_w_markers_follow_outer_layout_and_snapshot_boundaries(self): + _,proof=patch_sidecar(self.original,{'path':'/private/tmp/pc-intel-diagnostic-fixture/startup-timings.jsonl','dev':'1','ino':'2','uid':'501'}) + phases=proof['phases'] + self.assertFalse(any(p.startswith('pi.hash.') for p in phases)) + for begin,end in [('pi.native_manifest.begin','pi.native_manifest.end'),('pi.discovery.begin','pi.discovery.end'),('pi.layout.begin','pi.layout.end'),('snapshot.copy.begin','snapshot.copy.end'),('snapshot.seal.begin','snapshot.seal.end')]: + self.assertEqual(phases.count(begin),1);self.assertEqual(phases.count(end),1) + # Discovery recursion and descriptor-copy loops must not emit one row per file. + source=self.original.decode() + for begin,end in [(' const visit = async (directory) => {',' await visit(physicalRoot);'),(' const copyEntry = async (entry) => {',' for (let start = 0; start < entries.length; ) {')]: + start=source.index(begin);finish=source.index(end,start) + interior=[x for x in proof['insertions'] if start < x['offset'] < finish] + self.assertEqual(interior,[]) + def test_missing_terminal_retains_incomplete_classification(self): + with tempfile.TemporaryDirectory() as tmp: + p,b=self.binding(Path(tmp)) + p.write_text(''.join(json.dumps({'phase':phase,'ns':str(i)})+'\n' for i,phase in enumerate(['sidecar.entry','session.open.begin','snapshot.copy.begin']))) + with self.assertRaisesRegex(RuntimeError,'terminal marks missing'): + validate_timing_sink(p,b,{'sidecar.entry','session.open.begin','snapshot.copy.begin','sidecar.exit'}) + self.assertIn('snapshot.copy.begin',p.read_text()) def test_numeric_inode_rejected(self): with self.assertRaises(RuntimeError):patch_sidecar(self.original,{'path':'/private/tmp/pc-intel-diagnostic-x/startup-timings.jsonl','dev':1,'ino':2,'uid':3}) def run_prelude(self,extra='',identity_mutation=None):