From 145d1edca3dcb47fa4ba2b84e964888e8b54d258 Mon Sep 17 00:00:00 2001 From: Stefan Preu Date: Sun, 27 Sep 2026 00:22:19 +0200 Subject: [PATCH 1/3] Add static extension inventory through the public SDK and CLI Expose configured project and global extension state without importing extension entrypoints, running lifecycle hooks, checking updates, or writing tracker files. Return explicit completeness and source errors so hosted and agent reads do not confuse saved enablement with runtime activation. Register package inventory and the compatibility aliases in CLI grammar, refresh generated contracts and error catalogs, and document the configured state boundary. Add source and built CLI acceptance coverage plus a manual import-marker negative control. Close pm-lhhnx9 with linked code, docs, test, defect-gate, and changelog evidence. Record the separate linked-test context issue pm-t05d8d with historical dependencies while leaving it open and unclaimed. --- .../pm/extensions/.managed-extensions.json | 2 +- .agents/pm/history/pm-lhhnx9.jsonl | 25 ++ .agents/pm/history/pm-t05d8d.jsonl | 8 + .agents/pm/issues/pm-lhhnx9.toon | 63 ++++- .agents/pm/issues/pm-t05d8d.toon | 22 ++ CHANGELOG.md | 4 + docs/EXTENSION_LIFECYCLE.md | 18 ++ docs/generated/REFUSAL_CLOSURE_CENSUS.md | 12 +- scripts/release/surface-replication-sets.json | 4 +- sdk/public-surface.json | 30 +++ src/cli/main.ts | 16 +- src/cli/register-setup.ts | 42 +++- src/cli/runtime/selection.ts | 11 +- src/sdk/cli-contracts/grammar-contracts.ts | 4 + src/sdk/extension/static-inventory.ts | 223 ++++++++++++++++++ .../generated-error-code-catalog-part-1.ts | 65 ++++- .../generated-error-code-catalog-part-2.ts | 39 +++ src/sdk/index.ts | 1 + tests/fixtures/contracts/full.json | 142 +++++++++++ ...ic-extension-inventory.integration.spec.ts | 146 ++++++++++++ .../cli/static-extension-inventory.spec.ts | 53 +++++ 21 files changed, 894 insertions(+), 36 deletions(-) create mode 100644 .agents/pm/history/pm-t05d8d.jsonl create mode 100644 .agents/pm/issues/pm-t05d8d.toon create mode 100644 src/sdk/extension/static-inventory.ts create mode 100644 tests/integration/extensions/static-extension-inventory.integration.spec.ts create mode 100644 tests/unit/cli/static-extension-inventory.spec.ts diff --git a/.agents/pm/extensions/.managed-extensions.json b/.agents/pm/extensions/.managed-extensions.json index 94a2bf526..87a26482f 100644 --- a/.agents/pm/extensions/.managed-extensions.json +++ b/.agents/pm/extensions/.managed-extensions.json @@ -1,6 +1,6 @@ { "version": 1, - "updated_at": "2026-09-26T16:33:16.667Z", + "updated_at": "2026-09-26T22:20:41.276Z", "entries": [ { "name": "pm-changelog", diff --git a/.agents/pm/history/pm-lhhnx9.jsonl b/.agents/pm/history/pm-lhhnx9.jsonl index f0f5081c0..05a4f883f 100644 --- a/.agents/pm/history/pm-lhhnx9.jsonl +++ b/.agents/pm/history/pm-lhhnx9.jsonl @@ -1,2 +1,27 @@ {"hash_algorithm":"sha256","ts":"2026-09-26T16:19:02.178Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"469d814b4c0d85a8d28fe809","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"create","patch":[{"op":"add","path":"/metadata/id","value":"pm-lhhnx9"},{"op":"add","path":"/metadata/title","value":"Expose static extension inventory for hosted and agent reads"},{"op":"add","path":"/metadata/description","value":"A default extension explore read activates installed extension code, so hosted GET routes need an explicit static configured-state inventory through the public SDK and CLI with honest completeness and error receipts."},{"op":"add","path":"/metadata/type","value":"Issue"},{"op":"add","path":"/metadata/status","value":"open"},{"op":"add","path":"/metadata/priority","value":1},{"op":"add","path":"/metadata/tags","value":["agent-ux","area:extensions","area:sdk","gh-1316","security"]},{"op":"add","path":"/metadata/created_at","value":"2026-09-26T16:19:02.178Z"},{"op":"add","path":"/metadata/updated_at","value":"2026-09-26T16:19:02.178Z"},{"op":"add","path":"/metadata/author","value":"harness:codex"},{"op":"add","path":"/metadata/acceptance_criteria","value":"Static CLI and SDK inventory never import or activate extension code, adopt state, check updates, or write workspace files; installed, absent, inactive, malformed, and global/project cases are distinct; completeness and errors are explicit; docs distinguish configured state from runtime probes."},{"op":"add","path":"/metadata/parent","value":"pm-grst"},{"op":"add","path":"/metadata/dependencies","value":[{"id":"pm-5mua","kind":"verifies","created_at":"2026-09-26T16:19:02.178Z","author":"harness:codex","source_kind":"cli:create:dep","author_source":"detected"},{"id":"pm-x6jf","kind":"discovered_from","created_at":"2026-09-26T16:19:02.178Z","author":"harness:codex","source_kind":"cli:create:dep","author_source":"detected"}]},{"op":"add","path":"/metadata/comments","value":[{"created_at":"2026-09-26T16:19:02.178Z","author":"harness:codex","text":"Duplicate check 2026-09-26: all-status searches for extension state inventory, side-effect-free extension, and hosted extension read found closed pm-grst managed state, pm-x6jf public lifecycle SDK, pm-l4c8 runtime describe, and pm-5mua runtime command-path diagnostics. Their shipped runtime probes do not provide the requested static configured-state receipt. GitHub issue: https://github.com/unbraind/pm-cli/issues/1316."}]}],"before_hash":"3cc22dff72be7b14824654a7a64ea62b04799939b2fee54c1b5f52ca60bf6df0","after_hash":"d0f0e9baf4a57e89f667451017a42229850297f23f0b0df709e5269d4832e796","item_hash_version":3,"message":"","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"3c636d7ebc8786b46c8786a7c069ffe94c223173bc0758ff5973ad43a1b54eaa"} {"hash_algorithm":"sha256","ts":"2026-09-26T16:33:43.788Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"469d814b4c0d85a8d28fe809","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"update","patch":[{"op":"add","path":"/metadata/dependencies/2","value":{"id":"pm-l4c8","kind":"verifies","created_at":"2026-09-26T16:33:43.474Z","author":"harness:codex","source_kind":"cli:update:dep","author_source":"detected"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T16:33:43.788Z"}],"before_hash":"d0f0e9baf4a57e89f667451017a42229850297f23f0b0df709e5269d4832e796","after_hash":"47fe5a35775cf8f9dfd7eb19859f6e7e7e82824b7125f9c9460408ca2002c4f5","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"78ddd1bc0e4b12e0cd5319aa80d93d72157255687730bf98b650781e0ff7adb8"} +{"hash_algorithm":"sha256","ts":"2026-09-26T20:02:18.285Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T20:02:18.285Z"},{"op":"add","path":"/metadata/assignee","value":"harness:codex"},{"op":"add","path":"/metadata/claim_principal","value":"harness:codex#1988a47670c5b401cd591f8d"}],"before_hash":"47fe5a35775cf8f9dfd7eb19859f6e7e7e82824b7125f9c9460408ca2002c4f5","after_hash":"32f83c2b6ddbf93a117c7cbb8e9d74f13017f5b939f6db0f98f27acf1a09b79b","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"7d5456c24921c5d0fe0716349f01b57f246ce55a31294e69822e2bf0fe19aed8"} +{"hash_algorithm":"sha256","ts":"2026-09-26T20:02:18.446Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","lineage:pm-lhhnx9","lineage:pm-grst","lineage:pm-m9jc"]},"topic":{"value":"pm-lhhnx9","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","lineage:pm-lhhnx9","lineage:pm-grst","lineage:pm-m9jc"]}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T20:02:18.446Z"},{"op":"replace","path":"/metadata/status","value":"in_progress"}],"before_hash":"32f83c2b6ddbf93a117c7cbb8e9d74f13017f5b939f6db0f98f27acf1a09b79b","after_hash":"ff6a7f0f86728af4fda926938b161c691ae57b725c53f50bdc95457d0dcd314a","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"ff04b9f753fe1c90d299449621c80f7c02b1fa3f82ed0af93d0a02ab0481ac98"} +{"hash_algorithm":"sha256","ts":"2026-09-26T20:44:28.471Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"release-operator","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]},"topic":{"value":"pm-lhhnx9","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T20:44:28.471Z"},{"op":"add","path":"/metadata/files","value":[{"path":"sdk/public-surface.json","scope":"project","note":"Published additive SDK contract"},{"path":"src/cli/main.ts","scope":"project","note":"Bypass extension activation and writable CLI bootstrap for inventory"},{"path":"src/cli/register-setup.ts","scope":"project","note":"Package and extension inventory command"},{"path":"src/cli/runtime/selection.ts","scope":"project","note":"Static invocation routing"},{"path":"src/sdk/extension/static-inventory.ts","scope":"project","note":"Read-only SDK configured-state primitive"},{"path":"src/sdk/index.ts","scope":"project","note":"Public SDK export"},{"path":"tests/integration/extensions/static-extension-inventory.integration.spec.ts","scope":"project","note":"CLI and SDK behavior with nonactivation and read failure controls"}]}],"before_hash":"ff6a7f0f86728af4fda926938b161c691ae57b725c53f50bdc95457d0dcd314a","after_hash":"7e7e5020161d1b8e272ff3651577041724c74ee873ac08b51bc1b4c0e7aede06","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"6df4d573bdd998b9062b8a48c8f83e9b8f0b8b9947a475224ca707d115cbf56c"} +{"hash_algorithm":"sha256","ts":"2026-09-26T20:44:29.171Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"release-operator","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]},"topic":{"value":"pm-lhhnx9","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]}},"op":"docs_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T20:44:29.171Z"},{"op":"add","path":"/metadata/docs","value":[{"path":"docs/EXTENSION_LIFECYCLE.md","scope":"project","note":"Configured-state inventory and runtime-probe guidance"}]}],"before_hash":"7e7e5020161d1b8e272ff3651577041724c74ee873ac08b51bc1b4c0e7aede06","after_hash":"b2eac8140f7474f68efc729770ac843de9596944b68280c2f58fbf2e9ff52f9e","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"15b63ca623663b4d8516d6be983941c0a39056932b20a867e050ca5598d8c90e"} +{"hash_algorithm":"sha256","ts":"2026-09-26T20:44:29.851Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"release-operator","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]},"topic":{"value":"pm-lhhnx9","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]}},"op":"tests_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T20:44:29.851Z"},{"op":"add","path":"/metadata/tests","value":[{"command":"node scripts/run-tests.mjs test -- tests/integration/extensions/static-extension-inventory.integration.spec.ts","path":"tests/integration/extensions/static-extension-inventory.integration.spec.ts","scope":"project","timeout_seconds":240,"provenance":{"author":"harness:codex","created_at":"2026-09-26T20:44:29.754Z","source_kind":"local_mutation","source_ref":"feat/static-extension-inventory-and-agent-safety"}}]}],"before_hash":"b2eac8140f7474f68efc729770ac843de9596944b68280c2f58fbf2e9ff52f9e","after_hash":"4adefc8690bfc0437ecd60c7089d1bb34b84d4ac64653f8c58c80e8311e7d1e0","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"a0a5e52b9f1ef2e802505ab893974ac2870e72110247d7a8febb281c668f1f30"} +{"hash_algorithm":"sha256","ts":"2026-09-26T20:44:49.032Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"release-operator","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]},"topic":{"value":"pm-lhhnx9","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]}},"op":"test_run_track","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T20:44:49.032Z"},{"op":"add","path":"/metadata/test_runs","value":[{"run_id":"test-local-muiuyx33-rppz27","kind":"test","status":"passed","started_at":"2026-09-26T20:44:34.836Z","finished_at":"2026-09-26T20:44:49.023Z","recorded_at":"2026-09-26T20:44:49.023Z","passed":1,"failed":0,"skipped":0,"executions":[{"command":"node scripts/run-tests.mjs test -- tests/integration/extensions/static-extension-inventory.integration.spec.ts","requested_pm_context_mode":"schema","pm_context_mode":"schema","workspace_context_mode":"source","trust_reason":"local_source_ref"}]}]}],"before_hash":"4adefc8690bfc0437ecd60c7089d1bb34b84d4ac64653f8c58c80e8311e7d1e0","after_hash":"f793e28ba338fd926671f8fe0dbf131fb64121e5b2ffc6cbf9b60d47dacaf499","item_hash_version":3,"message":"Track test run summary (test-local-muiuyx33-rppz27)","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"16845c9d24a665a1311d494f8de8c6630b676ecab7321ac40c3e34d1bbb40603"} +{"hash_algorithm":"sha256","ts":"2026-09-26T21:24:21.475Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"release-operator","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]},"topic":{"value":"pm-lhhnx9","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]}},"op":"files_add","patch":[{"op":"add","path":"/metadata/files/7","value":{"path":"tests/unit/cli/static-extension-inventory.spec.ts","scope":"project","note":"Source CLI registration and routing coverage"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T21:24:21.475Z"}],"before_hash":"f793e28ba338fd926671f8fe0dbf131fb64121e5b2ffc6cbf9b60d47dacaf499","after_hash":"2993f6ae182d230218b07345f196528116b45351450b158c6256880042158b3d","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"27e7c7bdda3cf58dd874b8930cbe35c67e1126b29daef40a7e8c0263feff6e83"} +{"hash_algorithm":"sha256","ts":"2026-09-26T21:26:34.351Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"release-operator","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]},"topic":{"value":"pm-lhhnx9","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/1","value":{"created_at":"2026-09-26T21:26:34.351Z","author":"harness:codex","text":"Implementation evidence 2026-09-26: public SDK inspectStaticExtensionInventory and package/extension inventory CLI read settings, managed metadata, and manifests without loading entrypoints or running hooks. Receipts distinguish installed, inactive, absent, malformed, project/global, and incomplete sources. A disposable initialized workspace with an import-time write marker returned complete inventory without marker or tracker changes; explicit runtime explore afterward created the marker, proving the negative control. Source CLI and dist integration tests cover the behavior."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T21:26:34.351Z"}],"before_hash":"2993f6ae182d230218b07345f196528116b45351450b158c6256880042158b3d","after_hash":"d1a222421ce4536ac4915cbb6e88ead16e465203e55d3db8f784596f37e7cce7","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"e136376bce326f7b94acaff105ee17ef59386d88f4176379d9f78b5ccf026b35"} +{"hash_algorithm":"sha256","ts":"2026-09-26T21:26:35.294Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"release-operator","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]},"topic":{"value":"pm-lhhnx9","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/2","value":{"created_at":"2026-09-26T21:26:35.294Z","author":"harness:codex","text":"Verification evidence 2026-09-26: full sandboxed suite passed 748 files / 9,367 tests with exact statements 65,668/65,668, branches 50,209/50,209, functions 13,594/13,594, lines 62,715/62,715. Focused source and dist tests passed 10/10 after final help wording. pnpm build, typecheck, ESLint, jscpd, docs/skills, SDK surface, and flag-help gate passed; pm health summary ok with zero warnings. pm-changelog installed version 2026.9.25 matches npm latest."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T21:26:35.294Z"}],"before_hash":"d1a222421ce4536ac4915cbb6e88ead16e465203e55d3db8f784596f37e7cce7","after_hash":"cd7a283eccaaff6778a2f62e8ffbdadd78b2ed3e9f7c41e6351fb8173cd33e4c","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"58f19345abed7c32ee40e12d56cb3018f354c776a74a4f89af6e229c4cd015db"} +{"hash_algorithm":"sha256","ts":"2026-09-26T21:26:36.371Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"release-operator","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]},"topic":{"value":"pm-lhhnx9","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]}},"op":"learning_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T21:26:36.371Z"},{"op":"add","path":"/metadata/learnings","value":[{"created_at":"2026-09-26T21:26:36.371Z","author":"harness:codex","text":"A host GET route must use the static inventory primitive; runtime explore/manage/doctor/describe can import installed extension code. Read settings directly for inventory because the general settings reader may scaffold schema files. Keep configured enablement and runtime activation as separate facts, and fail incomplete reads explicitly."}]}],"before_hash":"cd7a283eccaaff6778a2f62e8ffbdadd78b2ed3e9f7c41e6351fb8173cd33e4c","after_hash":"12ac7e9efbea807f7a48b7603ea2b122f87e4d5dc1662faa74daf3933c71f0cb","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"7163dbfc12edda8f640c30c18717fd0516de7527a587f0f61c7d51c8e5d9e728"} +{"hash_algorithm":"sha256","ts":"2026-09-26T21:26:37.487Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"release-operator","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]},"topic":{"value":"pm-lhhnx9","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]}},"op":"close","patch":[{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T21:26:37.487Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/closed_at","value":"2026-09-26T21:26:37.459Z"},{"op":"add","path":"/metadata/completed_at","value":"2026-09-26T21:26:37.459Z"},{"op":"add","path":"/metadata/resolution","value":"Added public configured-state inventory, safe pre-bootstrap CLI routing, explicit completeness/error receipts, SDK surface, docs, and source/dist tests."},{"op":"add","path":"/metadata/expected_result","value":"Hosted and agent reads can inspect saved extension state without executing package code or mutating the tracker, with honest project/global and error reporting."},{"op":"add","path":"/metadata/actual_result","value":"Disposable workspace inventory preserved tracker files and did not trigger an import-time marker; runtime explore did. Full 9,367-test suite passed with exact 100/100/100/100 coverage and static gates passed."},{"op":"add","path":"/metadata/close_reason","value":"Delivered static SDK and CLI extension inventory with no activation or workspace writes, documented semantics, manual negative control, and exact coverage."}],"before_hash":"12ac7e9efbea807f7a48b7603ea2b122f87e4d5dc1662faa74daf3933c71f0cb","after_hash":"fcb8add5de8c29406cf65bb32a82cbb39d7700484f04e8344d3e33710f00ea78","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"53ec01b0a03a67c9effb541718ea4dc2f8fc7633487ad6ccc6d130ab27c232ba"} +{"hash_algorithm":"sha256","ts":"2026-09-26T21:26:38.627Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"release-operator","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]},"topic":{"value":"pm-lhhnx9","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]}},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T21:26:38.627Z"}],"before_hash":"fcb8add5de8c29406cf65bb32a82cbb39d7700484f04e8344d3e33710f00ea78","after_hash":"b088a6b4791f90c3d962ae606c60465222d8af45ad91dc7c3452899e15c265f3","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"2d4950d10aa1681f161abf83fd3561b18295ea76b3dd5ba1bc3e4b1a6bf6ab0b"} +{"hash_algorithm":"sha256","ts":"2026-09-26T21:27:25.087Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"docs_add","patch":[{"op":"replace","path":"/metadata/docs/0/note","value":"Generated pm-changelog 2026.9.25 entry for closed static inventory item"},{"op":"replace","path":"/metadata/docs/0/path","value":"CHANGELOG.md"},{"op":"add","path":"/metadata/docs/1","value":{"path":"docs/EXTENSION_LIFECYCLE.md","scope":"project","note":"Configured-state inventory and runtime-probe guidance"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T21:27:25.087Z"}],"before_hash":"b088a6b4791f90c3d962ae606c60465222d8af45ad91dc7c3452899e15c265f3","after_hash":"883d26c5320a54d7d09f32a83460844c6f2127bc57c230309389b3980fbdf5a3","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"56c65bed880b0fd336dc001cf4516e1749b79776908494ee91165437cbbd291c"} +{"hash_algorithm":"sha256","ts":"2026-09-26T21:27:25.739Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/3","value":{"created_at":"2026-09-26T21:27:25.738Z","author":"harness:codex","text":"Changelog evidence: refreshed release tags, installed npm-latest pm-changelog 2026.9.25, regenerated CHANGELOG.md from 2,510 closed items, and its canonical --check rerun reported unchanged."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T21:27:25.739Z"}],"before_hash":"883d26c5320a54d7d09f32a83460844c6f2127bc57c230309389b3980fbdf5a3","after_hash":"87799e514f0af820d6eca88d21f8ecea88d230c204d54b5bdcb17a6bafce860b","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"62e12164b580996845cce889146388b9b0e2f62ceee4822410f9799b972d37f7"} +{"hash_algorithm":"sha256","ts":"2026-09-26T21:33:48.986Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/7/note","value":"Generated SDK error catalog for static inventory receipts"},{"op":"replace","path":"/metadata/files/7/path","value":"src/sdk/generated/generated-error-code-catalog-part-2.ts"},{"op":"replace","path":"/metadata/files/6/note","value":"Generated SDK error catalog for static inventory receipts"},{"op":"replace","path":"/metadata/files/6/path","value":"src/sdk/generated/generated-error-code-catalog-part-1.ts"},{"op":"replace","path":"/metadata/files/5/note","value":"Read-only SDK configured-state primitive"},{"op":"replace","path":"/metadata/files/5/path","value":"src/sdk/extension/static-inventory.ts"},{"op":"replace","path":"/metadata/files/4/note","value":"Static invocation routing"},{"op":"replace","path":"/metadata/files/4/path","value":"src/cli/runtime/selection.ts"},{"op":"replace","path":"/metadata/files/3/note","value":"Package and extension inventory command"},{"op":"replace","path":"/metadata/files/3/path","value":"src/cli/register-setup.ts"},{"op":"replace","path":"/metadata/files/2/note","value":"Bypass extension activation and writable CLI bootstrap for inventory"},{"op":"replace","path":"/metadata/files/2/path","value":"src/cli/main.ts"},{"op":"replace","path":"/metadata/files/1/note","value":"Published additive SDK contract"},{"op":"replace","path":"/metadata/files/1/path","value":"sdk/public-surface.json"},{"op":"replace","path":"/metadata/files/0/note","value":"Installer metadata timestamp from latest pm-changelog verification"},{"op":"replace","path":"/metadata/files/0/path","value":".agents/pm/extensions/.managed-extensions.json"},{"op":"add","path":"/metadata/files/8","value":{"path":"src/sdk/index.ts","scope":"project","note":"Public SDK export"}},{"op":"add","path":"/metadata/files/9","value":{"path":"tests/integration/extensions/static-extension-inventory.integration.spec.ts","scope":"project","note":"CLI and SDK behavior with nonactivation and read failure controls"}},{"op":"add","path":"/metadata/files/10","value":{"path":"tests/unit/cli/static-extension-inventory.spec.ts","scope":"project","note":"Source CLI registration and routing coverage"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T21:33:48.986Z"}],"before_hash":"87799e514f0af820d6eca88d21f8ecea88d230c204d54b5bdcb17a6bafce860b","after_hash":"985b035790c0978a5bc727a6e615424e4dca3777934587072728489b0722783d","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"83f61d6f5db04161a5f57de426f3eee84330f6c683796617c62bf6c3269f77fe"} +{"hash_algorithm":"sha256","ts":"2026-09-26T21:33:49.678Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/4","value":{"created_at":"2026-09-26T21:33:49.678Z","author":"harness:codex","text":"Post-close preflight: repository static gate required regenerating the SDK error-code catalog for seven new static inventory receipt codes. Linked both generated partitions. Latest pm-changelog installation also refreshed only its managed metadata timestamp; linked that PM-managed artifact. The implementation and close outcome are unchanged."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T21:33:49.678Z"}],"before_hash":"985b035790c0978a5bc727a6e615424e4dca3777934587072728489b0722783d","after_hash":"01b9cd840fad7885e1553b63f44465ae539bbe4c6d9c2af206cabdccc3d271e7","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"a721707ece24e90fcb581df2c0fe91d8e7469407a0de3a7f05925eda43e7f940"} +{"hash_algorithm":"sha256","ts":"2026-09-26T21:37:21.747Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/10/note","value":"CLI and SDK behavior with nonactivation and read failure controls"},{"op":"replace","path":"/metadata/files/10/path","value":"tests/integration/extensions/static-extension-inventory.integration.spec.ts"},{"op":"replace","path":"/metadata/files/9/note","value":"CLI command contract snapshot for package inventory"},{"op":"replace","path":"/metadata/files/9/path","value":"tests/fixtures/contracts/full.json"},{"op":"add","path":"/metadata/files/11","value":{"path":"tests/unit/cli/static-extension-inventory.spec.ts","scope":"project","note":"Source CLI registration and routing coverage"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T21:37:21.747Z"}],"before_hash":"01b9cd840fad7885e1553b63f44465ae539bbe4c6d9c2af206cabdccc3d271e7","after_hash":"0e2560b20205a3f841cffe33aa427dd4201c0a02ce815d1c72fa2026af41f1d3","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"45e120533e499d18bba4b7c6dc4bac08bb803e002d829b9e07df05bec63faf1b"} +{"hash_algorithm":"sha256","ts":"2026-09-26T21:37:22.472Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/5","value":{"created_at":"2026-09-26T21:37:22.471Z","author":"harness:codex","text":"Static gate also required refreshing the CLI full contract snapshot for the new inventory command; generated via pnpm contracts:update and linked to this item."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T21:37:22.472Z"}],"before_hash":"0e2560b20205a3f841cffe33aa427dd4201c0a02ce815d1c72fa2026af41f1d3","after_hash":"a7ee6cec3af9248ab026dbc82722da1774450752df3d3f5217d2ffc044ddcf5c","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"714a6b107b3d9d73660c171e6b86f52ba62bce03a7f1ad670a287f035a19a4a8"} +{"hash_algorithm":"sha256","ts":"2026-09-26T21:39:04.521Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"docs_add","patch":[{"op":"add","path":"/metadata/docs/2","value":{"path":"docs/generated/REFUSAL_CLOSURE_CENSUS.md","scope":"project","note":"Generated command refusal census for package inventory"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T21:39:04.521Z"}],"before_hash":"a7ee6cec3af9248ab026dbc82722da1774450752df3d3f5217d2ffc044ddcf5c","after_hash":"3d0c4b00541d1d0a2450b7d21b4fabd44e6b6da86fd66955744dc20344389a76","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"64d9296f60e3943a083eba3c66ae755f5140750aab7ef2a9ee0313057040ca3f"} +{"hash_algorithm":"sha256","ts":"2026-09-26T21:39:05.278Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/6","value":{"created_at":"2026-09-26T21:39:05.278Z","author":"harness:codex","text":"Static gate required regenerating the agent capability and refusal census after adding package inventory; linked generated document."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T21:39:05.278Z"}],"before_hash":"3d0c4b00541d1d0a2450b7d21b4fabd44e6b6da86fd66955744dc20344389a76","after_hash":"9e88acd270cb19fe77c1f490712a181a377dfe894193cb9bbe91e69880629981","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"f25a990bc7584ea1061909227db4bf8b5a9f39916bedee5fd980eff54b8e2c34"} +{"hash_algorithm":"sha256","ts":"2026-09-26T21:42:50.581Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T21:42:50.581Z"},{"op":"add","path":"/metadata/escape_class","value":"production_defect"},{"op":"add","path":"/metadata/gate_evidence","value":{"disposition":"gate_added","gate_id":"static-extension-inventory-nonactivation","negative_control":"Disposable workspace with import-time write marker: package inventory leaves marker absent while package explore creates it","local_checks":["node scripts/run-tests.mjs test -- tests/integration/extensions/static-extension-inventory.integration.spec.ts","node scripts/run-tests.mjs coverage -- --maxWorkers=2"],"hosted_checks":["Gates (coverage)","codecov/patch"],"owner":"pm-cli maintainers"}}],"before_hash":"9e88acd270cb19fe77c1f490712a181a377dfe894193cb9bbe91e69880629981","after_hash":"d31f2f40580b3e5f81877c8d5e7acb1223ec67b2a1ee0457912204631b2d1e92","item_hash_version":3,"message":"Classify extension read activation escape and record nonactivation gate evidence","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"0a124c520498091beb3e22ee9b3610e26287861281e3cda53993ac6966ec5245"} +{"hash_algorithm":"sha256","ts":"2026-09-26T21:46:50.459Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/11/note","value":"CLI and SDK behavior with nonactivation and read failure controls"},{"op":"replace","path":"/metadata/files/11/path","value":"tests/integration/extensions/static-extension-inventory.integration.spec.ts"},{"op":"replace","path":"/metadata/files/10/note","value":"CLI command contract snapshot for package inventory"},{"op":"replace","path":"/metadata/files/10/path","value":"tests/fixtures/contracts/full.json"},{"op":"replace","path":"/metadata/files/9/note","value":"Public SDK export"},{"op":"replace","path":"/metadata/files/9/path","value":"src/sdk/index.ts"},{"op":"replace","path":"/metadata/files/8/note","value":"Generated SDK error catalog for static inventory receipts"},{"op":"replace","path":"/metadata/files/8/path","value":"src/sdk/generated/generated-error-code-catalog-part-2.ts"},{"op":"replace","path":"/metadata/files/7/path","value":"src/sdk/generated/generated-error-code-catalog-part-1.ts"},{"op":"replace","path":"/metadata/files/6/note","value":"Read-only SDK configured-state primitive"},{"op":"replace","path":"/metadata/files/6/path","value":"src/sdk/extension/static-inventory.ts"},{"op":"replace","path":"/metadata/files/5/note","value":"Static invocation routing"},{"op":"replace","path":"/metadata/files/5/path","value":"src/cli/runtime/selection.ts"},{"op":"replace","path":"/metadata/files/4/note","value":"Package and extension inventory command"},{"op":"replace","path":"/metadata/files/4/path","value":"src/cli/register-setup.ts"},{"op":"replace","path":"/metadata/files/3/note","value":"Bypass extension activation and writable CLI bootstrap for inventory"},{"op":"replace","path":"/metadata/files/3/path","value":"src/cli/main.ts"},{"op":"replace","path":"/metadata/files/2/note","value":"Published additive SDK contract"},{"op":"replace","path":"/metadata/files/2/path","value":"sdk/public-surface.json"},{"op":"replace","path":"/metadata/files/1/note","value":"CLI scope refusal ownership ratchet updated for inventory"},{"op":"replace","path":"/metadata/files/1/path","value":"scripts/release/surface-replication-sets.json"},{"op":"add","path":"/metadata/files/12","value":{"path":"tests/unit/cli/static-extension-inventory.spec.ts","scope":"project","note":"Source CLI registration and routing coverage"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T21:46:50.459Z"}],"before_hash":"d31f2f40580b3e5f81877c8d5e7acb1223ec67b2a1ee0457912204631b2d1e92","after_hash":"b36025ff1486ac26a9689c05676a8ba76a2dfe2bca7e298ee074c515d8ef3cf4","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"09521c73ce5e967977cdac720e898e61f6a6408b19adfa131cefb09f0c09262b"} +{"hash_algorithm":"sha256","ts":"2026-09-26T21:46:51.170Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/7","value":{"created_at":"2026-09-26T21:46:51.170Z","author":"harness:codex","text":"Static quality refusal inventory found the new mutually exclusive inventory scope check. Updated the declared CLI transport refusal count 6 to 7 with rationale; the SDK inventory remains read-only."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T21:46:51.170Z"}],"before_hash":"b36025ff1486ac26a9689c05676a8ba76a2dfe2bca7e298ee074c515d8ef3cf4","after_hash":"6bb884bb2f2b207b15ec5f8ae6f59f58e52863a972324d6714eee87448bdcb3a","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"50b77552989721a8506f7c589e4861fb032580ee0fffa2d6ca436681c297bbba"} +{"hash_algorithm":"sha256","ts":"2026-09-26T21:58:24.724Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/12/note","value":"CLI and SDK behavior with nonactivation and read failure controls"},{"op":"replace","path":"/metadata/files/12/path","value":"tests/integration/extensions/static-extension-inventory.integration.spec.ts"},{"op":"replace","path":"/metadata/files/11/note","value":"CLI command contract snapshot for package inventory"},{"op":"replace","path":"/metadata/files/11/path","value":"tests/fixtures/contracts/full.json"},{"op":"replace","path":"/metadata/files/10/note","value":"Public SDK export"},{"op":"replace","path":"/metadata/files/10/path","value":"src/sdk/index.ts"},{"op":"replace","path":"/metadata/files/9/note","value":"Generated SDK error catalog for static inventory receipts"},{"op":"replace","path":"/metadata/files/9/path","value":"src/sdk/generated/generated-error-code-catalog-part-2.ts"},{"op":"replace","path":"/metadata/files/8/path","value":"src/sdk/generated/generated-error-code-catalog-part-1.ts"},{"op":"replace","path":"/metadata/files/7/note","value":"Read-only SDK configured-state primitive"},{"op":"replace","path":"/metadata/files/7/path","value":"src/sdk/extension/static-inventory.ts"},{"op":"replace","path":"/metadata/files/6/note","value":"Agent-facing package inventory grammar and positional contract"},{"op":"replace","path":"/metadata/files/6/path","value":"src/sdk/cli-contracts/grammar-contracts.ts"},{"op":"add","path":"/metadata/files/13","value":{"path":"tests/unit/cli/static-extension-inventory.spec.ts","scope":"project","note":"Source CLI registration and routing coverage"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T21:58:24.724Z"}],"before_hash":"6bb884bb2f2b207b15ec5f8ae6f59f58e52863a972324d6714eee87448bdcb3a","after_hash":"a357252096d33206e8a314877223dd9cef0b0d3b24c551760d34a0182101244f","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"974b41f1e449f0473a2b298a699d6b2e29d99b14b2e5bd567f7867ce47dc61a4"} +{"hash_algorithm":"sha256","ts":"2026-09-26T21:58:25.472Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/8","value":{"created_at":"2026-09-26T21:58:25.472Z","author":"harness:codex","text":"Command grammar gate required noun/verb destination census and optional name positional signatures for package inventory and packages inventory. Declared both; the grammar gate now passes with 231 observed and declared paths."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T21:58:25.472Z"}],"before_hash":"a357252096d33206e8a314877223dd9cef0b0d3b24c551760d34a0182101244f","after_hash":"911d89938c3c639e1467beb30c360ac1544664d2b17d824b91ffaf0be4eba05c","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"36331f8157ea67cb112499e7d9d28a34dc757f672e12dcb117aeb3bdf9c9d633"} diff --git a/.agents/pm/history/pm-t05d8d.jsonl b/.agents/pm/history/pm-t05d8d.jsonl new file mode 100644 index 000000000..7186b5d07 --- /dev/null +++ b/.agents/pm/history/pm-t05d8d.jsonl @@ -0,0 +1,8 @@ +{"hash_algorithm":"sha256","ts":"2026-09-26T20:34:16.625Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","lineage:pm-lhhnx9","lineage:pm-grst","lineage:pm-m9jc"]},"topic":{"value":"pm-lhhnx9","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","lineage:pm-lhhnx9","lineage:pm-grst","lineage:pm-m9jc"]}},"op":"create","patch":[{"op":"add","path":"/metadata/id","value":"pm-t05d8d"},{"op":"add","path":"/metadata/title","value":"Allow self-isolating linked package tests without inherited PM_PATH"},{"op":"add","path":"/metadata/description","value":"A non-PM linked package test that initializes disposable SDK workspaces inherits the runner sandbox PM_PATH, so explicit cwd still resolves to a shared tracker and later fixtures fail. Provide a safe explicit context mode that omits inherited PM_PATH for self-isolating commands while keeping source workspace trust and default sandbox policy."},{"op":"add","path":"/metadata/type","value":"Issue"},{"op":"add","path":"/metadata/status","value":"open"},{"op":"add","path":"/metadata/priority","value":1},{"op":"add","path":"/metadata/tags","value":["area:sdk","area:test","context-management","gh-1318","packages"]},{"op":"add","path":"/metadata/created_at","value":"2026-09-26T20:34:16.625Z"},{"op":"add","path":"/metadata/updated_at","value":"2026-09-26T20:34:16.625Z"},{"op":"add","path":"/metadata/author","value":"harness:codex"},{"op":"add","path":"/metadata/acceptance_criteria","value":"An explicit linked-test context mode lets non-PM package commands create independent disposable SDK workspaces; default linked tests retain isolated PM_PATH; PM commands cannot bypass tracker isolation; source workspace trust remains enforced; CLI/SDK metadata and execution receipts expose requested and effective mode; focused, full coverage, and temporary package acceptance pass."},{"op":"add","path":"/metadata/parent","value":"pm-ugqx"},{"op":"add","path":"/metadata/dependencies","value":[{"id":"pm-954h0o","kind":"verifies","created_at":"2026-09-26T20:34:16.625Z","author":"harness:codex","source_kind":"cli:create:dep","author_source":"detected"},{"id":"pm-alhqbz","kind":"verifies","created_at":"2026-09-26T20:34:16.625Z","author":"harness:codex","source_kind":"cli:create:dep","author_source":"detected"},{"id":"pm-e97jyf","kind":"discovered_from","created_at":"2026-09-26T20:34:16.625Z","author":"harness:codex","source_kind":"cli:create:dep","author_source":"detected"}]},{"op":"add","path":"/metadata/comments","value":[{"created_at":"2026-09-26T20:34:16.625Z","author":"harness:codex","text":"Duplicate check 2026-09-26: all-status PM searches for GH-1318, inherited PM_PATH, self-isolating linked tests, and context mode found closed pm-e97jyf, pm-alhqbz, pm-954h0o, and pm-6pij. Those delivered source/tracker parity and nested-write protection but do not provide a no-inherited-PM_PATH mode. GitHub report: https://github.com/unbraind/pm-cli/issues/1318."}]}],"before_hash":"3cc22dff72be7b14824654a7a64ea62b04799939b2fee54c1b5f52ca60bf6df0","after_hash":"61f149ea687e6c6708b7bd2a25f688b6dfa21b8fb2c0432aef08d8cacdaaff82","item_hash_version":3,"message":"","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"b06a76db63a97145926816be9b1e1db6b46ddb9eeebd3ff74c1c6f6c10ff6918"} +{"hash_algorithm":"sha256","ts":"2026-09-26T20:35:18.342Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","lineage:pm-lhhnx9","lineage:pm-grst","lineage:pm-m9jc"]},"topic":{"value":"pm-lhhnx9","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","lineage:pm-lhhnx9","lineage:pm-grst","lineage:pm-m9jc"]}},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T20:35:18.342Z"},{"op":"add","path":"/metadata/assignee","value":"harness:codex"},{"op":"add","path":"/metadata/claim_principal","value":"harness:codex#1988a47670c5b401cd591f8d"}],"before_hash":"61f149ea687e6c6708b7bd2a25f688b6dfa21b8fb2c0432aef08d8cacdaaff82","after_hash":"c54a64632bc5fe9b0e4cb6e6629fe35a7faa1c0a54cf602a817ae8abb4c977af","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"e53e2782d7cf37ec7bc4c2b4f9d848a1fdad6f1d5718207a03b0ad6e5f64d0a1"} +{"hash_algorithm":"sha256","ts":"2026-09-26T20:35:19.292Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-lhhnx9+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T20:35:19.292Z"},{"op":"replace","path":"/metadata/status","value":"in_progress"}],"before_hash":"c54a64632bc5fe9b0e4cb6e6629fe35a7faa1c0a54cf602a817ae8abb4c977af","after_hash":"9e1d9ef25240c1165dc2eb7db3f8bb7dabca3d65279c5949626aa2ebfb85b3c6","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"aef0e8b584f9ea92f6dcc49fd28d6392c8aae07a3839f49e39e093eeb7e0984f"} +{"hash_algorithm":"sha256","ts":"2026-09-26T20:39:23.664Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-lhhnx9+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"note_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T20:39:23.664Z"},{"op":"add","path":"/metadata/notes","value":[{"created_at":"2026-09-26T20:39:23.664Z","author":"harness:codex","text":"Investigation 2026-09-26: linked-test runner currently injects sandbox PM_PATH for every child; parser, item serialization, import normalization, SDK/CLI help, and execution receipts all model schema|tracker|auto. A no-inherited-PM_PATH mode must refuse source working-directory execution: nested pm commands would otherwise rediscover the real source tracker. Safer design is an explicit mode paired with a disposable snapshot or isolated working directory; snapshot retains package files and binds .agents/pm to a sandbox tracker while explicit SDK init cwd can select a new temp tracker. Preserve protected env keys and clone trust checks. No source implementation or acceptance proof yet."}]}],"before_hash":"9e1d9ef25240c1165dc2eb7db3f8bb7dabca3d65279c5949626aa2ebfb85b3c6","after_hash":"f8dbe1196b5c4c9105bace5c062e25cdbb1f32e3b95067e3b159d09fbf4e3a29","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"de1688d15b020dc489e5b1975b180e8a87ee2571c8a99a65eb2bca7926be8e88"} +{"hash_algorithm":"sha256","ts":"2026-09-26T20:39:25.507Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-lhhnx9+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T20:39:25.507Z"}],"before_hash":"f8dbe1196b5c4c9105bace5c062e25cdbb1f32e3b95067e3b159d09fbf4e3a29","after_hash":"d8eeaab575ece66f2dae09999dbf3596448cb41cf3a6268c5d3101bc0c72bc12","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"65602a7fff291f439774eb70d4dc6d9dbe060e7a10b6167d3df2c723124ba716"} +{"hash_algorithm":"sha256","ts":"2026-09-26T20:39:34.483Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"release-operator","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]},"topic":{"value":"pm-lhhnx9","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T20:39:34.483Z"},{"op":"replace","path":"/metadata/status","value":"open"}],"before_hash":"d8eeaab575ece66f2dae09999dbf3596448cb41cf3a6268c5d3101bc0c72bc12","after_hash":"f722b292e2b1ebb98beb9096fd7917512ada80124b068e0499590d81cbd34d06","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"fc7c39e2a19410b15a936ae08df8720e9d7235bda24e3e46a2f74e3c4b7cf567"} +{"hash_algorithm":"sha256","ts":"2026-09-26T20:39:34.589Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"release-operator","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]},"topic":{"value":"pm-lhhnx9","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]}},"op":"release","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T20:39:34.589Z"}],"before_hash":"f722b292e2b1ebb98beb9096fd7917512ada80124b068e0499590d81cbd34d06","after_hash":"9aa0e42e1a39322ef1231943c6116df4509d9f32e6744eadb57c99ae13bcaf91","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"26a328f7d82603c54a571a9f7b76e4a7d400110e566504fc028c7f547a7d78f8"} +{"hash_algorithm":"sha256","ts":"2026-09-26T22:18:49.678Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"update","patch":[{"op":"add","path":"/metadata/dependencies/3","value":{"id":"pm-6pij","kind":"verifies","created_at":"2026-09-26T22:18:49.388Z","author":"harness:codex","source_kind":"cli:update:dep","author_source":"detected"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T22:18:49.678Z"}],"before_hash":"9aa0e42e1a39322ef1231943c6116df4509d9f32e6744eadb57c99ae13bcaf91","after_hash":"10862d51e188543f721c1ad49fe4ddc5c9f5f7368a2931447e3a9f88df024a5c","item_hash_version":3,"message":"Link historical linked-test dataset parity guardrail cited by duplicate check","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"14a1ceaa05d3209122af3705e20920fe2afc1e6b25ac2b537e7a5ba05d448db4"} diff --git a/.agents/pm/issues/pm-lhhnx9.toon b/.agents/pm/issues/pm-lhhnx9.toon index ddd5dea08..e2780a1df 100644 --- a/.agents/pm/issues/pm-lhhnx9.toon +++ b/.agents/pm/issues/pm-lhhnx9.toon @@ -2,18 +2,75 @@ id: pm-lhhnx9 title: Expose static extension inventory for hosted and agent reads description: "A default extension explore read activates installed extension code, so hosted GET routes need an explicit static configured-state inventory through the public SDK and CLI with honest completeness and error receipts." type: Issue -status: open +status: closed priority: 1 tags[5]: agent-ux,"area:extensions","area:sdk",gh-1316,security created_at: "2026-09-26T16:19:02.178Z" -updated_at: "2026-09-26T16:33:43.788Z" +updated_at: "2026-09-26T21:58:25.472Z" +closed_at: "2026-09-26T21:26:37.459Z" +completed_at: "2026-09-26T21:26:37.459Z" author: "harness:codex" acceptance_criteria: "Static CLI and SDK inventory never import or activate extension code, adopt state, check updates, or write workspace files; installed, absent, inactive, malformed, and global/project cases are distinct; completeness and errors are explicit; docs distinguish configured state from runtime probes." parent: pm-grst +resolution: "Added public configured-state inventory, safe pre-bootstrap CLI routing, explicit completeness/error receipts, SDK surface, docs, and source/dist tests." +expected_result: "Hosted and agent reads can inspect saved extension state without executing package code or mutating the tracker, with honest project/global and error reporting." +actual_result: "Disposable workspace inventory preserved tracker files and did not trigger an import-time marker; runtime explore did. Full 9,367-test suite passed with exact 100/100/100/100 coverage and static gates passed." dependencies[3]{id,kind,created_at,author,source_kind,author_source}: pm-5mua,verifies,"2026-09-26T16:19:02.178Z","harness:codex","cli:create:dep",detected pm-x6jf,discovered_from,"2026-09-26T16:19:02.178Z","harness:codex","cli:create:dep",detected pm-l4c8,verifies,"2026-09-26T16:33:43.474Z","harness:codex","cli:update:dep",detected -comments[1]{created_at,author,text}: +comments[9]{created_at,author,text}: "2026-09-26T16:19:02.178Z","harness:codex","Duplicate check 2026-09-26: all-status searches for extension state inventory, side-effect-free extension, and hosted extension read found closed pm-grst managed state, pm-x6jf public lifecycle SDK, pm-l4c8 runtime describe, and pm-5mua runtime command-path diagnostics. Their shipped runtime probes do not provide the requested static configured-state receipt. GitHub issue: https://github.com/unbraind/pm-cli/issues/1316." + "2026-09-26T21:26:34.351Z","harness:codex","Implementation evidence 2026-09-26: public SDK inspectStaticExtensionInventory and package/extension inventory CLI read settings, managed metadata, and manifests without loading entrypoints or running hooks. Receipts distinguish installed, inactive, absent, malformed, project/global, and incomplete sources. A disposable initialized workspace with an import-time write marker returned complete inventory without marker or tracker changes; explicit runtime explore afterward created the marker, proving the negative control. Source CLI and dist integration tests cover the behavior." + "2026-09-26T21:26:35.294Z","harness:codex","Verification evidence 2026-09-26: full sandboxed suite passed 748 files / 9,367 tests with exact statements 65,668/65,668, branches 50,209/50,209, functions 13,594/13,594, lines 62,715/62,715. Focused source and dist tests passed 10/10 after final help wording. pnpm build, typecheck, ESLint, jscpd, docs/skills, SDK surface, and flag-help gate passed; pm health summary ok with zero warnings. pm-changelog installed version 2026.9.25 matches npm latest." + "2026-09-26T21:27:25.738Z","harness:codex","Changelog evidence: refreshed release tags, installed npm-latest pm-changelog 2026.9.25, regenerated CHANGELOG.md from 2,510 closed items, and its canonical --check rerun reported unchanged." + "2026-09-26T21:33:49.678Z","harness:codex","Post-close preflight: repository static gate required regenerating the SDK error-code catalog for seven new static inventory receipt codes. Linked both generated partitions. Latest pm-changelog installation also refreshed only its managed metadata timestamp; linked that PM-managed artifact. The implementation and close outcome are unchanged." + "2026-09-26T21:37:22.471Z","harness:codex","Static gate also required refreshing the CLI full contract snapshot for the new inventory command; generated via pnpm contracts:update and linked to this item." + "2026-09-26T21:39:05.278Z","harness:codex",Static gate required regenerating the agent capability and refusal census after adding package inventory; linked generated document. + "2026-09-26T21:46:51.170Z","harness:codex",Static quality refusal inventory found the new mutually exclusive inventory scope check. Updated the declared CLI transport refusal count 6 to 7 with rationale; the SDK inventory remains read-only. + "2026-09-26T21:58:25.472Z","harness:codex",Command grammar gate required noun/verb destination census and optional name positional signatures for package inventory and packages inventory. Declared both; the grammar gate now passes with 231 observed and declared paths. +learnings[1]{created_at,author,text}: + "2026-09-26T21:26:36.371Z","harness:codex","A host GET route must use the static inventory primitive; runtime explore/manage/doctor/describe can import installed extension code. Read settings directly for inventory because the general settings reader may scaffold schema files. Keep configured enablement and runtime activation as separate facts, and fail incomplete reads explicitly." +files[14]{path,scope,note}: + .agents/pm/extensions/.managed-extensions.json,project,Installer metadata timestamp from latest pm-changelog verification + scripts/release/surface-replication-sets.json,project,CLI scope refusal ownership ratchet updated for inventory + sdk/public-surface.json,project,Published additive SDK contract + src/cli/main.ts,project,Bypass extension activation and writable CLI bootstrap for inventory + src/cli/register-setup.ts,project,Package and extension inventory command + src/cli/runtime/selection.ts,project,Static invocation routing + src/sdk/cli-contracts/grammar-contracts.ts,project,Agent-facing package inventory grammar and positional contract + src/sdk/extension/static-inventory.ts,project,Read-only SDK configured-state primitive + src/sdk/generated/generated-error-code-catalog-part-1.ts,project,Generated SDK error catalog for static inventory receipts + src/sdk/generated/generated-error-code-catalog-part-2.ts,project,Generated SDK error catalog for static inventory receipts + src/sdk/index.ts,project,Public SDK export + tests/fixtures/contracts/full.json,project,CLI command contract snapshot for package inventory + tests/integration/extensions/static-extension-inventory.integration.spec.ts,project,CLI and SDK behavior with nonactivation and read failure controls + tests/unit/cli/static-extension-inventory.spec.ts,project,Source CLI registration and routing coverage +tests[1]{command,path,scope,timeout_seconds,provenance{author,created_at,source_kind,source_ref}}: + node scripts/run-tests.mjs test -- tests/integration/extensions/static-extension-inventory.integration.spec.ts,tests/integration/extensions/static-extension-inventory.integration.spec.ts,project,240,"harness:codex","2026-09-26T20:44:29.754Z",local_mutation,feat/static-extension-inventory-and-agent-safety +test_runs[1]: + - run_id: test-local-muiuyx33-rppz27 + kind: test + status: passed + started_at: "2026-09-26T20:44:34.836Z" + finished_at: "2026-09-26T20:44:49.023Z" + recorded_at: "2026-09-26T20:44:49.023Z" + passed: 1 + failed: 0 + skipped: 0 + executions[1]{command,requested_pm_context_mode,pm_context_mode,workspace_context_mode,trust_reason}: + node scripts/run-tests.mjs test -- tests/integration/extensions/static-extension-inventory.integration.spec.ts,schema,schema,source,local_source_ref +docs[3]{path,scope,note}: + CHANGELOG.md,project,Generated pm-changelog 2026.9.25 entry for closed static inventory item + docs/EXTENSION_LIFECYCLE.md,project,Configured-state inventory and runtime-probe guidance + docs/generated/REFUSAL_CLOSURE_CENSUS.md,project,Generated command refusal census for package inventory +close_reason: "Delivered static SDK and CLI extension inventory with no activation or workspace writes, documented semantics, manual negative control, and exact coverage." +escape_class: production_defect +gate_evidence: + disposition: gate_added + gate_id: static-extension-inventory-nonactivation + negative_control: "Disposable workspace with import-time write marker: package inventory leaves marker absent while package explore creates it" + local_checks[2]: node scripts/run-tests.mjs test -- tests/integration/extensions/static-extension-inventory.integration.spec.ts,node scripts/run-tests.mjs coverage -- --maxWorkers=2 + hosted_checks[2]: Gates (coverage),codecov/patch + owner: pm-cli maintainers body: "" diff --git a/.agents/pm/issues/pm-t05d8d.toon b/.agents/pm/issues/pm-t05d8d.toon new file mode 100644 index 000000000..00c1b6fc2 --- /dev/null +++ b/.agents/pm/issues/pm-t05d8d.toon @@ -0,0 +1,22 @@ +id: pm-t05d8d +title: Allow self-isolating linked package tests without inherited PM_PATH +description: "A non-PM linked package test that initializes disposable SDK workspaces inherits the runner sandbox PM_PATH, so explicit cwd still resolves to a shared tracker and later fixtures fail. Provide a safe explicit context mode that omits inherited PM_PATH for self-isolating commands while keeping source workspace trust and default sandbox policy." +type: Issue +status: open +priority: 1 +tags[5]: "area:sdk","area:test",context-management,gh-1318,packages +created_at: "2026-09-26T20:34:16.625Z" +updated_at: "2026-09-26T22:18:49.678Z" +author: "harness:codex" +acceptance_criteria: "An explicit linked-test context mode lets non-PM package commands create independent disposable SDK workspaces; default linked tests retain isolated PM_PATH; PM commands cannot bypass tracker isolation; source workspace trust remains enforced; CLI/SDK metadata and execution receipts expose requested and effective mode; focused, full coverage, and temporary package acceptance pass." +parent: pm-ugqx +dependencies[4]{id,kind,created_at,author,source_kind,author_source}: + pm-954h0o,verifies,"2026-09-26T20:34:16.625Z","harness:codex","cli:create:dep",detected + pm-alhqbz,verifies,"2026-09-26T20:34:16.625Z","harness:codex","cli:create:dep",detected + pm-e97jyf,discovered_from,"2026-09-26T20:34:16.625Z","harness:codex","cli:create:dep",detected + pm-6pij,verifies,"2026-09-26T22:18:49.388Z","harness:codex","cli:update:dep",detected +comments[1]{created_at,author,text}: + "2026-09-26T20:34:16.625Z","harness:codex","Duplicate check 2026-09-26: all-status PM searches for GH-1318, inherited PM_PATH, self-isolating linked tests, and context mode found closed pm-e97jyf, pm-alhqbz, pm-954h0o, and pm-6pij. Those delivered source/tracker parity and nested-write protection but do not provide a no-inherited-PM_PATH mode. GitHub report: https://github.com/unbraind/pm-cli/issues/1318." +notes[1]{created_at,author,text}: + "2026-09-26T20:39:23.664Z","harness:codex","Investigation 2026-09-26: linked-test runner currently injects sandbox PM_PATH for every child; parser, item serialization, import normalization, SDK/CLI help, and execution receipts all model schema|tracker|auto. A no-inherited-PM_PATH mode must refuse source working-directory execution: nested pm commands would otherwise rediscover the real source tracker. Safer design is an explicit mode paired with a disposable snapshot or isolated working directory; snapshot retains package files and binds .agents/pm to a sandbox tracker while explicit SDK init cwd can select a new temp tracker. Preserve protected env keys and clone trust checks. No source implementation or acceptance proof yet." +body: "" diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e0561008..8702a2155 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ - SDK complete-list receipt falsely reports legacy aliases for canonical helper ([pm-vf9iaf](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/issues/pm-vf9iaf.toon)) +### Security + +- Expose static extension inventory for hosted and agent reads ([pm-lhhnx9](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/issues/pm-lhhnx9.toon)) + ### Other - Consolidate workspace customization discovery and commands under workspace with permanent compatibility aliases ([pm-npr3](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/tasks/pm-npr3.toon)) diff --git a/docs/EXTENSION_LIFECYCLE.md b/docs/EXTENSION_LIFECYCLE.md index bbc769e12..be5306a15 100644 --- a/docs/EXTENSION_LIFECYCLE.md +++ b/docs/EXTENSION_LIFECYCLE.md @@ -3,6 +3,24 @@ Tracked by [pm-ig5cfe](../.agents/pm/issues/pm-ig5cfe.toon), [pm-495lkc](../.agents/pm/issues/pm-495lkc.toon), and [pm-miy5k6](../.agents/pm/issues/pm-miy5k6.toon). +Static inventory is tracked by [pm-lhhnx9](../.agents/pm/issues/pm-lhhnx9.toon). + +## Read-only configured inventory + +Use `pm package inventory --project --json` (or `--global`) for a host GET route +or agent read. Pass a name to receive an explicit `absent` row. The public SDK +equivalent is `inspectStaticExtensionInventory({ pmRoot, scope, name })` from +`@unbrained/pm-cli/sdk`. Both read settings, managed metadata, and manifests +without importing package entrypoints, running hooks, checking for updates, +adopting installs, or writing workspace files. An absent managed-state file is +reported separately from an invalid one. `complete: false` and `errors` mean +the list must not be treated as authoritative; the CLI also exits nonzero. + +`configured_enabled` is the effective saved enablement setting, while +`runtime_active` is always `null`. The static read does not prove that a package +will activate successfully. `pm package explore`, `manage`, `doctor`, and +`describe` probe runtime state and may execute installed package code. Run +those commands only when an activation probe is intended. ## Explicit Install-Source Identity diff --git a/docs/generated/REFUSAL_CLOSURE_CENSUS.md b/docs/generated/REFUSAL_CLOSURE_CENSUS.md index 44ae02d05..41646fb17 100644 --- a/docs/generated/REFUSAL_CLOSURE_CENSUS.md +++ b/docs/generated/REFUSAL_CLOSURE_CENSUS.md @@ -4,12 +4,12 @@ Tracker: `pm-f05lsg`. Every catalog code is listed. An `uncovered` row is an explicit closure obligation, never an omission or implied approval. -- Catalog error codes: 378 +- Catalog error codes: 384 - Executable error codes: 19 - Executable-code ratchet floor: 18 - Required executable canonical codes: `bulk_ids_input_empty`, `bulk_ids_input_missing_path`, `bulk_ids_input_unreadable`, `invalid_argument_value`, `manifest_unknown_key`, `missing_lifecycle_target`, `missing_required_argument`, `no_version_bounds_declared`, `projection_options_mutually_exclusive`, `tracker_not_initialized`, `tracker_root_missing`, `tracker_root_not_directory`, `tracker_root_unreadable`, `unknown_context_intent`, `unknown_field_projection`, `unknown_option`, `unknown_subcommand` -- Uncovered error codes: 359 -- Coverage fraction: 0.050265 +- Uncovered error codes: 365 +- Coverage fraction: 0.049479 - Closed-domain probes: 19 - Grammar probes: 117 @@ -96,6 +96,7 @@ Every catalog code is listed. An `uncovered` row is an explicit closure obligati | `extension_mutation_guard_invalid_denial` | `extension_mutation_guard_invalid_denial` | uncovered | none | 0 | | `extension_mutation_guard_timed_out` | `extension_mutation_guard_timed_out` | uncovered | none | 0 | | `extension_update_health_partial_coverage` | `extension_update_health_partial_coverage` | uncovered | none | 0 | +| `extensions_unreadable` | `extensions_unreadable` | uncovered | none | 0 | | `field_duplicate` | `field_duplicate` | uncovered | none | 0 | | `field_invalid` | `field_invalid` | uncovered | none | 0 | | `field_mcp_input_collision` | `field_mcp_input_collision` | uncovered | none | 0 | @@ -189,7 +190,10 @@ Every catalog code is listed. An `uncovered` row is an explicit closure obligati | `locks_stale_count` | `locks_stale_count` | uncovered | none | 0 | | `locks_unreadable` | `locks_unreadable` | uncovered | none | 0 | | `malformed_plan_step_evidence` | `malformed_plan_step_evidence` | uncovered | none | 0 | +| `managed_state_invalid` | `managed_state_invalid` | uncovered | none | 0 | +| `managed_state_unreadable` | `managed_state_unreadable` | uncovered | none | 0 | | `manifest_capabilities_absent` | `manifest_capabilities_absent` | uncovered | none | 0 | +| `manifest_invalid` | `manifest_invalid` | uncovered | none | 0 | | `manifest_unknown_key` | `manifest_unknown_key` | executable | owned_state | 1 | | `mcp_annotation_file_unavailable` | `mcp_annotation_file_unavailable` | uncovered | none | 0 | | `mcp_stdin_unavailable` | `mcp_stdin_unavailable` | uncovered | none | 0 | @@ -274,10 +278,12 @@ Every catalog code is listed. An `uncovered` row is an explicit closure obligati | `retry_failed` | `retry_failed` | uncovered | none | 0 | | `schema_migration_input_required` | `schema_migration_input_required` | uncovered | none | 0 | | `semantic_spelling_collision` | `semantic_spelling_collision` | uncovered | none | 0 | +| `settings_invalid` | `settings_invalid` | uncovered | none | 0 | | `settings_read_fs_error` | `settings_read_fs_error` | uncovered | none | 0 | | `settings_read_invalid_json` | `settings_read_invalid_json` | uncovered | none | 0 | | `settings_read_invalid_schema` | `settings_read_invalid_schema` | uncovered | none | 0 | | `settings_read_merge_failed` | `settings_read_merge_failed` | uncovered | none | 0 | +| `settings_unreadable` | `settings_unreadable` | uncovered | none | 0 | | `stale_budget` | `stale_budget` | uncovered | none | 0 | | `stale_destination` | `stale_destination` | uncovered | none | 0 | | `stale_observed_signature` | `stale_observed_signature` | uncovered | none | 0 | diff --git a/scripts/release/surface-replication-sets.json b/scripts/release/surface-replication-sets.json index e9d80a998..09f4438dc 100644 --- a/scripts/release/surface-replication-sets.json +++ b/scripts/release/surface-replication-sets.json @@ -639,11 +639,11 @@ }, { "path": "src/cli/register-setup.ts", - "expected_count": 6, + "expected_count": 7, "rule_ownership": "all_occurrences", "owner": "pm-0xmajx", "disposition": "transport_validation", - "reason": "Installation and shell setup arguments are not SDK domain mutations." + "reason": "Installation and shell setup arguments, including mutually exclusive static inventory scope flags, are CLI transport checks before SDK reads or mutations." }, { "path": "src/cli/register-structured-mutation.ts", diff --git a/sdk/public-surface.json b/sdk/public-surface.json index f8133bc3b..edeb0fd9a 100644 --- a/sdk/public-surface.json +++ b/sdk/public-surface.json @@ -9408,6 +9408,12 @@ "name": "InspectStaleInProgressOptions", "signature": "export interface InspectStaleInProgressOptions { in_progress_status?: string; threshold_hours: number; now?: Date; last_history_activity?: (item: ItemMetadata) => string | undefined; }" }, + { + "classification": "advanced_export", + "kind": "function", + "name": "inspectStaticExtensionInventory", + "signature": "(options: { pmRoot: string; scope?: \"global\" | \"project\"; name?: string; cwd?: string; }) => Promise | calls: (options: { pmRoot: string; scope?: \"project\" | \"global\"; name?: string; cwd?: string; }) => Promise" + }, { "classification": "supported", "kind": "function", @@ -18954,6 +18960,24 @@ "name": "startTelemetryCommand", "signature": "(context: TelemetryCommandContext) => Promise | calls: (context: TelemetryCommandContext) => Promise" }, + { + "classification": "advanced_export", + "kind": "interface", + "name": "StaticExtensionInventoryEntry", + "signature": "export interface StaticExtensionInventoryEntry { name: string; directory: string | null; scope: \"project\" | \"global\"; installed: boolean; status: \"installed\" | \"inactive\" | \"absent\" | \"malformed_manifest\"; configured_enabled: boolean | null; managed: boolean | null; runtime_active: null; version?: string; }" + }, + { + "classification": "advanced_export", + "kind": "interface", + "name": "StaticExtensionInventoryError", + "signature": "export interface StaticExtensionInventoryError { code: \"settings_invalid\" | \"settings_unreadable\" | \"managed_state_invalid\" | \"managed_state_unreadable\" | \"extensions_unreadable\" | \"manifest_invalid\" | \"manifest_unreadable\"; path: string; }" + }, + { + "classification": "advanced_export", + "kind": "interface", + "name": "StaticExtensionInventoryResult", + "signature": "export interface StaticExtensionInventoryResult { scope: \"project\" | \"global\"; complete: boolean; settings_status: \"ok\" | \"absent\" | \"invalid\" | \"unreadable\"; managed_state_status: \"ok\" | \"absent\" | \"invalid\" | \"unreadable\"; extensions: StaticExtensionInventoryEntry[]; errors: StaticExtensionInventoryError[]; }" + }, { "classification": "advanced_export", "kind": "function", @@ -35216,6 +35240,7 @@ "extension_mutation_guard_invalid_denial", "extension_mutation_guard_timed_out", "extension_update_health_partial_coverage", + "extensions_unreadable", "field_duplicate", "field_invalid", "field_mcp_input_collision", @@ -35309,7 +35334,10 @@ "locks_stale_count", "locks_unreadable", "malformed_plan_step_evidence", + "managed_state_invalid", + "managed_state_unreadable", "manifest_capabilities_absent", + "manifest_invalid", "manifest_unknown_key", "mcp_annotation_file_unavailable", "mcp_stdin_unavailable", @@ -35394,10 +35422,12 @@ "retry_failed", "schema_migration_input_required", "semantic_spelling_collision", + "settings_invalid", "settings_read_fs_error", "settings_read_invalid_json", "settings_read_invalid_schema", "settings_read_merge_failed", + "settings_unreadable", "stale_budget", "stale_destination", "stale_observed_signature", diff --git a/src/cli/main.ts b/src/cli/main.ts index f42ec4071..020dd2a31 100644 --- a/src/cli/main.ts +++ b/src/cli/main.ts @@ -175,7 +175,7 @@ import type { RuntimeExtensionActivationProbe } from "./runtime/activation.js"; import { activationCommandMatchesProbe,buildBootstrapActivationProbe,buildRuntimeExtensionActivationScope,buildRuntimeExtensionFilterForProbe,collectActivationCommandCandidates,collectLeadingCommandArgs,collectParsedActivationCommandArgs,commandPathNeedsSearchExtensions,commandPathNeedsTemplateExtensions,discoveryNeedsActivationForProbe,extensionActivationCommands,extensionCapabilities,extensionNeedsActivationForProbe,extensionProvidesTemplatesRuntime,hasAnyCapability,hasGlobalExtensionContributions,matchesStaticExtensionCommand,probeUsesAnyFlag,resolveStaticExtensionActivationDecision } from "./runtime/activation.js"; import { collectExtensionFlagDefinitionsForCommand,collectExtensionFlagDefinitionsForInvocation,dynamicCommandArguments,extractCommandScopedOptions,forwardReadOutputIncludeModes,isImporterOrExporterCommandPath,recordCliReadOutputInvocationProvenance,validateDynamicExtensionCommandArgs,validateDynamicExtensionCommandInvocation } from "./runtime/invocation-options.js"; import type { CoreCommandRegistrationSelection } from "./runtime/selection.js"; -import { LIST_QUERY_COMMAND_NAMES,enforceExplicitRetryForFlagTypos,invocationRequestsVersion,resolveCoreCommandRegistrationSelection,shouldAttachRichHelpTextForInvocation,shouldRegisterDynamicExtensionPaths,shouldRegisterRuntimeSchemaFlags } from "./runtime/selection.js"; +import { LIST_QUERY_COMMAND_NAMES,enforceExplicitRetryForFlagTypos,invocationRequestsVersion,isStaticExtensionInventoryInvocation,resolveCoreCommandRegistrationSelection,shouldAttachRichHelpTextForInvocation,shouldRegisterDynamicExtensionPaths,shouldRegisterRuntimeSchemaFlags } from "./runtime/selection.js"; import { buildPostActionTelemetryOutcome,inferPostActionErrorCode,inferPostActionFailureMessage,normalizeTelemetryCommandResolution,normalizeTelemetryErrorCategory,normalizeTelemetryResolutionStage,readRecordBoolean,readRecordNumber,readRecordString } from "./runtime/telemetry-outcome.js"; const PM_PACKAGE_ROOT_ENV = "PM_CLI_PACKAGE_ROOT"; @@ -1134,6 +1134,9 @@ function wrapProgramActionsForExtensionHandlers(rootProgram: Command): void { clearResolvedGlobalOptions(actionCommand); let globalOptions = getGlobalOptions(actionCommand); const commandPath = resolvePmCommandOperation(getCommandPath(actionCommand)); + if (["package inventory", "packages inventory", "extension inventory"].includes(commandPath)) { + return await originalAction.apply(this, actionArgs); + } const pmRoot = resolvePmRoot(process.cwd(), globalOptions.path); let commandArgs = actionCommand.args.map(String); const activeRegistrations = getActiveExtensionRegistrations(); @@ -1391,7 +1394,8 @@ let program = createPmCliProgram(CLI_VERSION); /* c8 ignore start */ /** Bind output validation, extension policy, mutation guards, and observability to the selected semantic command. */ -function attachProgramLifecycleHooks(rootProgram: Command): void { +function attachProgramLifecycleHooks(rootProgram: Command, invocationArgv?: string[]): void { + if (invocationArgv && isStaticExtensionInventoryInvocation(invocationArgv)) return; rootProgram.hook("preAction", async (_thisCommand, actionCommand) => { activeExtensionHookContext = null; activeTelemetryCommandContext = null; @@ -1679,7 +1683,7 @@ function createTelemetryCommandErrorEmitter(params: { invocationArgv: string[]; } async function prepareExtensionServicesForRunPmCliError(params: { invocationArgv: string[]; bootstrapGlobal: GlobalOptions; bootstrapPmRoot: string }): Promise { - if (params.bootstrapGlobal.noExtensions) { + if (params.bootstrapGlobal.noExtensions || isStaticExtensionInventoryInvocation(params.invocationArgv)) { return; } const bootstrapProbe = buildBootstrapActivationProbe(params.invocationArgv); @@ -2006,7 +2010,7 @@ function assertRequestedNamespaceAvailable(program: Command, invocationArgv: str /** Dispatch one fresh CLI invocation with deterministic process state and tracker-scoped attribution. */ async function runPmCliInReproducibleContext(rawArgv: string[]): Promise { program = createPmCliProgram(CLI_VERSION); - attachProgramLifecycleHooks(program); + attachProgramLifecycleHooks(program, rawArgv); // The runtime-extension snapshot caches dedupe discovery work within a // single invocation only. Reset them on entry so long-lived embeddings // (in-process test runners, future SDK hosts) observe the same fresh @@ -2069,6 +2073,10 @@ async function runPmCliInReproducibleContext(rawArgv: string[]): Promise { program.outputHelp(); return; } + if (isStaticExtensionInventoryInvocation(invocationArgv)) { + await program.parseAsync(invocationProcessArgv); + return; + } const invocationPmRoot = resolvePmRoot(process.cwd(), bootstrapGlobal.path); const invocationSettings = await readSettings(invocationPmRoot); const intentSnapshot = await loadContextIntentSnapshotForInvocation( diff --git a/src/cli/register-setup.ts b/src/cli/register-setup.ts index d07ac7205..a6e560ca5 100644 --- a/src/cli/register-setup.ts +++ b/src/cli/register-setup.ts @@ -36,6 +36,7 @@ import { import { runConfig } from "./commands/workspace/config.js"; import { runInit, summarizeInitResult } from "./commands/workspace/init.js"; import { runUpgrade } from "./commands/workspace/upgrade.js"; +import { inspectStaticExtensionInventory } from "../sdk/extension/static-inventory.js"; type ExtensionSubcommandAction = | "init" @@ -630,37 +631,37 @@ function registerLifecycleCommand( collect, ) .option("--install", `Install a ${noun} source`) - .option("--dry-run", "Resolve install sources and estimate copying without destination writes or activation") + .option("--dry-run", "Preview install without writes or activation") .option("--uninstall", `Uninstall an installed ${noun}`) .option("--explore", `List discovered ${plural} in selected scope`) .option("--list", "Alias for --explore") - .option("--manage", `List managed ${plural} with update-check metadata`) + .option("--manage", `List managed ${plural} and updates`) .option( "--describe", - `Map every surface a loaded ${noun} registers (optionally one by name)`, + `Show surfaces registered by a loaded ${noun}`, ) .option( "--markdown", - "Render describe output as a Markdown reference document (describe only)", + "Render describe as Markdown", ) .option( "--output ", - "Write describe Markdown to a file (requires --markdown)", + "Write Markdown to a file", ) - .option("--reload", `Reload ${plural} with cache-busted module imports`) + .option("--reload", `Reload ${plural} without import cache`) .option("--watch", "Use watch mode with --reload") .option( "--doctor", - `Run consolidated ${noun} diagnostics (summary/deep modes)`, + `Run ${noun} diagnostics`, ) .option("--catalog", `List bundled first-party ${noun} catalog metadata`) .option( "--adopt", - `Adopt an existing unmanaged ${noun} into managed metadata`, + `Adopt unmanaged ${noun}`, ) .option( "--adopt-all", - `Adopt all unmanaged ${plural} into managed metadata`, + `Adopt all unmanaged ${plural}`, ) .option("--activate", `Activate a ${noun} in selected scope settings`) .option("--deactivate", `Deactivate a ${noun} in selected scope settings`) @@ -832,6 +833,29 @@ function registerLifecycleCommand( ); }); + addLifecycleScopeOptions( + lifecycleCommand + .command("inventory") + .argument("[name]", `${noun[0]!.toUpperCase()}${noun.slice(1)} name or directory to inspect`) + .description(`Read configured ${noun} install and enablement state without activating extension code.`), + vocabulary, + ).action(async (name: string | undefined, _options: Record, command) => { + const globalOptions = getGlobalOptions(command); + const options = command.optsWithGlobals() as Record; + if (options.global === true && (options.project === true || options.local === true)) { + throw new PmCliError("--global and --project/--local are mutually exclusive.", EXIT_CODE.USAGE); + } + const result = await inspectStaticExtensionInventory({ + pmRoot: resolvePmRoot(process.cwd(), globalOptions.path), + scope: options.global === true ? "global" : "project", + name, + }); + printResult(result, globalOptions); + if (!result.complete) { + process.exitCode = EXIT_CODE.GENERIC_FAILURE; + } + }); + addLifecycleScopeOptions( lifecycleCommand .command("manage") diff --git a/src/cli/runtime/selection.ts b/src/cli/runtime/selection.ts index e98e9a02c..14a6892bb 100644 --- a/src/cli/runtime/selection.ts +++ b/src/cli/runtime/selection.ts @@ -21,6 +21,12 @@ import { const VERSION_FLAG_TOKENS = new Set(["--version", "-V"]); +/** Recognize the read-only package inventory before extension bootstrap. */ +function isStaticExtensionInventoryInvocation(invocationArgv: string[]): boolean { + const tokens = stripGlobalBootstrapTokens(invocationArgv); + return ["package", "packages", "extension"].includes(tokens[0] ?? "") && tokens[1] === "inventory"; +} + const SETUP_COMMAND_NAMES = new Set(["config", "extension", "init", "install", "package", "packages", "templates", "upgrade"]); /** Core read command names used to select the list/query registration family. */ @@ -199,6 +205,9 @@ function shouldAttachRichHelpTextForInvocation(invocationArgv: string[]): boolea /** Decide whether extension command paths must be registered for this invocation. */ function shouldRegisterDynamicExtensionPaths(_rootProgram: Command, invocationArgv: string[]): boolean { + if (isStaticExtensionInventoryInvocation(invocationArgv)) { + return false; + } if (invocationRequestsVersion(invocationArgv)) { return false; } @@ -299,4 +308,4 @@ function enforceExplicitRetryForFlagTypos(bootstrapInvocation: ReturnType).extensions; + if (configured === undefined) return { enabled: [], disabled: [] }; + if (typeof configured !== "object" || configured === null || Array.isArray(configured)) throw new SyntaxError("Invalid extensions settings"); + const value = configured as Record; + if ((value.enabled !== undefined && (!Array.isArray(value.enabled) || !value.enabled.every((entry) => typeof entry === "string"))) || + (value.disabled !== undefined && (!Array.isArray(value.disabled) || !value.disabled.every((entry) => typeof entry === "string")))) { + throw new SyntaxError("Invalid extension enablement lists"); + } + return { enabled: (value.enabled as string[] | undefined) ?? [], disabled: (value.disabled as string[] | undefined) ?? [] }; +} + +/** Read only the enablement fields; the general settings reader may scaffold schema files. */ +async function readEnablement(settingsPath: string): Promise<{ + status: StaticExtensionInventoryResult["settings_status"]; + enabled: string[]; + disabled: string[]; + error?: StaticExtensionInventoryError; +}> { + let raw: string; + try { + raw = await fs.readFile(settingsPath, "utf8"); + } catch (error: unknown) { + if (isMissing(error)) return { status: "absent", enabled: [], disabled: [] }; + return { status: "unreadable", enabled: [], disabled: [], error: { code: "settings_unreadable", path: settingsPath } }; + } + try { + return { status: "ok", ...parseEnablement(raw) }; + } catch { + return { status: "invalid", enabled: [], disabled: [], error: { code: "settings_invalid", path: settingsPath } }; + } +} + +/** Keep malformed managed metadata visible rather than silently dropping records. */ +async function readManagedRecords(managedPath: string): Promise<{ + status: StaticExtensionInventoryResult["managed_state_status"]; + entries: ManagedExtensionRecord[]; + error?: StaticExtensionInventoryError; +}> { + let raw: string; + try { + raw = await fs.readFile(managedPath, "utf8"); + } catch (error: unknown) { + if (isMissing(error)) return { status: "absent", entries: [] }; + return { status: "unreadable", entries: [], error: { code: "managed_state_unreadable", path: managedPath } }; + } + try { + const parsed: unknown = JSON.parse(raw); + const normalized = normalizeManagedState(parsed); + if (!normalized || typeof parsed !== "object" || parsed === null || !Array.isArray((parsed as { entries?: unknown }).entries) || + normalized.entries.length !== (parsed as { entries: unknown[] }).entries.length) { + throw new SyntaxError("Invalid managed extension state"); + } + return { status: "ok", entries: normalized.entries }; + } catch { + return { status: "invalid", entries: [], error: { code: "managed_state_invalid", path: managedPath } }; + } +} + +/** List installed directories without importing or resolving their entrypoints. */ +async function readExtensionDirectories(extensionsRoot: string): Promise<{ directories: string[]; error?: StaticExtensionInventoryError }> { + try { + const directories = (await fs.readdir(extensionsRoot, { withFileTypes: true })) + .filter((entry) => entry.isDirectory()) + .map((entry) => entry.name) + .sort((left, right) => left.localeCompare(right)); + return { directories }; + } catch (error: unknown) { + if (isMissing(error)) return { directories: [] }; + return { directories: [], error: { code: "extensions_unreadable", path: extensionsRoot } }; + } +} + +/** Parse manifest metadata only; an invalid entry remains in the receipt. */ +async function readManifest(manifestPath: string): Promise<{ + manifest: ReturnType; + error?: StaticExtensionInventoryError; +}> { + let raw: string; + try { + raw = await fs.readFile(manifestPath, "utf8"); + } catch (error: unknown) { + return { manifest: null, error: { code: isMissing(error) ? "manifest_invalid" : "manifest_unreadable", path: manifestPath } }; + } + try { + const manifest = parseExtensionManifest(JSON.parse(raw) as unknown); + return manifest ? { manifest } : { manifest: null, error: { code: "manifest_invalid", path: manifestPath } }; + } catch { + return { manifest: null, error: { code: "manifest_invalid", path: manifestPath } }; + } +} + +/** Project saved state without presenting it as runtime activation truth. */ +function projectConfiguredEntry(input: { + directory: string; + scope: "project" | "global"; + manifest: NonNullable> | null; + settings: Awaited>; + managed: Awaited>; +}): StaticExtensionInventoryEntry { + const name = input.manifest?.name ?? input.directory; + const configuredEnabled = input.settings.status === "invalid" || input.settings.status === "unreadable" || !input.manifest ? null : + !input.settings.disabled.includes(name) && (input.settings.enabled.length === 0 || input.settings.enabled.includes(name)); + const managed = input.managed.status === "invalid" || input.managed.status === "unreadable" ? null : input.managed.entries.some((entry) => + normalizeExtensionNameForMatch(entry.name) === normalizeExtensionNameForMatch(name) || + normalizeExtensionNameForMatch(entry.directory) === normalizeExtensionNameForMatch(input.directory)); + return { + name, + directory: input.directory, + scope: input.scope, + installed: true, + status: input.manifest ? configuredEnabled === false ? "inactive" : "installed" : "malformed_manifest", + configured_enabled: configuredEnabled, + managed, + runtime_active: null, + ...(input.manifest ? { version: input.manifest.version } : {}), + }; +} + +/** Read configured extension state through filesystem reads only. */ +export async function inspectStaticExtensionInventory(options: { + /** Tracker root for the project scope. */ + pmRoot: string; + /** Storage scope; project is the default. */ + scope?: "project" | "global"; + /** Optional name or directory to inspect, including absent installs. */ + name?: string; + /** Base directory used to resolve the global PM root. */ + cwd?: string; +}): Promise { + const scope = options.scope ?? "project"; + const selectedRoot = scope === "global" ? resolveGlobalPmRoot(options.cwd ?? process.cwd()) : options.pmRoot; + const extensionsRoot = path.join(selectedRoot, "extensions"); + const settings = await readEnablement(path.join(selectedRoot, "settings.json")); + const managed = await readManagedRecords(path.join(extensionsRoot, ".managed-extensions.json")); + const listed = await readExtensionDirectories(extensionsRoot); + const errors: StaticExtensionInventoryError[] = []; + if (settings.error) errors.push(settings.error); + if (managed.error) errors.push(managed.error); + if (listed.error) errors.push(listed.error); + + const requested = options.name?.trim(); + const extensions: StaticExtensionInventoryEntry[] = []; + for (const directory of listed.directories) { + const manifestPath = path.join(extensionsRoot, directory, "manifest.json"); + const inspected = await readManifest(manifestPath); + if (inspected.error) errors.push(inspected.error); + const entry = projectConfiguredEntry({ directory, scope, manifest: inspected.manifest, settings, managed }); + if (requested && ![entry.name, directory].some((value) => normalizeExtensionNameForMatch(value) === normalizeExtensionNameForMatch(requested))) { + continue; + } + extensions.push(entry); + } + if (requested && extensions.length === 0 && errors.every((error) => error.code !== "extensions_unreadable")) { + extensions.push({ name: requested, directory: null, scope, installed: false, status: "absent", configured_enabled: false, managed: false, runtime_active: null }); + } + return { scope, complete: errors.length === 0, settings_status: settings.status, managed_state_status: managed.status, extensions, errors }; +} diff --git a/src/sdk/generated/generated-error-code-catalog-part-1.ts b/src/sdk/generated/generated-error-code-catalog-part-1.ts index a8138a407..a6558b665 100644 --- a/src/sdk/generated/generated-error-code-catalog-part-1.ts +++ b/src/sdk/generated/generated-error-code-catalog-part-1.ts @@ -1069,6 +1069,19 @@ export const PM_ERROR_CODE_CATALOG_PART_1: PmErrorCodeContract[] = [ canonical_code: "extension_update_health_partial_coverage", aliases: [], }, + { + code: "extensions_unreadable", + meaning: "Extensions unreadable condition.", + stability: "provisional", + exit_code: 1, + class: "generic_failure", + recovery: + "Inspect the structured error guidance and retry the suggested command.", + sources: ["sdk/extension/static-inventory.ts"], + emitting_commands: ["*"], + canonical_code: "extensions_unreadable", + aliases: [], + }, { code: "field_duplicate", meaning: "Field duplicate condition.", @@ -2289,6 +2302,32 @@ export const PM_ERROR_CODE_CATALOG_PART_1: PmErrorCodeContract[] = [ canonical_code: "malformed_plan_step_evidence", aliases: [], }, + { + code: "managed_state_invalid", + meaning: "Managed state invalid condition.", + stability: "provisional", + exit_code: 2, + class: "usage", + recovery: + "Inspect the structured error guidance and retry the suggested command.", + sources: ["sdk/extension/static-inventory.ts"], + emitting_commands: ["*"], + canonical_code: "managed_state_invalid", + aliases: [], + }, + { + code: "managed_state_unreadable", + meaning: "Managed state unreadable condition.", + stability: "provisional", + exit_code: 1, + class: "generic_failure", + recovery: + "Inspect the structured error guidance and retry the suggested command.", + sources: ["sdk/extension/static-inventory.ts"], + emitting_commands: ["*"], + canonical_code: "managed_state_unreadable", + aliases: [], + }, { code: "manifest_capabilities_absent", meaning: "Manifest capabilities absent condition.", @@ -2302,6 +2341,19 @@ export const PM_ERROR_CODE_CATALOG_PART_1: PmErrorCodeContract[] = [ canonical_code: "manifest_capabilities_absent", aliases: [], }, + { + code: "manifest_invalid", + meaning: "Manifest invalid condition.", + stability: "provisional", + exit_code: 2, + class: "usage", + recovery: + "Inspect the structured error guidance and retry the suggested command.", + sources: ["sdk/extension/static-inventory.ts"], + emitting_commands: ["*"], + canonical_code: "manifest_invalid", + aliases: [], + }, { code: "manifest_unknown_key", meaning: "Manifest unknown key condition.", @@ -2474,17 +2526,4 @@ export const PM_ERROR_CODE_CATALOG_PART_1: PmErrorCodeContract[] = [ canonical_code: "merge_reconcile_receipt_evidence_untrusted", aliases: [], }, - { - code: "merge_root_not_found", - meaning: "Merge root not found condition.", - stability: "stable", - exit_code: 3, - class: "not_found", - recovery: - "Inspect the structured error guidance and retry the suggested command.", - sources: ["sdk/merge/install.ts"], - emitting_commands: ["*"], - canonical_code: "merge_root_not_found", - aliases: [], - }, ]; diff --git a/src/sdk/generated/generated-error-code-catalog-part-2.ts b/src/sdk/generated/generated-error-code-catalog-part-2.ts index cce67b470..5ea492049 100644 --- a/src/sdk/generated/generated-error-code-catalog-part-2.ts +++ b/src/sdk/generated/generated-error-code-catalog-part-2.ts @@ -7,6 +7,19 @@ import type { PmErrorCodeContract } from "../error-code-catalog.js"; /** Generated partition 2 of the exhaustive error-code catalog. */ export const PM_ERROR_CODE_CATALOG_PART_2: PmErrorCodeContract[] = [ + { + code: "merge_root_not_found", + meaning: "Merge root not found condition.", + stability: "stable", + exit_code: 3, + class: "not_found", + recovery: + "Inspect the structured error guidance and retry the suggested command.", + sources: ["sdk/merge/install.ts"], + emitting_commands: ["*"], + canonical_code: "merge_root_not_found", + aliases: [], + }, { code: "missing_allowed_values", meaning: "Missing allowed values condition.", @@ -935,6 +948,19 @@ export const PM_ERROR_CODE_CATALOG_PART_2: PmErrorCodeContract[] = [ canonical_code: "semantic_spelling_collision", aliases: [], }, + { + code: "settings_invalid", + meaning: "Settings invalid condition.", + stability: "provisional", + exit_code: 2, + class: "usage", + recovery: + "Inspect the structured error guidance and retry the suggested command.", + sources: ["sdk/extension/static-inventory.ts"], + emitting_commands: ["*"], + canonical_code: "settings_invalid", + aliases: [], + }, { code: "settings_read_fs_error", meaning: "Settings read fs error condition.", @@ -987,6 +1013,19 @@ export const PM_ERROR_CODE_CATALOG_PART_2: PmErrorCodeContract[] = [ canonical_code: "settings_read_merge_failed", aliases: [], }, + { + code: "settings_unreadable", + meaning: "Settings unreadable condition.", + stability: "provisional", + exit_code: 1, + class: "generic_failure", + recovery: + "Inspect the structured error guidance and retry the suggested command.", + sources: ["sdk/extension/static-inventory.ts"], + emitting_commands: ["*"], + canonical_code: "settings_unreadable", + aliases: [], + }, { code: "stale_budget", meaning: "Stale budget condition.", diff --git a/src/sdk/index.ts b/src/sdk/index.ts index da86e0910..ed40eafe9 100644 --- a/src/sdk/index.ts +++ b/src/sdk/index.ts @@ -191,6 +191,7 @@ export { type ValidateMutationServices, } from "./governance/validate.js"; export { runExtension, type ExtensionCommandAction } from "./extension.js"; +export { inspectStaticExtensionInventory, type StaticExtensionInventoryEntry, type StaticExtensionInventoryError, type StaticExtensionInventoryResult } from "./extension/static-inventory.js"; export { buildExtensionInstallPlan, planExtensionDirectoryCopy, diff --git a/tests/fixtures/contracts/full.json b/tests/fixtures/contracts/full.json index d9fc9b9a6..10dad1bb4 100644 --- a/tests/fixtures/contracts/full.json +++ b/tests/fixtures/contracts/full.json @@ -21305,6 +21305,23 @@ ], "stability": "stable" }, + { + "aliases": [], + "canonical_code": "extensions_unreadable", + "class": "generic_failure", + "code": "extensions_unreadable", + "emitting_commands": [ + "*" + ], + "exit_code": 1, + "meaning": "Extensions unreadable condition.", + "owned_states": [], + "recovery": "Inspect the structured error guidance and retry the suggested command.", + "sources": [ + "sdk/extension/static-inventory.ts" + ], + "stability": "provisional" + }, { "aliases": [], "canonical_code": "field_duplicate", @@ -22918,6 +22935,40 @@ ], "stability": "stable" }, + { + "aliases": [], + "canonical_code": "managed_state_invalid", + "class": "usage", + "code": "managed_state_invalid", + "emitting_commands": [ + "*" + ], + "exit_code": 2, + "meaning": "Managed state invalid condition.", + "owned_states": [], + "recovery": "Inspect the structured error guidance and retry the suggested command.", + "sources": [ + "sdk/extension/static-inventory.ts" + ], + "stability": "provisional" + }, + { + "aliases": [], + "canonical_code": "managed_state_unreadable", + "class": "generic_failure", + "code": "managed_state_unreadable", + "emitting_commands": [ + "*" + ], + "exit_code": 1, + "meaning": "Managed state unreadable condition.", + "owned_states": [], + "recovery": "Inspect the structured error guidance and retry the suggested command.", + "sources": [ + "sdk/extension/static-inventory.ts" + ], + "stability": "provisional" + }, { "aliases": [], "canonical_code": "manifest_capabilities_absent", @@ -22935,6 +22986,23 @@ ], "stability": "stable" }, + { + "aliases": [], + "canonical_code": "manifest_invalid", + "class": "usage", + "code": "manifest_invalid", + "emitting_commands": [ + "*" + ], + "exit_code": 2, + "meaning": "Manifest invalid condition.", + "owned_states": [], + "recovery": "Inspect the structured error guidance and retry the suggested command.", + "sources": [ + "sdk/extension/static-inventory.ts" + ], + "stability": "provisional" + }, { "aliases": [], "canonical_code": "manifest_unknown_key", @@ -24405,6 +24473,23 @@ ], "stability": "provisional" }, + { + "aliases": [], + "canonical_code": "settings_invalid", + "class": "usage", + "code": "settings_invalid", + "emitting_commands": [ + "*" + ], + "exit_code": 2, + "meaning": "Settings invalid condition.", + "owned_states": [], + "recovery": "Inspect the structured error guidance and retry the suggested command.", + "sources": [ + "sdk/extension/static-inventory.ts" + ], + "stability": "provisional" + }, { "aliases": [], "canonical_code": "settings_read_fs_error", @@ -24473,6 +24558,23 @@ ], "stability": "stable" }, + { + "aliases": [], + "canonical_code": "settings_unreadable", + "class": "generic_failure", + "code": "settings_unreadable", + "emitting_commands": [ + "*" + ], + "exit_code": 1, + "meaning": "Settings unreadable condition.", + "owned_states": [], + "recovery": "Inspect the structured error guidance and retry the suggested command.", + "sources": [ + "sdk/extension/static-inventory.ts" + ], + "stability": "provisional" + }, { "aliases": [], "canonical_code": "stale_budget", @@ -27536,6 +27638,14 @@ "reason": "", "target": "package" }, + { + "command": "package inventory", + "disposition": "target_noun", + "noun": "package", + "owner": "pm-pbyu", + "reason": "", + "target": "package" + }, { "command": "package list", "disposition": "target_noun", @@ -27792,6 +27902,14 @@ "reason": "", "target": "package" }, + { + "command": "packages inventory", + "disposition": "consolidation", + "noun": "package", + "owner": "pm-tnud", + "reason": "", + "target": "package" + }, { "command": "packages list", "disposition": "consolidation", @@ -29719,6 +29837,18 @@ } ] }, + { + "command": "package inventory", + "slots": [ + { + "name": "name", + "polymorphic": false, + "required": false, + "value_kind": "string", + "variadic": false + } + ] + }, { "command": "package list", "slots": [] @@ -29983,6 +30113,18 @@ } ] }, + { + "command": "packages inventory", + "slots": [ + { + "name": "name", + "polymorphic": false, + "required": false, + "value_kind": "string", + "variadic": false + } + ] + }, { "command": "packages list", "slots": [] diff --git a/tests/integration/extensions/static-extension-inventory.integration.spec.ts b/tests/integration/extensions/static-extension-inventory.integration.spec.ts new file mode 100644 index 000000000..fb2d65a80 --- /dev/null +++ b/tests/integration/extensions/static-extension-inventory.integration.spec.ts @@ -0,0 +1,146 @@ +import { mkdir, readFile, readdir, rmdir, stat, unlink, writeFile } from "node:fs/promises"; +import path from "node:path"; +import { describe, expect, it } from "vitest"; +import { inspectStaticExtensionInventory } from "../../../src/sdk/index.js"; +import { withTempPmPath } from "../../helpers/withTempPmPath.js"; + +describe("read-only configured extension inventory", () => { + it("reports saved enablement and absent names without importing extension code or changing tracker files", async () => { + await withTempPmPath(async ({ pmPath, tempRoot, runCli }) => { + const extensionRoot = path.join(pmPath, "extensions", "probe"); + const marker = path.join(tempRoot, "activated"); + await mkdir(extensionRoot, { recursive: true }); + await writeFile(path.join(extensionRoot, "manifest.json"), JSON.stringify({ name: "probe", version: "1.0.0", entry: "./index.mjs", manifest_version: 1, capabilities: ["commands"] })); + await writeFile(path.join(extensionRoot, "index.mjs"), `import { writeFileSync } from "node:fs"; writeFileSync(${JSON.stringify(marker)}, "activated"); export function activate() {}`); + const settingsPath = path.join(pmPath, "settings.json"); + const settingsBefore = await readFile(settingsPath); + const modifiedBefore = (await stat(settingsPath)).mtimeMs; + const filesBefore = (await readdir(pmPath, { recursive: true })).sort(); + + const cli = runCli(["package", "inventory", "--project", "--json"], { cwd: tempRoot, expectJson: true }); + expect(cli.code).toBe(0); + expect(cli.json).toMatchObject({ complete: true, extensions: [{ name: "probe", status: "installed", configured_enabled: true, managed: false, runtime_active: null }] }); + expect(runCli(["extension", "inventory", "probe", "--json"], { cwd: tempRoot, expectJson: true }).json) + .toMatchObject({ complete: true, extensions: [{ name: "probe" }] }); + expect(runCli(["package", "inventory", "--global", "--project"], { cwd: tempRoot }).code).not.toBe(0); + expect(await inspectStaticExtensionInventory({ pmRoot: pmPath, name: "missing" })).toMatchObject({ complete: true, extensions: [{ name: "missing", status: "absent", installed: false }] }); + expect((await readdir(pmPath, { recursive: true })).sort()).toEqual(filesBefore); + expect(await readFile(settingsPath)).toEqual(settingsBefore); + expect((await stat(settingsPath)).mtimeMs).toBe(modifiedBefore); + expect(await readdir(tempRoot)).not.toContain("activated"); + + const settings = JSON.parse(settingsBefore.toString()) as { extensions: { disabled: string[] } }; + settings.extensions.disabled = ["probe"]; + await writeFile(settingsPath, JSON.stringify(settings)); + const inactive = await inspectStaticExtensionInventory({ pmRoot: pmPath, name: "probe" }); + expect(inactive.extensions).toMatchObject([{ status: "inactive", configured_enabled: false }]); + }); + }); + + it("keeps global and project installations distinct", async () => { + await withTempPmPath(async ({ pmPath, tempRoot }) => { + const globalRoot = path.join(tempRoot, ".pm-cli-global", "extensions", "global-probe"); + await mkdir(globalRoot, { recursive: true }); + await writeFile(path.join(globalRoot, "manifest.json"), JSON.stringify({ name: "global-probe", version: "2.0.0", entry: "./index.mjs", manifest_version: 1, capabilities: [] })); + expect(await inspectStaticExtensionInventory({ pmRoot: pmPath, scope: "global", cwd: tempRoot })).toMatchObject({ scope: "global", complete: true, settings_status: "absent", extensions: [{ name: "global-probe", scope: "global", status: "installed" }] }); + expect(await inspectStaticExtensionInventory({ pmRoot: pmPath, scope: "project", name: "global-probe" })).toMatchObject({ scope: "project", extensions: [{ status: "absent" }] }); + expect((await inspectStaticExtensionInventory({ pmRoot: pmPath, scope: "global" })).extensions).toMatchObject([{ name: "global-probe" }]); + }); + }); + + it("surfaces malformed settings, managed records, and manifests as incomplete", async () => { + await withTempPmPath(async ({ pmPath, tempRoot, runCli }) => { + const extensionsRoot = path.join(pmPath, "extensions"); + const probeRoot = path.join(extensionsRoot, "broken"); + await mkdir(probeRoot, { recursive: true }); + await writeFile(path.join(probeRoot, "manifest.json"), "{"); + await writeFile(path.join(pmPath, "settings.json"), "{"); + await writeFile(path.join(extensionsRoot, ".managed-extensions.json"), JSON.stringify({ version: 1, entries: [{ name: "incomplete" }] })); + const sdk = await inspectStaticExtensionInventory({ pmRoot: pmPath }); + expect(sdk).toMatchObject({ complete: false, settings_status: "invalid", managed_state_status: "invalid", extensions: [{ status: "malformed_manifest", configured_enabled: null, managed: null }] }); + expect(sdk.errors.map((error) => error.code)).toEqual(["settings_invalid", "managed_state_invalid", "manifest_invalid"]); + const cli = runCli(["package", "inventory", "--json"], { cwd: tempRoot, expectJson: true }); + expect(cli.code).not.toBe(0); + expect(cli.json).toMatchObject({ complete: false }); + expect((cli.json as { errors: Array<{ code: string }> }).errors.map((error) => error.code)).toEqual(["settings_invalid", "managed_state_invalid", "manifest_invalid"]); + }); + }); + + it("reports filesystem read failures instead of treating them as empty inventory", async () => { + await withTempPmPath(async ({ pmPath }) => { + const settingsPath = path.join(pmPath, "settings.json"); + const extensionsRoot = path.join(pmPath, "extensions"); + await mkdir(extensionsRoot, { recursive: true }); + await writeFile(path.join(extensionsRoot, ".managed-extensions.json"), "{}"); + await mkdir(path.join(extensionsRoot, "unreadable")); + await mkdir(path.join(extensionsRoot, "unreadable", "manifest.json")); + const inventory = await inspectStaticExtensionInventory({ pmRoot: pmPath }); + expect(inventory.errors.map((error) => error.code)).toEqual(["managed_state_invalid", "manifest_unreadable"]); + expect(inventory.extensions).toMatchObject([{ status: "malformed_manifest" }]); + expect((await stat(settingsPath)).isFile()).toBe(true); + }); + }); + + it("preserves explicit errors for unreadable roots and sources", async () => { + await withTempPmPath(async ({ pmPath }) => { + const extensionsRoot = path.join(pmPath, "extensions"); + await rmdir(extensionsRoot); + await writeFile(extensionsRoot, "not a directory"); + const result = await inspectStaticExtensionInventory({ pmRoot: pmPath, name: "missing" }); + expect(result.complete).toBe(false); + expect(result.errors.map((error) => error.code)).toEqual(["managed_state_unreadable", "extensions_unreadable"]); + expect(result.extensions).toEqual([]); + }); + }); + + it("rejects malformed enablement shapes and reports unreadable settings", async () => { + await withTempPmPath(async ({ pmPath }) => { + const settingsPath = path.join(pmPath, "settings.json"); + for (const settings of ["null", "[]", '{"extensions":null}', '{"extensions":[]}', '{"extensions":{"enabled":1}}', '{"extensions":{"enabled":[1]}}', '{"extensions":{"disabled":1}}', '{"extensions":{"disabled":[1]}}']) { + await writeFile(settingsPath, settings); + expect(await inspectStaticExtensionInventory({ pmRoot: pmPath })).toMatchObject({ complete: false, settings_status: "invalid", errors: [{ code: "settings_invalid" }] }); + } + await writeFile(settingsPath, "{}"); + expect(await inspectStaticExtensionInventory({ pmRoot: pmPath })).toMatchObject({ complete: true, settings_status: "ok" }); + await writeFile(settingsPath, '{"extensions":{}}'); + expect(await inspectStaticExtensionInventory({ pmRoot: pmPath })).toMatchObject({ complete: true, settings_status: "ok" }); + await unlink(settingsPath); + await mkdir(settingsPath); + expect(await inspectStaticExtensionInventory({ pmRoot: pmPath })).toMatchObject({ complete: false, settings_status: "unreadable", errors: [{ code: "settings_unreadable" }] }); + }); + }); + + it("distinguishes valid managed installs, enablement allowlists, and bad manifests", async () => { + await withTempPmPath(async ({ pmPath }) => { + const root = path.join(pmPath, "extensions"); + const installed = path.join(root, "managed-dir"); + await mkdir(installed); + await writeFile(path.join(installed, "manifest.json"), JSON.stringify({ name: "managed-name", version: "1.0.0", entry: "./index.mjs", manifest_version: 1, capabilities: [] })); + await writeFile(path.join(pmPath, "settings.json"), JSON.stringify({ extensions: { enabled: ["elsewhere"], disabled: [] } })); + const managedPath = path.join(root, ".managed-extensions.json"); + await writeFile(managedPath, JSON.stringify({ version: 1, entries: [{ name: "managed-name", directory: "managed-dir", scope: "project", manifest_version: "1", manifest_entry: "./index.mjs", capabilities: [], installed_at: "2026-01-01T00:00:00Z", updated_at: "2026-01-01T00:00:00Z", source: { kind: "local", input: "fixture", location: "fixture" } }] })); + expect(await inspectStaticExtensionInventory({ pmRoot: pmPath, name: "managed-dir" })).toMatchObject({ complete: true, managed_state_status: "ok", extensions: [{ name: "managed-name", managed: true, status: "inactive" }] }); + await writeFile(path.join(pmPath, "settings.json"), JSON.stringify({ extensions: { enabled: ["managed-name"], disabled: [] } })); + expect((await inspectStaticExtensionInventory({ pmRoot: pmPath })).extensions).toMatchObject([{ configured_enabled: true, status: "installed" }]); + await writeFile(managedPath, JSON.stringify({ version: 1, entries: [{ name: "different", directory: "managed-dir", scope: "project", manifest_version: "1", manifest_entry: "./index.mjs", capabilities: [], installed_at: "2026-01-01T00:00:00Z", updated_at: "2026-01-01T00:00:00Z", source: { kind: "local", input: "fixture", location: "fixture" } }] })); + expect((await inspectStaticExtensionInventory({ pmRoot: pmPath })).extensions).toMatchObject([{ managed: true }]); + await writeFile(path.join(installed, "manifest.json"), "{}"); + expect(await inspectStaticExtensionInventory({ pmRoot: pmPath })).toMatchObject({ complete: false, extensions: [{ status: "malformed_manifest" }], errors: [{ code: "manifest_invalid" }] }); + await writeFile(managedPath, "{"); + expect((await inspectStaticExtensionInventory({ pmRoot: pmPath })).managed_state_status).toBe("invalid"); + }); + }); + + it("treats missing optional sources as absent and missing manifests as incomplete", async () => { + await withTempPmPath(async ({ pmPath }) => { + const root = path.join(pmPath, "extensions"); + await mkdir(path.join(root, "without-manifest")); + await mkdir(path.join(root, "another-missing-manifest")); + expect(await inspectStaticExtensionInventory({ pmRoot: pmPath })).toMatchObject({ complete: false, extensions: [{ directory: "another-missing-manifest", status: "malformed_manifest" }, { directory: "without-manifest", status: "malformed_manifest" }], errors: [{ code: "manifest_invalid" }, { code: "manifest_invalid" }] }); + await rmdir(path.join(root, "another-missing-manifest")); + await rmdir(path.join(root, "without-manifest")); + await rmdir(root); + expect(await inspectStaticExtensionInventory({ pmRoot: pmPath, name: "missing" })).toMatchObject({ complete: true, managed_state_status: "absent", extensions: [{ status: "absent" }] }); + }); + }); +}); diff --git a/tests/unit/cli/static-extension-inventory.spec.ts b/tests/unit/cli/static-extension-inventory.spec.ts new file mode 100644 index 000000000..8edb741e6 --- /dev/null +++ b/tests/unit/cli/static-extension-inventory.spec.ts @@ -0,0 +1,53 @@ +import { mkdir, writeFile } from "node:fs/promises"; +import path from "node:path"; +import { describe, expect, it, vi } from "vitest"; +import { registerSetupCommands } from "../../../src/cli/register-setup.js"; +import { isStaticExtensionInventoryInvocation, shouldRegisterDynamicExtensionPaths } from "../../../src/cli/runtime/selection.js"; +import { createPmCliProgram } from "../../../src/sdk/cli-program.js"; +import { withTempPmPath } from "../../helpers/withTempPmPath.js"; + +describe("static extension inventory command routing", () => { + it("selects static invocations before dynamic extension registration", () => { + const program = createPmCliProgram("test"); + for (const name of ["package", "packages", "extension"]) { + expect(isStaticExtensionInventoryInvocation(["--json", name, "inventory"])).toBe(true); + expect(shouldRegisterDynamicExtensionPaths(program, [name, "inventory"])).toBe(false); + } + expect(isStaticExtensionInventoryInvocation(["package", "explore"])).toBe(false); + expect(isStaticExtensionInventoryInvocation([])).toBe(false); + }); + + it("dispatches source CLI inventory with scope and incomplete-read receipts", async () => { + await withTempPmPath(async ({ pmPath }) => { + const extensionRoot = path.join(pmPath, "extensions", "source-probe"); + await mkdir(extensionRoot, { recursive: true }); + const manifestPath = path.join(extensionRoot, "manifest.json"); + await writeFile(manifestPath, JSON.stringify({ name: "source-probe", version: "1.0.0", entry: "./index.mjs", manifest_version: 1, capabilities: [] })); + const output: string[] = []; + const stdout = vi.spyOn(process.stdout, "write").mockImplementation((chunk) => { + output.push(String(chunk)); + return true; + }); + const previousExitCode = process.exitCode; + try { + const run = async (args: string[]): Promise => { + const program = createPmCliProgram("test"); + registerSetupCommands(program); + await program.parseAsync(["--pm-path", pmPath, "--json", ...args], { from: "user" }); + }; + await run(["package", "inventory", "--project"]); + expect(JSON.parse(output.pop() ?? "{}")).toMatchObject({ complete: true, extensions: [{ name: "source-probe" }] }); + await run(["packages", "inventory", "--global"]); + expect(JSON.parse(output.pop() ?? "{}")).toMatchObject({ complete: true, scope: "global", extensions: [] }); + await expect(run(["extension", "inventory", "--global", "--local"])).rejects.toThrow(/mutually exclusive/); + await writeFile(manifestPath, "{"); + await run(["package", "inventory"]); + expect(JSON.parse(output.pop() ?? "{}")).toMatchObject({ complete: false, errors: [{ code: "manifest_invalid" }] }); + expect(process.exitCode).not.toBe(0); + } finally { + process.exitCode = previousExitCode; + stdout.mockRestore(); + } + }); + }); +}); From c50739d8cad08940247f928bae296dd581ff38b6 Mon Sep 17 00:00:00 2001 From: Stefan Preu Date: Sun, 27 Sep 2026 00:44:17 +0200 Subject: [PATCH 2/3] Align static inventory receipts with configured source trust Normalize saved enablement names with the runtime discovery rules so whitespace cannot invert installed or inactive results. Preserve unknown values on absent entries when settings or managed metadata are invalid or unreadable. Emit manifest_unreadable as a literal code for generated SDK and CLI contracts, add acceptance assertions for the corrected cases, and record the first PR review findings and verification in the PM history. --- .agents/pm/history/pm-lhhnx9.jsonl | 1 + .agents/pm/issues/pm-lhhnx9.toon | 5 +-- sdk/public-surface.json | 1 + src/sdk/extension/static-inventory.ts | 31 +++++++++++++------ .../generated-error-code-catalog-part-1.ts | 13 ++++++++ tests/fixtures/contracts/full.json | 17 ++++++++++ ...ic-extension-inventory.integration.spec.ts | 7 ++++- 7 files changed, 63 insertions(+), 12 deletions(-) diff --git a/.agents/pm/history/pm-lhhnx9.jsonl b/.agents/pm/history/pm-lhhnx9.jsonl index 05a4f883f..934d91d84 100644 --- a/.agents/pm/history/pm-lhhnx9.jsonl +++ b/.agents/pm/history/pm-lhhnx9.jsonl @@ -25,3 +25,4 @@ {"hash_algorithm":"sha256","ts":"2026-09-26T21:46:51.170Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/7","value":{"created_at":"2026-09-26T21:46:51.170Z","author":"harness:codex","text":"Static quality refusal inventory found the new mutually exclusive inventory scope check. Updated the declared CLI transport refusal count 6 to 7 with rationale; the SDK inventory remains read-only."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T21:46:51.170Z"}],"before_hash":"b36025ff1486ac26a9689c05676a8ba76a2dfe2bca7e298ee074c515d8ef3cf4","after_hash":"6bb884bb2f2b207b15ec5f8ae6f59f58e52863a972324d6714eee87448bdcb3a","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"50b77552989721a8506f7c589e4861fb032580ee0fffa2d6ca436681c297bbba"} {"hash_algorithm":"sha256","ts":"2026-09-26T21:58:24.724Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/12/note","value":"CLI and SDK behavior with nonactivation and read failure controls"},{"op":"replace","path":"/metadata/files/12/path","value":"tests/integration/extensions/static-extension-inventory.integration.spec.ts"},{"op":"replace","path":"/metadata/files/11/note","value":"CLI command contract snapshot for package inventory"},{"op":"replace","path":"/metadata/files/11/path","value":"tests/fixtures/contracts/full.json"},{"op":"replace","path":"/metadata/files/10/note","value":"Public SDK export"},{"op":"replace","path":"/metadata/files/10/path","value":"src/sdk/index.ts"},{"op":"replace","path":"/metadata/files/9/note","value":"Generated SDK error catalog for static inventory receipts"},{"op":"replace","path":"/metadata/files/9/path","value":"src/sdk/generated/generated-error-code-catalog-part-2.ts"},{"op":"replace","path":"/metadata/files/8/path","value":"src/sdk/generated/generated-error-code-catalog-part-1.ts"},{"op":"replace","path":"/metadata/files/7/note","value":"Read-only SDK configured-state primitive"},{"op":"replace","path":"/metadata/files/7/path","value":"src/sdk/extension/static-inventory.ts"},{"op":"replace","path":"/metadata/files/6/note","value":"Agent-facing package inventory grammar and positional contract"},{"op":"replace","path":"/metadata/files/6/path","value":"src/sdk/cli-contracts/grammar-contracts.ts"},{"op":"add","path":"/metadata/files/13","value":{"path":"tests/unit/cli/static-extension-inventory.spec.ts","scope":"project","note":"Source CLI registration and routing coverage"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T21:58:24.724Z"}],"before_hash":"6bb884bb2f2b207b15ec5f8ae6f59f58e52863a972324d6714eee87448bdcb3a","after_hash":"a357252096d33206e8a314877223dd9cef0b0d3b24c551760d34a0182101244f","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"974b41f1e449f0473a2b298a699d6b2e29d99b14b2e5bd567f7867ce47dc61a4"} {"hash_algorithm":"sha256","ts":"2026-09-26T21:58:25.472Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/8","value":{"created_at":"2026-09-26T21:58:25.472Z","author":"harness:codex","text":"Command grammar gate required noun/verb destination census and optional name positional signatures for package inventory and packages inventory. Declared both; the grammar gate now passes with 231 observed and declared paths."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T21:58:25.472Z"}],"before_hash":"a357252096d33206e8a314877223dd9cef0b0d3b24c551760d34a0182101244f","after_hash":"911d89938c3c639e1467beb30c360ac1544664d2b17d824b91ffaf0be4eba05c","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"36331f8157ea67cb112499e7d9d28a34dc757f672e12dcb117aeb3bdf9c9d633"} +{"hash_algorithm":"sha256","ts":"2026-09-26T22:43:58.417Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/9","value":{"created_at":"2026-09-26T22:43:58.417Z","author":"harness:codex","text":"PR #1319 review round 1 at head 145d1edca: CodeRabbit identified three valid receipt inconsistencies. Static inventory now trims and filters enablement names like runtime discovery, uses null for absent-entry values when settings or managed metadata are untrusted, and emits a literal manifest_unreadable code so generated SDK and CLI catalogs include it. Extended existing integration cases for whitespace and untrusted absence. Rebuilt, regenerated catalogs and contracts, and passed focused 10/10, typecheck, ESLint, and snapshot checks. Hosted head checks were green before this revision; new head will rerun them and bot reviews."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T22:43:58.417Z"}],"before_hash":"911d89938c3c639e1467beb30c360ac1544664d2b17d824b91ffaf0be4eba05c","after_hash":"01743849949202e79c436ac64d0561c2bbd837d4b8f9857e759af4f6ecae4c7c","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"328edc4e27d34a4d1fb0c0325ec96b9b2cc7a2de58908dd6ff5e391f7f56ac2c"} diff --git a/.agents/pm/issues/pm-lhhnx9.toon b/.agents/pm/issues/pm-lhhnx9.toon index e2780a1df..350aaed9a 100644 --- a/.agents/pm/issues/pm-lhhnx9.toon +++ b/.agents/pm/issues/pm-lhhnx9.toon @@ -6,7 +6,7 @@ status: closed priority: 1 tags[5]: agent-ux,"area:extensions","area:sdk",gh-1316,security created_at: "2026-09-26T16:19:02.178Z" -updated_at: "2026-09-26T21:58:25.472Z" +updated_at: "2026-09-26T22:43:58.417Z" closed_at: "2026-09-26T21:26:37.459Z" completed_at: "2026-09-26T21:26:37.459Z" author: "harness:codex" @@ -19,7 +19,7 @@ dependencies[3]{id,kind,created_at,author,source_kind,author_source}: pm-5mua,verifies,"2026-09-26T16:19:02.178Z","harness:codex","cli:create:dep",detected pm-x6jf,discovered_from,"2026-09-26T16:19:02.178Z","harness:codex","cli:create:dep",detected pm-l4c8,verifies,"2026-09-26T16:33:43.474Z","harness:codex","cli:update:dep",detected -comments[9]{created_at,author,text}: +comments[10]{created_at,author,text}: "2026-09-26T16:19:02.178Z","harness:codex","Duplicate check 2026-09-26: all-status searches for extension state inventory, side-effect-free extension, and hosted extension read found closed pm-grst managed state, pm-x6jf public lifecycle SDK, pm-l4c8 runtime describe, and pm-5mua runtime command-path diagnostics. Their shipped runtime probes do not provide the requested static configured-state receipt. GitHub issue: https://github.com/unbraind/pm-cli/issues/1316." "2026-09-26T21:26:34.351Z","harness:codex","Implementation evidence 2026-09-26: public SDK inspectStaticExtensionInventory and package/extension inventory CLI read settings, managed metadata, and manifests without loading entrypoints or running hooks. Receipts distinguish installed, inactive, absent, malformed, project/global, and incomplete sources. A disposable initialized workspace with an import-time write marker returned complete inventory without marker or tracker changes; explicit runtime explore afterward created the marker, proving the negative control. Source CLI and dist integration tests cover the behavior." "2026-09-26T21:26:35.294Z","harness:codex","Verification evidence 2026-09-26: full sandboxed suite passed 748 files / 9,367 tests with exact statements 65,668/65,668, branches 50,209/50,209, functions 13,594/13,594, lines 62,715/62,715. Focused source and dist tests passed 10/10 after final help wording. pnpm build, typecheck, ESLint, jscpd, docs/skills, SDK surface, and flag-help gate passed; pm health summary ok with zero warnings. pm-changelog installed version 2026.9.25 matches npm latest." @@ -29,6 +29,7 @@ comments[9]{created_at,author,text}: "2026-09-26T21:39:05.278Z","harness:codex",Static gate required regenerating the agent capability and refusal census after adding package inventory; linked generated document. "2026-09-26T21:46:51.170Z","harness:codex",Static quality refusal inventory found the new mutually exclusive inventory scope check. Updated the declared CLI transport refusal count 6 to 7 with rationale; the SDK inventory remains read-only. "2026-09-26T21:58:25.472Z","harness:codex",Command grammar gate required noun/verb destination census and optional name positional signatures for package inventory and packages inventory. Declared both; the grammar gate now passes with 231 observed and declared paths. + "2026-09-26T22:43:58.417Z","harness:codex","PR #1319 review round 1 at head 145d1edca: CodeRabbit identified three valid receipt inconsistencies. Static inventory now trims and filters enablement names like runtime discovery, uses null for absent-entry values when settings or managed metadata are untrusted, and emits a literal manifest_unreadable code so generated SDK and CLI catalogs include it. Extended existing integration cases for whitespace and untrusted absence. Rebuilt, regenerated catalogs and contracts, and passed focused 10/10, typecheck, ESLint, and snapshot checks. Hosted head checks were green before this revision; new head will rerun them and bot reviews." learnings[1]{created_at,author,text}: "2026-09-26T21:26:36.371Z","harness:codex","A host GET route must use the static inventory primitive; runtime explore/manage/doctor/describe can import installed extension code. Read settings directly for inventory because the general settings reader may scaffold schema files. Keep configured enablement and runtime activation as separate facts, and fail incomplete reads explicitly." files[14]{path,scope,note}: diff --git a/sdk/public-surface.json b/sdk/public-surface.json index edeb0fd9a..c6986130f 100644 --- a/sdk/public-surface.json +++ b/sdk/public-surface.json @@ -35339,6 +35339,7 @@ "manifest_capabilities_absent", "manifest_invalid", "manifest_unknown_key", + "manifest_unreadable", "mcp_annotation_file_unavailable", "mcp_stdin_unavailable", "mcp_task_not_found_or_not_authorized", diff --git a/src/sdk/extension/static-inventory.ts b/src/sdk/extension/static-inventory.ts index fb1dccd9d..3605f28f6 100644 --- a/src/sdk/extension/static-inventory.ts +++ b/src/sdk/extension/static-inventory.ts @@ -7,7 +7,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { resolveGlobalPmRoot } from "../../core/store/paths.js"; import { normalizeManagedState, type ManagedExtensionRecord } from "./managed-state.js"; -import { normalizeExtensionNameForMatch, parseExtensionManifest } from "./shared.js"; +import { normalizeExtensionNameForMatch, normalizeStringList, parseExtensionManifest } from "./shared.js"; /** One read failure or malformed document that prevents a complete inventory. */ export interface StaticExtensionInventoryError { @@ -60,6 +60,11 @@ function isMissing(error: unknown): boolean { return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT"; } +/** An absent optional source is trustworthy; malformed and unreadable sources are not. */ +function isTrustedSourceStatus(status: "ok" | "absent" | "invalid" | "unreadable"): boolean { + return status !== "invalid" && status !== "unreadable"; +} + /** Validate the saved enablement lists before they influence a hosted read. */ function parseEnablement(raw: string): { enabled: string[]; disabled: string[] } { const parsed: unknown = JSON.parse(raw); @@ -145,7 +150,10 @@ async function readManifest(manifestPath: string): Promise<{ try { raw = await fs.readFile(manifestPath, "utf8"); } catch (error: unknown) { - return { manifest: null, error: { code: isMissing(error) ? "manifest_invalid" : "manifest_unreadable", path: manifestPath } }; + if (isMissing(error)) { + return { manifest: null, error: { code: "manifest_invalid", path: manifestPath } }; + } + return { manifest: null, error: { code: "manifest_unreadable", path: manifestPath } }; } try { const manifest = parseExtensionManifest(JSON.parse(raw) as unknown); @@ -164,9 +172,11 @@ function projectConfiguredEntry(input: { managed: Awaited>; }): StaticExtensionInventoryEntry { const name = input.manifest?.name ?? input.directory; - const configuredEnabled = input.settings.status === "invalid" || input.settings.status === "unreadable" || !input.manifest ? null : - !input.settings.disabled.includes(name) && (input.settings.enabled.length === 0 || input.settings.enabled.includes(name)); - const managed = input.managed.status === "invalid" || input.managed.status === "unreadable" ? null : input.managed.entries.some((entry) => + const enabled = new Set(normalizeStringList(input.settings.enabled)); + const disabled = new Set(normalizeStringList(input.settings.disabled)); + const configuredEnabled = !isTrustedSourceStatus(input.settings.status) || !input.manifest ? null : + !disabled.has(name) && (enabled.size === 0 || enabled.has(name)); + const managed = !isTrustedSourceStatus(input.managed.status) ? null : input.managed.entries.some((entry) => normalizeExtensionNameForMatch(entry.name) === normalizeExtensionNameForMatch(name) || normalizeExtensionNameForMatch(entry.directory) === normalizeExtensionNameForMatch(input.directory)); return { @@ -200,9 +210,9 @@ export async function inspectStaticExtensionInventory(options: { const managed = await readManagedRecords(path.join(extensionsRoot, ".managed-extensions.json")); const listed = await readExtensionDirectories(extensionsRoot); const errors: StaticExtensionInventoryError[] = []; - if (settings.error) errors.push(settings.error); - if (managed.error) errors.push(managed.error); - if (listed.error) errors.push(listed.error); + for (const source of [settings, managed, listed]) { + if (source.error) errors.push(source.error); + } const requested = options.name?.trim(); const extensions: StaticExtensionInventoryEntry[] = []; @@ -217,7 +227,10 @@ export async function inspectStaticExtensionInventory(options: { extensions.push(entry); } if (requested && extensions.length === 0 && errors.every((error) => error.code !== "extensions_unreadable")) { - extensions.push({ name: requested, directory: null, scope, installed: false, status: "absent", configured_enabled: false, managed: false, runtime_active: null }); + extensions.push({ name: requested, directory: null, scope, installed: false, status: "absent", + configured_enabled: isTrustedSourceStatus(settings.status) ? false : null, + managed: isTrustedSourceStatus(managed.status) ? false : null, + runtime_active: null }); } return { scope, complete: errors.length === 0, settings_status: settings.status, managed_state_status: managed.status, extensions, errors }; } diff --git a/src/sdk/generated/generated-error-code-catalog-part-1.ts b/src/sdk/generated/generated-error-code-catalog-part-1.ts index a6558b665..1aff75fb2 100644 --- a/src/sdk/generated/generated-error-code-catalog-part-1.ts +++ b/src/sdk/generated/generated-error-code-catalog-part-1.ts @@ -2370,6 +2370,19 @@ export const PM_ERROR_CODE_CATALOG_PART_1: PmErrorCodeContract[] = [ { state: "author_workspace_manifest_declares_an_unknown_top_level_key", probe_id: "author-manifest-unknown-key", entrypoints: ["health"], expected_exit_class: "generic_failure" }, ], }, + { + code: "manifest_unreadable", + meaning: "Manifest unreadable condition.", + stability: "provisional", + exit_code: 1, + class: "generic_failure", + recovery: + "Inspect the structured error guidance and retry the suggested command.", + sources: ["sdk/extension/static-inventory.ts"], + emitting_commands: ["*"], + canonical_code: "manifest_unreadable", + aliases: [], + }, { code: "mcp_annotation_file_unavailable", meaning: "Mcp annotation file unavailable condition.", diff --git a/tests/fixtures/contracts/full.json b/tests/fixtures/contracts/full.json index 10dad1bb4..6eecae034 100644 --- a/tests/fixtures/contracts/full.json +++ b/tests/fixtures/contracts/full.json @@ -23029,6 +23029,23 @@ ], "stability": "provisional" }, + { + "aliases": [], + "canonical_code": "manifest_unreadable", + "class": "generic_failure", + "code": "manifest_unreadable", + "emitting_commands": [ + "*" + ], + "exit_code": 1, + "meaning": "Manifest unreadable condition.", + "owned_states": [], + "recovery": "Inspect the structured error guidance and retry the suggested command.", + "sources": [ + "sdk/extension/static-inventory.ts" + ], + "stability": "provisional" + }, { "aliases": [], "canonical_code": "mcp_annotation_file_unavailable", diff --git a/tests/integration/extensions/static-extension-inventory.integration.spec.ts b/tests/integration/extensions/static-extension-inventory.integration.spec.ts index fb2d65a80..67d10792b 100644 --- a/tests/integration/extensions/static-extension-inventory.integration.spec.ts +++ b/tests/integration/extensions/static-extension-inventory.integration.spec.ts @@ -30,10 +30,13 @@ describe("read-only configured extension inventory", () => { expect(await readdir(tempRoot)).not.toContain("activated"); const settings = JSON.parse(settingsBefore.toString()) as { extensions: { disabled: string[] } }; - settings.extensions.disabled = ["probe"]; + settings.extensions.disabled = [" probe "]; await writeFile(settingsPath, JSON.stringify(settings)); const inactive = await inspectStaticExtensionInventory({ pmRoot: pmPath, name: "probe" }); expect(inactive.extensions).toMatchObject([{ status: "inactive", configured_enabled: false }]); + await writeFile(settingsPath, JSON.stringify({ extensions: { enabled: [" probe "], disabled: [] } })); + expect((await inspectStaticExtensionInventory({ pmRoot: pmPath, name: "probe" })).extensions) + .toMatchObject([{ status: "installed", configured_enabled: true }]); }); }); @@ -59,6 +62,8 @@ describe("read-only configured extension inventory", () => { const sdk = await inspectStaticExtensionInventory({ pmRoot: pmPath }); expect(sdk).toMatchObject({ complete: false, settings_status: "invalid", managed_state_status: "invalid", extensions: [{ status: "malformed_manifest", configured_enabled: null, managed: null }] }); expect(sdk.errors.map((error) => error.code)).toEqual(["settings_invalid", "managed_state_invalid", "manifest_invalid"]); + expect((await inspectStaticExtensionInventory({ pmRoot: pmPath, name: "missing" })).extensions) + .toMatchObject([{ status: "absent", configured_enabled: null, managed: null }]); const cli = runCli(["package", "inventory", "--json"], { cwd: tempRoot, expectJson: true }); expect(cli.code).not.toBe(0); expect(cli.json).toMatchObject({ complete: false }); From f0733eaecb749b93f275be61dd1f3301e7d4726a Mon Sep 17 00:00:00 2001 From: Stefan Preu Date: Sun, 27 Sep 2026 00:54:21 +0200 Subject: [PATCH 3/3] Refresh refusal census for manifest read errors Regenerate the agent capability census after the static inventory's manifest_unreadable code became part of the public error catalog. Record the hosted static-gate finding and its resolution in the linked PM item. --- .agents/pm/history/pm-lhhnx9.jsonl | 1 + .agents/pm/issues/pm-lhhnx9.toon | 5 +++-- docs/generated/REFUSAL_CLOSURE_CENSUS.md | 7 ++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.agents/pm/history/pm-lhhnx9.jsonl b/.agents/pm/history/pm-lhhnx9.jsonl index 934d91d84..370dda7b0 100644 --- a/.agents/pm/history/pm-lhhnx9.jsonl +++ b/.agents/pm/history/pm-lhhnx9.jsonl @@ -26,3 +26,4 @@ {"hash_algorithm":"sha256","ts":"2026-09-26T21:58:24.724Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/12/note","value":"CLI and SDK behavior with nonactivation and read failure controls"},{"op":"replace","path":"/metadata/files/12/path","value":"tests/integration/extensions/static-extension-inventory.integration.spec.ts"},{"op":"replace","path":"/metadata/files/11/note","value":"CLI command contract snapshot for package inventory"},{"op":"replace","path":"/metadata/files/11/path","value":"tests/fixtures/contracts/full.json"},{"op":"replace","path":"/metadata/files/10/note","value":"Public SDK export"},{"op":"replace","path":"/metadata/files/10/path","value":"src/sdk/index.ts"},{"op":"replace","path":"/metadata/files/9/note","value":"Generated SDK error catalog for static inventory receipts"},{"op":"replace","path":"/metadata/files/9/path","value":"src/sdk/generated/generated-error-code-catalog-part-2.ts"},{"op":"replace","path":"/metadata/files/8/path","value":"src/sdk/generated/generated-error-code-catalog-part-1.ts"},{"op":"replace","path":"/metadata/files/7/note","value":"Read-only SDK configured-state primitive"},{"op":"replace","path":"/metadata/files/7/path","value":"src/sdk/extension/static-inventory.ts"},{"op":"replace","path":"/metadata/files/6/note","value":"Agent-facing package inventory grammar and positional contract"},{"op":"replace","path":"/metadata/files/6/path","value":"src/sdk/cli-contracts/grammar-contracts.ts"},{"op":"add","path":"/metadata/files/13","value":{"path":"tests/unit/cli/static-extension-inventory.spec.ts","scope":"project","note":"Source CLI registration and routing coverage"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T21:58:24.724Z"}],"before_hash":"6bb884bb2f2b207b15ec5f8ae6f59f58e52863a972324d6714eee87448bdcb3a","after_hash":"a357252096d33206e8a314877223dd9cef0b0d3b24c551760d34a0182101244f","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"974b41f1e449f0473a2b298a699d6b2e29d99b14b2e5bd567f7867ce47dc61a4"} {"hash_algorithm":"sha256","ts":"2026-09-26T21:58:25.472Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/8","value":{"created_at":"2026-09-26T21:58:25.472Z","author":"harness:codex","text":"Command grammar gate required noun/verb destination census and optional name positional signatures for package inventory and packages inventory. Declared both; the grammar gate now passes with 231 observed and declared paths."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T21:58:25.472Z"}],"before_hash":"a357252096d33206e8a314877223dd9cef0b0d3b24c551760d34a0182101244f","after_hash":"911d89938c3c639e1467beb30c360ac1544664d2b17d824b91ffaf0be4eba05c","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"36331f8157ea67cb112499e7d9d28a34dc757f672e12dcb117aeb3bdf9c9d633"} {"hash_algorithm":"sha256","ts":"2026-09-26T22:43:58.417Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/9","value":{"created_at":"2026-09-26T22:43:58.417Z","author":"harness:codex","text":"PR #1319 review round 1 at head 145d1edca: CodeRabbit identified three valid receipt inconsistencies. Static inventory now trims and filters enablement names like runtime discovery, uses null for absent-entry values when settings or managed metadata are untrusted, and emits a literal manifest_unreadable code so generated SDK and CLI catalogs include it. Extended existing integration cases for whitespace and untrusted absence. Rebuilt, regenerated catalogs and contracts, and passed focused 10/10, typecheck, ESLint, and snapshot checks. Hosted head checks were green before this revision; new head will rerun them and bot reviews."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T22:43:58.417Z"}],"before_hash":"911d89938c3c639e1467beb30c360ac1544664d2b17d824b91ffaf0be4eba05c","after_hash":"01743849949202e79c436ac64d0561c2bbd837d4b8f9857e759af4f6ecae4c7c","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"328edc4e27d34a4d1fb0c0325ec96b9b2cc7a2de58908dd6ff5e391f7f56ac2c"} +{"hash_algorithm":"sha256","ts":"2026-09-26T22:53:33.788Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/10","value":{"created_at":"2026-09-26T22:53:33.788Z","author":"harness:codex","text":"PR #1319 round 2 hosted static gate found generated REFUSAL_CLOSURE_CENSUS.md stale after manifest_unreadable became a catalog code. Regenerated via pnpm contracts:agent-surfaces:update; the document now records 385 catalog codes and the explicit uncovered row. All other completed hosted checks on c50739d8c passed."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T22:53:33.788Z"}],"before_hash":"01743849949202e79c436ac64d0561c2bbd837d4b8f9857e759af4f6ecae4c7c","after_hash":"c23369f7dac8da43e3a8414f83865e80277520b2b7b3f0606b264d4bec3d023e","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"5597a6c0ccbae9422317e100e2dbc2208ba18c6993f76a10753841b67754c67c"} diff --git a/.agents/pm/issues/pm-lhhnx9.toon b/.agents/pm/issues/pm-lhhnx9.toon index 350aaed9a..6ad215bd0 100644 --- a/.agents/pm/issues/pm-lhhnx9.toon +++ b/.agents/pm/issues/pm-lhhnx9.toon @@ -6,7 +6,7 @@ status: closed priority: 1 tags[5]: agent-ux,"area:extensions","area:sdk",gh-1316,security created_at: "2026-09-26T16:19:02.178Z" -updated_at: "2026-09-26T22:43:58.417Z" +updated_at: "2026-09-26T22:53:33.788Z" closed_at: "2026-09-26T21:26:37.459Z" completed_at: "2026-09-26T21:26:37.459Z" author: "harness:codex" @@ -19,7 +19,7 @@ dependencies[3]{id,kind,created_at,author,source_kind,author_source}: pm-5mua,verifies,"2026-09-26T16:19:02.178Z","harness:codex","cli:create:dep",detected pm-x6jf,discovered_from,"2026-09-26T16:19:02.178Z","harness:codex","cli:create:dep",detected pm-l4c8,verifies,"2026-09-26T16:33:43.474Z","harness:codex","cli:update:dep",detected -comments[10]{created_at,author,text}: +comments[11]{created_at,author,text}: "2026-09-26T16:19:02.178Z","harness:codex","Duplicate check 2026-09-26: all-status searches for extension state inventory, side-effect-free extension, and hosted extension read found closed pm-grst managed state, pm-x6jf public lifecycle SDK, pm-l4c8 runtime describe, and pm-5mua runtime command-path diagnostics. Their shipped runtime probes do not provide the requested static configured-state receipt. GitHub issue: https://github.com/unbraind/pm-cli/issues/1316." "2026-09-26T21:26:34.351Z","harness:codex","Implementation evidence 2026-09-26: public SDK inspectStaticExtensionInventory and package/extension inventory CLI read settings, managed metadata, and manifests without loading entrypoints or running hooks. Receipts distinguish installed, inactive, absent, malformed, project/global, and incomplete sources. A disposable initialized workspace with an import-time write marker returned complete inventory without marker or tracker changes; explicit runtime explore afterward created the marker, proving the negative control. Source CLI and dist integration tests cover the behavior." "2026-09-26T21:26:35.294Z","harness:codex","Verification evidence 2026-09-26: full sandboxed suite passed 748 files / 9,367 tests with exact statements 65,668/65,668, branches 50,209/50,209, functions 13,594/13,594, lines 62,715/62,715. Focused source and dist tests passed 10/10 after final help wording. pnpm build, typecheck, ESLint, jscpd, docs/skills, SDK surface, and flag-help gate passed; pm health summary ok with zero warnings. pm-changelog installed version 2026.9.25 matches npm latest." @@ -30,6 +30,7 @@ comments[10]{created_at,author,text}: "2026-09-26T21:46:51.170Z","harness:codex",Static quality refusal inventory found the new mutually exclusive inventory scope check. Updated the declared CLI transport refusal count 6 to 7 with rationale; the SDK inventory remains read-only. "2026-09-26T21:58:25.472Z","harness:codex",Command grammar gate required noun/verb destination census and optional name positional signatures for package inventory and packages inventory. Declared both; the grammar gate now passes with 231 observed and declared paths. "2026-09-26T22:43:58.417Z","harness:codex","PR #1319 review round 1 at head 145d1edca: CodeRabbit identified three valid receipt inconsistencies. Static inventory now trims and filters enablement names like runtime discovery, uses null for absent-entry values when settings or managed metadata are untrusted, and emits a literal manifest_unreadable code so generated SDK and CLI catalogs include it. Extended existing integration cases for whitespace and untrusted absence. Rebuilt, regenerated catalogs and contracts, and passed focused 10/10, typecheck, ESLint, and snapshot checks. Hosted head checks were green before this revision; new head will rerun them and bot reviews." + "2026-09-26T22:53:33.788Z","harness:codex","PR #1319 round 2 hosted static gate found generated REFUSAL_CLOSURE_CENSUS.md stale after manifest_unreadable became a catalog code. Regenerated via pnpm contracts:agent-surfaces:update; the document now records 385 catalog codes and the explicit uncovered row. All other completed hosted checks on c50739d8c passed." learnings[1]{created_at,author,text}: "2026-09-26T21:26:36.371Z","harness:codex","A host GET route must use the static inventory primitive; runtime explore/manage/doctor/describe can import installed extension code. Read settings directly for inventory because the general settings reader may scaffold schema files. Keep configured enablement and runtime activation as separate facts, and fail incomplete reads explicitly." files[14]{path,scope,note}: diff --git a/docs/generated/REFUSAL_CLOSURE_CENSUS.md b/docs/generated/REFUSAL_CLOSURE_CENSUS.md index 41646fb17..6635d9299 100644 --- a/docs/generated/REFUSAL_CLOSURE_CENSUS.md +++ b/docs/generated/REFUSAL_CLOSURE_CENSUS.md @@ -4,12 +4,12 @@ Tracker: `pm-f05lsg`. Every catalog code is listed. An `uncovered` row is an explicit closure obligation, never an omission or implied approval. -- Catalog error codes: 384 +- Catalog error codes: 385 - Executable error codes: 19 - Executable-code ratchet floor: 18 - Required executable canonical codes: `bulk_ids_input_empty`, `bulk_ids_input_missing_path`, `bulk_ids_input_unreadable`, `invalid_argument_value`, `manifest_unknown_key`, `missing_lifecycle_target`, `missing_required_argument`, `no_version_bounds_declared`, `projection_options_mutually_exclusive`, `tracker_not_initialized`, `tracker_root_missing`, `tracker_root_not_directory`, `tracker_root_unreadable`, `unknown_context_intent`, `unknown_field_projection`, `unknown_option`, `unknown_subcommand` -- Uncovered error codes: 365 -- Coverage fraction: 0.049479 +- Uncovered error codes: 366 +- Coverage fraction: 0.049351 - Closed-domain probes: 19 - Grammar probes: 117 @@ -195,6 +195,7 @@ Every catalog code is listed. An `uncovered` row is an explicit closure obligati | `manifest_capabilities_absent` | `manifest_capabilities_absent` | uncovered | none | 0 | | `manifest_invalid` | `manifest_invalid` | uncovered | none | 0 | | `manifest_unknown_key` | `manifest_unknown_key` | executable | owned_state | 1 | +| `manifest_unreadable` | `manifest_unreadable` | uncovered | none | 0 | | `mcp_annotation_file_unavailable` | `mcp_annotation_file_unavailable` | uncovered | none | 0 | | `mcp_stdin_unavailable` | `mcp_stdin_unavailable` | uncovered | none | 0 | | `mcp_task_not_found_or_not_authorized` | `mcp_task_not_found_or_not_authorized` | uncovered | none | 0 |