From 0fba88ae43db1399b2835e02c68947c1d3afe065 Mon Sep 17 00:00:00 2001 From: Stefan Preu Date: Tue, 29 Sep 2026 08:54:56 +0200 Subject: [PATCH 1/2] fix(sdk): audit extension settings and item JSON updates across runtimes Expose a host-bound settings mutation primitive to extension commands. Apply validated changes under the workspace-history lock, support dry-run and idempotent retries, and attribute events to the invoking actor. Reject full-item JSON updates that modify persisted comments, notes, or learnings. Check the submitted snapshot under the item lock so annotation-only edits cannot return a successful no-op. Install and audit an absolute Bun merge-driver launcher alongside Node. Cover packed Bun and copied-extension workflows, record the three issue resolutions in pm, and regenerate the SDK contracts and changelog. --- .../pm/extensions/.managed-extensions.json | 2 +- .agents/pm/history/pm-2589e6.jsonl | 14 +++ .agents/pm/history/pm-vks66s.jsonl | 13 +++ .agents/pm/history/pm-wtqltn.jsonl | 16 +++ .agents/pm/issues/pm-2589e6.toon | 43 ++++++- .agents/pm/issues/pm-vks66s.toon | 39 ++++++- .agents/pm/issues/pm-wtqltn.toon | 49 +++++++- CHANGELOG.md | 8 ++ docs/MERGE_SAFETY.md | 17 ++- docs/README.md | 1 + docs/SDK_AUDITED_MUTATIONS.md | 47 ++++++++ docs/generated/REFUSAL_CLOSURE_CENSUS.md | 7 +- sdk/public-surface.json | 31 ++++-- src/core/extensions/extension-types.ts | 9 ++ src/core/history/workspace-history.ts | 37 +++++- src/sdk/extension-command-context.ts | 61 ++++++++++ .../generated-error-code-catalog-part-2.ts | 13 +++ src/sdk/lifecycle/update.ts | 34 ++++++ src/sdk/merge/install.ts | 20 +++- src/sdk/runtime.ts | 10 +- src/sdk/structured-mutations.ts | 15 +++ tests/fixtures/contracts/full.json | 17 +++ ...tension-host-contracts.integration.spec.ts | 57 ++++++++++ ...structured-mutation-io.integration.spec.ts | 19 ++++ .../history/operation-source-contract.spec.ts | 2 +- .../core/history/workspace-history.spec.ts | 70 ++++++++++++ tests/unit/sdk/merge/worktree-config.spec.ts | 31 +++++- .../sdk-extension-mutation-platform.spec.ts | 105 +++++++++++++++++- 28 files changed, 744 insertions(+), 43 deletions(-) create mode 100644 docs/SDK_AUDITED_MUTATIONS.md diff --git a/.agents/pm/extensions/.managed-extensions.json b/.agents/pm/extensions/.managed-extensions.json index 8410323a4d..02721f7c8f 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-28T23:41:46.999Z", + "updated_at": "2026-09-29T06:51:40.338Z", "entries": [ { "name": "pm-changelog", diff --git a/.agents/pm/history/pm-2589e6.jsonl b/.agents/pm/history/pm-2589e6.jsonl index e2c0731a73..29771b9dc8 100644 --- a/.agents/pm/history/pm-2589e6.jsonl +++ b/.agents/pm/history/pm-2589e6.jsonl @@ -1,3 +1,17 @@ {"hash_algorithm":"sha256","ts":"2026-09-29T00:19:58.543Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"2792d5841519ba2b16ce4d24","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":"replace","path":"/body","value":"GitHub: https://github.com/unbraind/pm-cli/issues/1343\\n\\nReproduce with a disposable tracker: create an item with a note, get the full JSON item, edit notes[0].text, and pipe it to update --stdin-json. Require either an atomic audited note correction or a nonzero field-specific rejection with pm notes --edit guidance."},{"op":"add","path":"/metadata/id","value":"pm-2589e6"},{"op":"add","path":"/metadata/title","value":"Full-item stdin JSON update silently ignores edited notes"},{"op":"add","path":"/metadata/description","value":"Published 2026.9.28 returns changed_field_count=0 for a changed notes array while preserving the old note; successful no-op conceals stale evidence."},{"op":"add","path":"/metadata/type","value":"Issue"},{"op":"add","path":"/metadata/status","value":"open"},{"op":"add","path":"/metadata/priority","value":2},{"op":"add","path":"/metadata/tags","value":[]},{"op":"add","path":"/metadata/created_at","value":"2026-09-29T00:19:58.543Z"},{"op":"add","path":"/metadata/updated_at","value":"2026-09-29T00:19:58.543Z"},{"op":"add","path":"/metadata/author","value":"harness:codex"},{"op":"add","path":"/metadata/parent","value":"pm-mpbb"},{"op":"add","path":"/metadata/severity","value":"medium"},{"op":"add","path":"/metadata/expected_result","value":"Changed notes are applied with history or explicitly rejected."},{"op":"add","path":"/metadata/actual_result","value":"Update succeeds with changed_field_count=0 and retains the original note."},{"op":"add","path":"/metadata/affected_version","value":"2026.9.28"},{"op":"add","path":"/metadata/component","value":"CLI structured mutations"},{"op":"add","path":"/metadata/dependencies","value":[{"id":"pm-a2h3","kind":"related","created_at":"2026-09-29T00:19:58.543Z","author":"harness:codex","source_kind":"cli:create:dep","author_source":"detected"},{"id":"pm-kipd","kind":"discovered_from","created_at":"2026-09-29T00:19:58.543Z","author":"harness:codex","source_kind":"cli:create:dep","author_source":"detected"}]},{"op":"add","path":"/metadata/comments","value":[{"created_at":"2026-09-29T00:19:58.543Z","author":"harness:codex","text":"Duplicate check: searched all-status PM items for stdin-json and notes; closed pm-kipd delivered the original round-trip feature and pm-a2h3 delivered direct note editing, but neither tracks this new silent no-op defect. GitHub issue #1343 has no prior PM link."}]}],"before_hash":"3cc22dff72be7b14824654a7a64ea62b04799939b2fee54c1b5f52ca60bf6df0","after_hash":"852ac78111a1107df911c94384e575d238250f6d52dd1be76992d236dc4964c6","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":"1529e2a782e86db4a90c0eaaafcfd52187a20e7f33587b6a3635790685b0452d"} {"hash_algorithm":"sha256","ts":"2026-09-29T00:21:27.747Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"2792d5841519ba2b16ce4d24","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":"/body","value":"GitHub: https://github.com/unbraind/pm-cli/issues/1343\n\nReproduce with a disposable tracker: create an item with a note, get the full JSON item, edit notes[0].text, and pipe it to update --stdin-json. Require either an atomic audited note correction or a nonzero field-specific rejection with pm notes --edit guidance."},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T00:21:27.747Z"}],"before_hash":"852ac78111a1107df911c94384e575d238250f6d52dd1be76992d236dc4964c6","after_hash":"95d88a11fc9931db014987bfcf0c09014962ce78850038bbf50289c7ce864aa2","item_hash_version":3,"message":"Format GitHub intake body","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":"2555eec5bf7e28d83fe6e2ea65078cc71104bd718a7014d0792943f11986466b"} {"hash_algorithm":"sha256","ts":"2026-09-29T00:31:43.731Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"2792d5841519ba2b16ce4d24","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-29T00:31:43.731Z"},{"op":"add","path":"/metadata/estimated_minutes","value":180},{"op":"add","path":"/metadata/acceptance_criteria","value":"Changed notes are atomically audited or rejected with a field-specific error; Disposable get-modify-update regression proves no false successful no-op"},{"op":"add","path":"/metadata/risk","value":"medium"},{"op":"add","path":"/metadata/confidence","value":85}],"before_hash":"95d88a11fc9931db014987bfcf0c09014962ce78850038bbf50289c7ce864aa2","after_hash":"9b536e9e5f17e250a61ecf2415c14ba7dc79667a98463866c088d14aa9376638","item_hash_version":3,"message":"Complete active-issue planning fields required by static quality gate","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":"8ec92a8b0bfa30306e87b7601d5f4cbef7543e86b733e5d58fc3c0a3b7d6d086"} +{"hash_algorithm":"sha256","ts":"2026-09-29T04:54:09.935Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-29T04:54:09.935Z"},{"op":"add","path":"/metadata/assignee","value":"harness:codex"},{"op":"add","path":"/metadata/claim_principal","value":"harness:codex#4035266b6ea871edfae828b3"}],"before_hash":"9b536e9e5f17e250a61ecf2415c14ba7dc79667a98463866c088d14aa9376638","after_hash":"55cb3421f157990270429bfe3f0683eedf2177613a648d1b758b720267503af1","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":"bbea0f0a6ac00206f55398df383f2e42a8304c9f9bd9cb16154561de1595517c"} +{"hash_algorithm":"sha256","ts":"2026-09-29T04:54:10.123Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","lineage:pm-2589e6","lineage:pm-mpbb","lineage:pm-doxj"]},"topic":{"value":"pm-2589e6","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","lineage:pm-2589e6","lineage:pm-mpbb","lineage:pm-doxj"]}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T04:54:10.123Z"},{"op":"replace","path":"/metadata/status","value":"in_progress"}],"before_hash":"55cb3421f157990270429bfe3f0683eedf2177613a648d1b758b720267503af1","after_hash":"04b6c82b3c18f494b3da7b25fa7c768b6ee031d12fb06b5006c9f7b0a6ff4eff","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":"1466d2b3b0ac4e04c59b230d07b2900926ae3727e164a0b3c9c70ed7bb9cee25"} +{"hash_algorithm":"sha256","ts":"2026-09-29T04:56:30.520Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/1","value":{"created_at":"2026-09-29T04:56:30.520Z","author":"harness:codex","text":"Test-audit contract: a changed persisted note in get->modify->update --stdin-json must never return a successful no-op. Existing structured round-trip test only appends a new note and does not detect edits to a persisted annotation. Primary proof will use a disposable tracker and history verification; no test-only production seam."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T04:56:30.520Z"}],"before_hash":"04b6c82b3c18f494b3da7b25fa7c768b6ee031d12fb06b5006c9f7b0a6ff4eff","after_hash":"ee99a4804e57fd250d7850b94470849da1bb41308d7c6aeb447bf800b13ad3ef","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":"1d2c0ced82081cf750237c8f2308b3fa65de9474f550c7a53111bf222d554585"} +{"hash_algorithm":"sha256","ts":"2026-09-29T05:13:53.840Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T05:13:53.840Z"},{"op":"add","path":"/metadata/files","value":[{"path":"src/sdk/lifecycle/update.ts","scope":"project","note":"locked annotation integrity"},{"path":"src/sdk/structured-mutations.ts","scope":"project","note":"full-item annotation snapshot"},{"path":"tests/integration/structured-mutation-io.integration.spec.ts","scope":"project","note":"regression acceptance"}]}],"before_hash":"ee99a4804e57fd250d7850b94470849da1bb41308d7c6aeb447bf800b13ad3ef","after_hash":"72c92db25a31ef78d3fa8ea5d18f3911002d0a102d5cad668fc9b80b66b0f92a","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":"80f4f0da7ee3772f837e9fe34e43e0ef6a7533118f328a32edf2882c3fc43108"} +{"hash_algorithm":"sha256","ts":"2026-09-29T05:13:54.936Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"docs_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T05:13:54.936Z"},{"op":"add","path":"/metadata/docs","value":[{"path":"docs/README.md","scope":"project","note":"documentation index"},{"path":"docs/SDK_AUDITED_MUTATIONS.md","scope":"project","note":"full-item JSON correction contract"}]}],"before_hash":"72c92db25a31ef78d3fa8ea5d18f3911002d0a102d5cad668fc9b80b66b0f92a","after_hash":"caf7040435e52933797b1c0ce4ea4d7d8f07f2e7cf00da928071ab709230769a","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":"a7ee2c0e23ca873b4fa30f57985d4723cd8e44941fa8ddd352a9405a2e83d0ef"} +{"hash_algorithm":"sha256","ts":"2026-09-29T05:13:56.059Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"tests_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T05:13:56.059Z"},{"op":"add","path":"/metadata/tests","value":[{"command":"node scripts/run-tests.mjs test -- tests/integration/structured-mutation-io.integration.spec.ts","scope":"project","timeout_seconds":600,"provenance":{"author":"harness:codex","created_at":"2026-09-29T05:13:55.996Z","source_kind":"local_mutation","source_ref":"fix/audited-workspace-mutations-bun-merge"}}]}],"before_hash":"caf7040435e52933797b1c0ce4ea4d7d8f07f2e7cf00da928071ab709230769a","after_hash":"70d3577ceb09f51a194a10f48d2f9f91134a619a5d59f7342fea39a1303cf641","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":"4f078f862c77b2a05b6dea0d3fe3d202328b179f121877adb879ff35b4291d1c"} +{"hash_algorithm":"sha256","ts":"2026-09-29T06:15:39.764Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/2/note","value":"full-item annotation snapshot"},{"op":"replace","path":"/metadata/files/2/path","value":"src/sdk/structured-mutations.ts"},{"op":"replace","path":"/metadata/files/1/note","value":"locked annotation integrity"},{"op":"replace","path":"/metadata/files/1/path","value":"src/sdk/lifecycle/update.ts"},{"op":"replace","path":"/metadata/files/0/note","value":"stable annotation refusal catalog"},{"op":"replace","path":"/metadata/files/0/path","value":"src/sdk/generated/generated-error-code-catalog-part-2.ts"},{"op":"add","path":"/metadata/files/3","value":{"path":"tests/fixtures/contracts/full.json","scope":"project","note":"generated command contract snapshot"}},{"op":"add","path":"/metadata/files/4","value":{"path":"tests/integration/structured-mutation-io.integration.spec.ts","scope":"project","note":"regression acceptance"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:15:39.764Z"}],"before_hash":"70d3577ceb09f51a194a10f48d2f9f91134a619a5d59f7342fea39a1303cf641","after_hash":"6551a41a6eb95349e0152c77f3ffe961c040bc356510173bdab4ee5daa0ea1a9","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":"5a3eb486d74759b8f689c02707cd03e05fa788bcf70f37841cfeb0d7aa45740f"} +{"hash_algorithm":"sha256","ts":"2026-09-29T06:15:40.492Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"docs_add","patch":[{"op":"replace","path":"/metadata/docs/1/note","value":"documentation index"},{"op":"replace","path":"/metadata/docs/1/path","value":"docs/README.md"},{"op":"replace","path":"/metadata/docs/0/note","value":"generated refusal recovery contract"},{"op":"replace","path":"/metadata/docs/0/path","value":"docs/generated/REFUSAL_CLOSURE_CENSUS.md"},{"op":"add","path":"/metadata/docs/2","value":{"path":"docs/SDK_AUDITED_MUTATIONS.md","scope":"project","note":"full-item JSON correction contract"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:15:40.492Z"}],"before_hash":"6551a41a6eb95349e0152c77f3ffe961c040bc356510173bdab4ee5daa0ea1a9","after_hash":"eb9cc97ec321d980e9bfb54a089e683e623ebfb7dc7e7aae80c975da5754ae74","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":"64858e68d599cd24add7647e53836c2343e6b716d9079af79717d7ca07a3417c"} +{"hash_algorithm":"sha256","ts":"2026-09-29T06:47:49.249Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"test_run_track","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:47:49.249Z"},{"op":"add","path":"/metadata/test_runs","value":[{"run_id":"test-local-mumbe394-q4gosn","kind":"test","status":"passed","started_at":"2026-09-29T06:47:32.094Z","finished_at":"2026-09-29T06:47:49.240Z","recorded_at":"2026-09-29T06:47:49.240Z","passed":1,"failed":0,"skipped":0,"executions":[{"command":"node scripts/run-tests.mjs test -- tests/integration/structured-mutation-io.integration.spec.ts","requested_pm_context_mode":"schema","pm_context_mode":"schema","workspace_context_mode":"source","trust_reason":"local_source_ref"}]}]}],"before_hash":"eb9cc97ec321d980e9bfb54a089e683e623ebfb7dc7e7aae80c975da5754ae74","after_hash":"9eded018aad519d8dbcf2a6c995b0af813ce92cd4f4d3687b59e1e93c1588aec","item_hash_version":3,"message":"Track test run summary (test-local-mumbe394-q4gosn)","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":"474d7400b4827e5a8353ac0d018d80ba3d47a521ae3f297df5497b8f5278676c"} +{"hash_algorithm":"sha256","ts":"2026-09-29T06:50:36.685Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/2","value":{"created_at":"2026-09-29T06:50:36.685Z","author":"harness:codex","text":"Acceptance evidence: full-item get to update --stdin-json now preserves persisted comments, notes, and learnings as immutable snapshots and rejects edited or removed entries under the item lock with stdin_json_persisted_annotation_changed. The structured-mutation integration test proves the formerly silent edited-note no-op fails without changing item or history, while legitimate new annotations still append. Linked test passed; full suite 9414 passed; exact V8 coverage 100/100/100/100; pnpm quality:static passed. Manual correction remains pm notes/comments/learnings --edit with audit history."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:50:36.685Z"}],"before_hash":"9eded018aad519d8dbcf2a6c995b0af813ce92cd4f4d3687b59e1e93c1588aec","after_hash":"dbad093f9123c800696d5b7e432321092aabe0a5b50ca7bef7c4b9992b45d37c","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":"e317245f3aff0fb6716f9c8b2ea31cf51e73a8d6faea04a0e467dcfa5e34e7a0"} +{"hash_algorithm":"sha256","ts":"2026-09-29T06:50:37.375Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"learning_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:50:37.375Z"},{"op":"add","path":"/metadata/learnings","value":[{"created_at":"2026-09-29T06:50:37.375Z","author":"harness:codex","text":"A full-item JSON round trip must distinguish new annotation entries from persisted audit entries and verify the persisted snapshot inside the locked mutation, including annotation-only updates."}]}],"before_hash":"dbad093f9123c800696d5b7e432321092aabe0a5b50ca7bef7c4b9992b45d37c","after_hash":"1056074ba76b888bb68bc59166a421f48c56a84f350947a4af393fb8b3d7ed87","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":"5b513aea3b65d2566e8686fe6aff7ea3b1549cb28600ae70b82d33facf92af63"} +{"hash_algorithm":"sha256","ts":"2026-09-29T06:50:53.644Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"close","patch":[{"op":"replace","path":"/metadata/actual_result","value":"Disposable structured-mutation regression rejects edited note with unchanged verified history; linked test, full suite, exact coverage, and static quality pass."},{"op":"replace","path":"/metadata/expected_result","value":"Edited existing notes cannot silently succeed or change item state."},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:50:53.644Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/closed_at","value":"2026-09-29T06:50:53.619Z"},{"op":"add","path":"/metadata/completed_at","value":"2026-09-29T06:50:53.619Z"},{"op":"add","path":"/metadata/resolution","value":"Full-item JSON updates now compare persisted comments, notes, and learnings under the item lock and return a stable actionable refusal for edits or removals; new entries still append."},{"op":"add","path":"/metadata/close_reason","value":"Reject persisted annotation edits in full-item JSON updates"}],"before_hash":"1056074ba76b888bb68bc59166a421f48c56a84f350947a4af393fb8b3d7ed87","after_hash":"f9ecc88fc6f648bff3bcb2ef7012c3bc137f287196019198810f1b2292772450","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":"b751179dea2adbe715859a81caa96c58f83ab9bf04052c3787f21cefd69a92bc"} +{"hash_algorithm":"sha256","ts":"2026-09-29T06:50:54.234Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:50:54.234Z"}],"before_hash":"f9ecc88fc6f648bff3bcb2ef7012c3bc137f287196019198810f1b2292772450","after_hash":"4293eec66cf0444495919842a264ca028f610203c44a40db6a9d1a6cea04bed4","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":"601700e456bc705604594176db45533ba8124c4b1f71803f54c899be3ebe6dea"} +{"hash_algorithm":"sha256","ts":"2026-09-29T06:53:29.820Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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/2/note","value":"documentation index"},{"op":"replace","path":"/metadata/docs/2/path","value":"docs/README.md"},{"op":"replace","path":"/metadata/docs/1/note","value":"generated refusal recovery contract"},{"op":"replace","path":"/metadata/docs/1/path","value":"docs/generated/REFUSAL_CLOSURE_CENSUS.md"},{"op":"replace","path":"/metadata/docs/0/note","value":"generated release entry"},{"op":"replace","path":"/metadata/docs/0/path","value":"CHANGELOG.md"},{"op":"add","path":"/metadata/docs/3","value":{"path":"docs/SDK_AUDITED_MUTATIONS.md","scope":"project","note":"full-item JSON correction contract"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:53:29.820Z"}],"before_hash":"4293eec66cf0444495919842a264ca028f610203c44a40db6a9d1a6cea04bed4","after_hash":"641f44ae5fa16c25bf1a087b6c666259291d206dab7259c9cf00dbab82585a60","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":"b323cea78d6758d1e44df84b5b6ef44a17bd0815c96cebce0d3973fc076da75b"} diff --git a/.agents/pm/history/pm-vks66s.jsonl b/.agents/pm/history/pm-vks66s.jsonl index 207c7e16b4..9f8984c11e 100644 --- a/.agents/pm/history/pm-vks66s.jsonl +++ b/.agents/pm/history/pm-vks66s.jsonl @@ -1,3 +1,16 @@ {"hash_algorithm":"sha256","ts":"2026-09-29T00:20:04.660Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"2792d5841519ba2b16ce4d24","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":"replace","path":"/body","value":"GitHub: https://github.com/unbraind/pm-cli/issues/1346\\n\\nReproduce in a disposable Bun Git project; compare installed Git driver commands against strict health expectation, prove idempotent reinstall, and execute a real two-branch item merge using the registered driver."},{"op":"add","path":"/metadata/id","value":"pm-vks66s"},{"op":"add","path":"/metadata/title","value":"Bun merge-driver installation disagrees with strict health"},{"op":"add","path":"/metadata/description","value":"Published 2026.9.28 bunx --bun init and merge install configure merge drivers that strict health immediately reports drifted, even after reinstall."},{"op":"add","path":"/metadata/type","value":"Issue"},{"op":"add","path":"/metadata/status","value":"open"},{"op":"add","path":"/metadata/priority","value":2},{"op":"add","path":"/metadata/tags","value":[]},{"op":"add","path":"/metadata/created_at","value":"2026-09-29T00:20:04.660Z"},{"op":"add","path":"/metadata/updated_at","value":"2026-09-29T00:20:04.660Z"},{"op":"add","path":"/metadata/author","value":"harness:codex"},{"op":"add","path":"/metadata/parent","value":"pm-u9d0"},{"op":"add","path":"/metadata/severity","value":"high"},{"op":"add","path":"/metadata/expected_result","value":"Bun init registers stable, executable merge drivers accepted by strict health."},{"op":"add","path":"/metadata/actual_result","value":"All five driver keys are reported drifted after init and merge install."},{"op":"add","path":"/metadata/affected_version","value":"2026.9.28"},{"op":"add","path":"/metadata/component","value":"Bun merge driver"},{"op":"add","path":"/metadata/dependencies","value":[{"id":"pm-rnl3sa","kind":"related","created_at":"2026-09-29T00:20:04.660Z","author":"harness:codex","source_kind":"cli:create:dep","author_source":"detected"}]},{"op":"add","path":"/metadata/comments","value":[{"created_at":"2026-09-29T00:20:04.660Z","author":"harness:codex","text":"Duplicate check: all-status PM search for Bun merge-driver drift found the closed package-runner dispatch issue pm-rnl3sa and other merge work, but no owner for this Bun-specific install/health mismatch."}]}],"before_hash":"3cc22dff72be7b14824654a7a64ea62b04799939b2fee54c1b5f52ca60bf6df0","after_hash":"f8b55a13591bdad46be8ef9e61ce5670d2c4a2477cc2c91cbaaf7b8e559129bf","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":"277992b3acf88ba7607f3e2c6624ed2594c57b6157e970703c05c6255287f43d"} {"hash_algorithm":"sha256","ts":"2026-09-29T00:21:29.023Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"2792d5841519ba2b16ce4d24","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":"/body","value":"GitHub: https://github.com/unbraind/pm-cli/issues/1346\n\nReproduce in a disposable Bun Git project; compare installed Git driver commands against strict health expectation, prove idempotent reinstall, and execute a real two-branch item merge using the registered driver."},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T00:21:29.023Z"}],"before_hash":"f8b55a13591bdad46be8ef9e61ce5670d2c4a2477cc2c91cbaaf7b8e559129bf","after_hash":"9cf104fca6b1b4a06250dd75f275c0cd201d16c7c5cd29c9e4cbf7e110ed4ac0","item_hash_version":3,"message":"Format GitHub intake body","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":"4a6f4f65612519f8b4bc363bf300599b88dfe81ac2c4f6f4df2cecae05df2d1e"} {"hash_algorithm":"sha256","ts":"2026-09-29T00:31:44.903Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"2792d5841519ba2b16ce4d24","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-29T00:31:44.903Z"},{"op":"add","path":"/metadata/estimated_minutes","value":240},{"op":"add","path":"/metadata/acceptance_criteria","value":"Bun init and merge install pass strict health on a fresh Git workspace; Repeated install is idempotent and a real two-branch merge executes the driver"},{"op":"add","path":"/metadata/risk","value":"high"},{"op":"add","path":"/metadata/confidence","value":85}],"before_hash":"9cf104fca6b1b4a06250dd75f275c0cd201d16c7c5cd29c9e4cbf7e110ed4ac0","after_hash":"44d2255848049a47bf85c92f4f596fac11d9debaefa485aa156a604f6d2cf62f","item_hash_version":3,"message":"Complete active-issue planning fields required by static quality gate","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":"c1273df9807160fa6400318af60193a3eda9ac1288bed34aeb44b5de0cb2314e"} +{"hash_algorithm":"sha256","ts":"2026-09-29T04:54:11.635Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-wtqltn","lineage:pm-wtqltn","lineage:pm-o2kc","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-wtqltn","lineage:pm-wtqltn","lineage:pm-o2kc","lineage:pm-doxj"]}},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T04:54:11.635Z"},{"op":"add","path":"/metadata/assignee","value":"harness:codex"},{"op":"add","path":"/metadata/claim_principal","value":"harness:codex#4035266b6ea871edfae828b3"}],"before_hash":"44d2255848049a47bf85c92f4f596fac11d9debaefa485aa156a604f6d2cf62f","after_hash":"9cc8c8a47989f0d3ad15a906fca2f8bc695e51d7b173fab9a6f02219baf924e0","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":"80e57fb3cf76af3c6a7da061ba951fa74d6f616d02fda28d38b092a112b07db4"} +{"hash_algorithm":"sha256","ts":"2026-09-29T04:54:11.815Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T04:54:11.815Z"},{"op":"replace","path":"/metadata/status","value":"in_progress"}],"before_hash":"9cc8c8a47989f0d3ad15a906fca2f8bc695e51d7b173fab9a6f02219baf924e0","after_hash":"d9b0c5ca9e50b05c1e5375296174db2930ca2e5dfe239ba7711bffe5cbfa0058","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":"1ab199e5f851f6e59d425971d377583f852a87f4f4f187a6ce9e070a34c4bd32"} +{"hash_algorithm":"sha256","ts":"2026-09-29T04:56:31.028Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/1","value":{"created_at":"2026-09-29T04:56:31.027Z","author":"harness:codex","text":"Test-audit contract: Bun-installed merge drivers must pass strict health, remain idempotent, and execute a real merge. Existing merge safety tests cover Node driver syntax and worktree identity, not Bun's executable name or bunx runtime path. Primary proof will use a disposable Git/Bun workspace."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T04:56:31.028Z"}],"before_hash":"d9b0c5ca9e50b05c1e5375296174db2930ca2e5dfe239ba7711bffe5cbfa0058","after_hash":"41c14d0459c7abb7de58222fdedb68fee695733abe148d0809bf233195c169bf","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":"eb6ecb7db7a9833e7383a1ba3d00d5ff1cd8591cc262ebf08d612289c99ba60f"} +{"hash_algorithm":"sha256","ts":"2026-09-29T05:14:00.084Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T05:14:00.084Z"},{"op":"add","path":"/metadata/files","value":[{"path":"src/sdk/merge/install.ts","scope":"project","note":"Bun launcher selection and audit"},{"path":"tests/unit/sdk/merge/worktree-config.spec.ts","scope":"project","note":"launcher regression"}]}],"before_hash":"41c14d0459c7abb7de58222fdedb68fee695733abe148d0809bf233195c169bf","after_hash":"b52c89fd8c60781fd15902e5a5eb22e0c8d9854b66273501fed472f7abcdc684","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":"f78f71bbb2d660d2065f7f7cf61cc76c1ef964536308068d624b1acf4aa8974a"} +{"hash_algorithm":"sha256","ts":"2026-09-29T05:14:01.160Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"docs_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T05:14:01.160Z"},{"op":"add","path":"/metadata/docs","value":[{"path":"docs/MERGE_SAFETY.md","scope":"project","note":"Bun merge-driver contract"}]}],"before_hash":"b52c89fd8c60781fd15902e5a5eb22e0c8d9854b66273501fed472f7abcdc684","after_hash":"11e4971ed85cb1d7ed264fd87b93a9b79355668bcc355935796e34c3267ede2c","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":"80d96a27e8ace26e68e1fee9cc02bb8d6af229309a5494931baa6ede319bdcc2"} +{"hash_algorithm":"sha256","ts":"2026-09-29T05:14:02.162Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"tests_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T05:14:02.162Z"},{"op":"add","path":"/metadata/tests","value":[{"command":"node scripts/run-tests.mjs test -- tests/unit/sdk/merge/worktree-config.spec.ts","scope":"project","timeout_seconds":600,"provenance":{"author":"harness:codex","created_at":"2026-09-29T05:14:02.131Z","source_kind":"local_mutation","source_ref":"fix/audited-workspace-mutations-bun-merge"}}]}],"before_hash":"11e4971ed85cb1d7ed264fd87b93a9b79355668bcc355935796e34c3267ede2c","after_hash":"bfc10ba70e65c8d52cb7247babb7be30335b1e6f67ebe655275e20aedf5023aa","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":"f7c133b6b94ffee3d399949735178224b69039cd2b9792fa3038a8a50b86c2ef"} +{"hash_algorithm":"sha256","ts":"2026-09-29T06:48:50.066Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"test_run_track","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:48:50.066Z"},{"op":"add","path":"/metadata/test_runs","value":[{"run_id":"test-local-mumbfe6g-zm3zsz","kind":"test","status":"passed","started_at":"2026-09-29T06:48:31.465Z","finished_at":"2026-09-29T06:48:50.056Z","recorded_at":"2026-09-29T06:48:50.056Z","passed":1,"failed":0,"skipped":0,"executions":[{"command":"node scripts/run-tests.mjs test -- tests/unit/sdk/merge/worktree-config.spec.ts","requested_pm_context_mode":"schema","pm_context_mode":"schema","workspace_context_mode":"source","trust_reason":"local_source_ref"}]}]}],"before_hash":"bfc10ba70e65c8d52cb7247babb7be30335b1e6f67ebe655275e20aedf5023aa","after_hash":"979c32ad3156da42337efca07bf7ca6cf7205c9a290cd16cbf20f7bb86a73838","item_hash_version":3,"message":"Track test run summary (test-local-mumbfe6g-zm3zsz)","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":"1a6420b9fcc76a011271a673f0856884525f227bd642a87b8efdfee0fe6d6609"} +{"hash_algorithm":"sha256","ts":"2026-09-29T06:50:40.724Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/2","value":{"created_at":"2026-09-29T06:50:40.724Z","author":"harness:codex","text":"Acceptance evidence: actual Bun 1.4.2 in a disposable npm-packed consumer installed an absolute /usr/bin/bun merge driver; bunx --bun init and repeated merge install both passed strict health with zero findings. A real Git branch merge invoked item and history drivers, retained the left note and right description, and pm history --verify returned three valid entries. npx --no-install pm and bunx --no-install pm both returned the packed CLI version. Linked regression test passed; full suite 9414 passed; exact V8 coverage 100/100/100/100; pnpm quality:static passed. Repository source npm audit found zero advisories and GitHub open Dependabot, code, and secret scanning alerts were empty."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:50:40.724Z"}],"before_hash":"979c32ad3156da42337efca07bf7ca6cf7205c9a290cd16cbf20f7bb86a73838","after_hash":"86a9b479a56082417ed2752ff7fcea8f7ce9025d3e34fb5e16764b32473a3252","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":"f5c76ccfddb6272ca8a5beff58ebe9e43b73132ec9148ce00bb33bdaf1d88e48"} +{"hash_algorithm":"sha256","ts":"2026-09-29T06:50:41.415Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"learning_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:50:41.415Z"},{"op":"add","path":"/metadata/learnings","value":[{"created_at":"2026-09-29T06:50:41.415Z","author":"harness:codex","text":"Bun can report a versioned executable path while Git later needs a durable absolute launcher; resolve and validate bun from absolute PATH entries at install time and audit both Node and Bun driver identities."}]}],"before_hash":"86a9b479a56082417ed2752ff7fcea8f7ce9025d3e34fb5e16764b32473a3252","after_hash":"2b4c266c4f7c1835228f866105b1384d8f37b357c6ae41738c18ed9d51b0eb2e","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":"8cae23ffa0d7d26f303c7a2bf993c85b7ff7c9447cc2957be4d540be2c5cdf46"} +{"hash_algorithm":"sha256","ts":"2026-09-29T06:50:57.079Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-vks66s","release:pm-wtqltn"]},"topic":{"value":"pm-vks66s","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-vks66s","release:pm-wtqltn"]}},"op":"close","patch":[{"op":"replace","path":"/metadata/actual_result","value":"Disposable packed Bun consumer passed strict health twice and real item/history Git merge with verified chain; npx and bunx executable checks, linked test, full suite, exact coverage, and static quality pass."},{"op":"replace","path":"/metadata/expected_result","value":"bunx-installed pm passes strict merge-driver health and performs semantic Git merges."},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:50:57.079Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/closed_at","value":"2026-09-29T06:50:57.064Z"},{"op":"add","path":"/metadata/completed_at","value":"2026-09-29T06:50:57.064Z"},{"op":"add","path":"/metadata/resolution","value":"Merge installation now selects a version-matched stable absolute Bun launcher and driver audit accepts valid Node or Bun identities."},{"op":"add","path":"/metadata/close_reason","value":"Repair Bun merge-driver launcher identity"}],"before_hash":"2b4c266c4f7c1835228f866105b1384d8f37b357c6ae41738c18ed9d51b0eb2e","after_hash":"3ad6fcc5cf4f6a755467b3e4d1e9a1182bc96daf2023f6b03965aa0d10ca6491","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":"3dbf3d83924976174c47134f9cb7302f2ffc89bfaf8c832ee51346fa3dd21dff"} +{"hash_algorithm":"sha256","ts":"2026-09-29T06:50:57.744Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-vks66s","release:pm-wtqltn"]},"topic":{"value":"pm-vks66s","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-vks66s","release:pm-wtqltn"]}},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:50:57.744Z"}],"before_hash":"3ad6fcc5cf4f6a755467b3e4d1e9a1182bc96daf2023f6b03965aa0d10ca6491","after_hash":"439947347e270f0e75c7a41fa6a36720ccc85fcfb0d52ffb4c82075a4e4676ea","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":"69b29a8ec9d089a92393ab0f1d56b47d31f118eb3601a28b16f944522fd306ab"} +{"hash_algorithm":"sha256","ts":"2026-09-29T06:53:31.228Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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 release entry"},{"op":"replace","path":"/metadata/docs/0/path","value":"CHANGELOG.md"},{"op":"add","path":"/metadata/docs/1","value":{"path":"docs/MERGE_SAFETY.md","scope":"project","note":"Bun merge-driver contract"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:53:31.228Z"}],"before_hash":"439947347e270f0e75c7a41fa6a36720ccc85fcfb0d52ffb4c82075a4e4676ea","after_hash":"c65008574240b5cca12f2bc658fa71ae44e2491b6af707b1d1961087d61cd02b","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":"b437ebc01a0122ad745313ae889000266bd41b3fe8055bcd8da30d087defa805"} +{"hash_algorithm":"sha256","ts":"2026-09-29T06:58:37.384Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-29T06:58:37.384Z","author":"harness:codex","text":"Post-rebase packed acceptance on v2026.9.29: fresh tarball installed with Bun in a disposable Git project, bunx --bun pm init succeeded, bunx --bun pm health --strict-exit reported ok with zero findings, and both npx --no-install pm --version and bunx --no-install pm --version returned 2026.9.29. The earlier disposable project also completed a real semantic item/history Git merge with a verified chain."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:58:37.384Z"}],"before_hash":"c65008574240b5cca12f2bc658fa71ae44e2491b6af707b1d1961087d61cd02b","after_hash":"c6fec14a8858fe970d5527645b900bef1cf52127a943ba6665e2b8e1905140cf","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":"a964a8edeb9c75d6793eb2141c68d89c24449b853b92d1cfadd7230753496567"} diff --git a/.agents/pm/history/pm-wtqltn.jsonl b/.agents/pm/history/pm-wtqltn.jsonl index 239beb0b0a..ee22a1e434 100644 --- a/.agents/pm/history/pm-wtqltn.jsonl +++ b/.agents/pm/history/pm-wtqltn.jsonl @@ -1,3 +1,19 @@ {"hash_algorithm":"sha256","ts":"2026-09-29T00:20:00.805Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"2792d5841519ba2b16ce4d24","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":"replace","path":"/body","value":"GitHub: https://github.com/unbraind/pm-cli/issues/1345\\n\\nExpose a typed extension-command SDK operation for lock-scoped audited singleton mutation with invocation identity, atomic write and history, compensation, idempotence, and dry-run. Prove a packed extension without a local host SDK dependency can apply settings and pass strict health."},{"op":"add","path":"/metadata/id","value":"pm-wtqltn"},{"op":"add","path":"/metadata/title","value":"Expose host-bound audited workspace settings mutation to extension commands"},{"op":"add","path":"/metadata/description","value":"A packed extension can apply settings directly but lacks a portable host SDK transaction that preserves the workspace audit chain, leaving strict health with settings history drift."},{"op":"add","path":"/metadata/type","value":"Issue"},{"op":"add","path":"/metadata/status","value":"open"},{"op":"add","path":"/metadata/priority","value":2},{"op":"add","path":"/metadata/tags","value":[]},{"op":"add","path":"/metadata/created_at","value":"2026-09-29T00:20:00.805Z"},{"op":"add","path":"/metadata/updated_at","value":"2026-09-29T00:20:00.805Z"},{"op":"add","path":"/metadata/author","value":"harness:codex"},{"op":"add","path":"/metadata/parent","value":"pm-o2kc"},{"op":"add","path":"/metadata/severity","value":"high"},{"op":"add","path":"/metadata/expected_result","value":"Extension settings updates are atomic, audited and pass strict health."},{"op":"add","path":"/metadata/actual_result","value":"pm-presets applies directly to settings.json and strict health reports workspace history drift."},{"op":"add","path":"/metadata/affected_version","value":"2026.9.28"},{"op":"add","path":"/metadata/component","value":"SDK extension command context"},{"op":"add","path":"/metadata/dependencies","value":[{"id":"pm-klo8","kind":"discovered_from","created_at":"2026-09-29T00:20:00.805Z","author":"harness:codex","source_kind":"cli:create:dep","author_source":"detected"},{"id":"pm-lvea","kind":"related","created_at":"2026-09-29T00:20:00.805Z","author":"harness:codex","source_kind":"cli:create:dep","author_source":"detected"}]},{"op":"add","path":"/metadata/comments","value":[{"created_at":"2026-09-29T00:20:00.805Z","author":"harness:codex","text":"Duplicate check: all-status PM search for audited extension settings mutation found related closed workspace-audit and SDK audits but no current owner of this missing host-bound contract. GitHub #1345 cites pm-presets pm-1vx6 in that package's tracker; that is a separate consumer defect."}]}],"before_hash":"3cc22dff72be7b14824654a7a64ea62b04799939b2fee54c1b5f52ca60bf6df0","after_hash":"3e36c81d4a9eb097d8aee4b806af3a62ff88f8f53e9938a98f5679407dd8dfa4","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":"4fc3f631ad5e125f1a9b913d2e15f694e563cd734b8f1869813e1c223f1f5c08"} {"hash_algorithm":"sha256","ts":"2026-09-29T00:21:28.391Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"2792d5841519ba2b16ce4d24","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":"/body","value":"GitHub: https://github.com/unbraind/pm-cli/issues/1345\n\nExpose a typed extension-command SDK operation for lock-scoped audited singleton mutation with invocation identity, atomic write and history, compensation, idempotence, and dry-run. Prove a packed extension without a local host SDK dependency can apply settings and pass strict health."},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T00:21:28.391Z"}],"before_hash":"3e36c81d4a9eb097d8aee4b806af3a62ff88f8f53e9938a98f5679407dd8dfa4","after_hash":"234e3d3d14502b18bf3686a8099a9ffc0f91845c86ebe09d1a5776df91a67a07","item_hash_version":3,"message":"Format GitHub intake body","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":"f34a9e057761fc3a0785856b694646b37c8a39cfddcac7dc58181d995afe0786"} {"hash_algorithm":"sha256","ts":"2026-09-29T00:31:44.311Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"2792d5841519ba2b16ce4d24","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-29T00:31:44.311Z"},{"op":"add","path":"/metadata/estimated_minutes","value":360},{"op":"add","path":"/metadata/acceptance_criteria","value":"Host-bound extension SDK mutation writes settings and workspace history atomically; Packed extension without local CLI SDK dependency passes strict health and rollback tests"},{"op":"add","path":"/metadata/risk","value":"high"},{"op":"add","path":"/metadata/confidence","value":80}],"before_hash":"234e3d3d14502b18bf3686a8099a9ffc0f91845c86ebe09d1a5776df91a67a07","after_hash":"72eb1e548977d102db08f7026123c5efa53ff8f6061ba87582722981b2e849c7","item_hash_version":3,"message":"Complete active-issue planning fields required by static quality gate","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":"6cf295cdecb1581714438506f4cef8e9d08cbba48a678fdec678341b68bf9e61"} +{"hash_algorithm":"sha256","ts":"2026-09-29T04:54:10.773Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","lineage:pm-2589e6","lineage:pm-mpbb","lineage:pm-doxj"]},"topic":{"value":"pm-2589e6","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","lineage:pm-2589e6","lineage:pm-mpbb","lineage:pm-doxj"]}},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T04:54:10.773Z"},{"op":"add","path":"/metadata/assignee","value":"harness:codex"},{"op":"add","path":"/metadata/claim_principal","value":"harness:codex#4035266b6ea871edfae828b3"}],"before_hash":"72eb1e548977d102db08f7026123c5efa53ff8f6061ba87582722981b2e849c7","after_hash":"01ef03b71f4883682be139421a30ab082ed67285b99b362700f119f0411ca078","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":"34322edfb485c9744daf233eed685bad801b7fe30fd3e7d09d3decd9e8ccb5ee"} +{"hash_algorithm":"sha256","ts":"2026-09-29T04:54:10.951Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-wtqltn","lineage:pm-wtqltn","lineage:pm-o2kc","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-wtqltn","lineage:pm-wtqltn","lineage:pm-o2kc","lineage:pm-doxj"]}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T04:54:10.951Z"},{"op":"replace","path":"/metadata/status","value":"in_progress"}],"before_hash":"01ef03b71f4883682be139421a30ab082ed67285b99b362700f119f0411ca078","after_hash":"520a44f70547ee6c6e50b85f60c12f2ee16e59dd3ab7e84c067f68d86e3483ea","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":"55a454e76dbd30fa92a06c1cf31724b211379ec3fc3443e751a987ff043f8fec"} +{"hash_algorithm":"sha256","ts":"2026-09-29T04:56:31.683Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/1","value":{"created_at":"2026-09-29T04:56:31.683Z","author":"harness:codex","text":"Test-audit contract: a copied extension with no locally resolvable pm SDK can atomically mutate governed settings through its injected host SDK while retaining strict workspace history. Existing direct SDK singleton tests do not prove extension portability or failure compensation. Primary proof will use a disposable packed consumer."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T04:56:31.683Z"}],"before_hash":"520a44f70547ee6c6e50b85f60c12f2ee16e59dd3ab7e84c067f68d86e3483ea","after_hash":"09385b35d9d7ad5aab7cbb96545a1faea9e8b6b89acb7b093e374cfb162a521d","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":"0923ccf468177daf71d72eebe4942a29deeceeb933317d54643f11808c575671"} +{"hash_algorithm":"sha256","ts":"2026-09-29T05:13:57.134Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T05:13:57.134Z"},{"op":"add","path":"/metadata/files","value":[{"path":"src/core/extensions/extension-types.ts","scope":"project","note":"typed host capability"},{"path":"src/core/history/workspace-history.ts","scope":"project","note":"audited preview and replay"},{"path":"src/sdk/extension-command-context.ts","scope":"project","note":"host-bound mutation"},{"path":"src/sdk/runtime.ts","scope":"project","note":"caller attribution"},{"path":"tests/integration/extension-host-contracts.integration.spec.ts","scope":"project","note":"copied-extension acceptance"},{"path":"tests/unit/core/history/workspace-history.spec.ts","scope":"project","note":"history atomicity regression"}]}],"before_hash":"09385b35d9d7ad5aab7cbb96545a1faea9e8b6b89acb7b093e374cfb162a521d","after_hash":"363ddbe67783d4f50a6485c44ce29f47cc41189360f9428f1da458991f4f37ed","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":"8c4d3d3a844bc7163b2fa9a338b8c77c68b46625b03eccb62d99082754cf52f0"} +{"hash_algorithm":"sha256","ts":"2026-09-29T05:13:58.488Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"docs_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T05:13:58.488Z"},{"op":"add","path":"/metadata/docs","value":[{"path":"docs/README.md","scope":"project","note":"documentation index"},{"path":"docs/SDK_AUDITED_MUTATIONS.md","scope":"project","note":"extension settings API"}]}],"before_hash":"363ddbe67783d4f50a6485c44ce29f47cc41189360f9428f1da458991f4f37ed","after_hash":"066eca34bb8f1e06992d69811dd67a30b0de3136171ebf7ce622a7af0d9f014b","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":"16f187a28a97ecd8b3d5d73d35714232ab983cd7ee9f7e819a5230072706ff7f"} +{"hash_algorithm":"sha256","ts":"2026-09-29T05:13:59.258Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"tests_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T05:13:59.258Z"},{"op":"add","path":"/metadata/tests","value":[{"command":"node scripts/run-tests.mjs test -- tests/integration/extension-host-contracts.integration.spec.ts tests/unit/core/history/workspace-history.spec.ts","scope":"project","timeout_seconds":600,"provenance":{"author":"harness:codex","created_at":"2026-09-29T05:13:59.218Z","source_kind":"local_mutation","source_ref":"fix/audited-workspace-mutations-bun-merge"}}]}],"before_hash":"066eca34bb8f1e06992d69811dd67a30b0de3136171ebf7ce622a7af0d9f014b","after_hash":"e04c1c63395e22226bc2072507508e36e71547a5beec1b453c2b894e5f51868c","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":"55c8a7765ce0450bf6ca346a799b7d55099e89283b39cbf5fa74a1933f008bc1"} +{"hash_algorithm":"sha256","ts":"2026-09-29T06:15:41.145Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/5/note","value":"copied-extension acceptance"},{"op":"replace","path":"/metadata/files/5/path","value":"tests/integration/extension-host-contracts.integration.spec.ts"},{"op":"replace","path":"/metadata/files/4/note","value":"caller attribution"},{"op":"replace","path":"/metadata/files/4/path","value":"src/sdk/runtime.ts"},{"op":"replace","path":"/metadata/files/3/note","value":"host-bound mutation"},{"op":"replace","path":"/metadata/files/3/path","value":"src/sdk/extension-command-context.ts"},{"op":"replace","path":"/metadata/files/2/note","value":"audited preview and replay"},{"op":"replace","path":"/metadata/files/2/path","value":"src/core/history/workspace-history.ts"},{"op":"replace","path":"/metadata/files/1/note","value":"typed host capability"},{"op":"replace","path":"/metadata/files/1/path","value":"src/core/extensions/extension-types.ts"},{"op":"replace","path":"/metadata/files/0/note","value":"versioned public SDK capability"},{"op":"replace","path":"/metadata/files/0/path","value":"sdk/public-surface.json"},{"op":"add","path":"/metadata/files/6","value":{"path":"tests/unit/core/history/workspace-history.spec.ts","scope":"project","note":"history atomicity regression"}},{"op":"add","path":"/metadata/files/7","value":{"path":"tests/unit/sdk/sdk-extension-mutation-platform.spec.ts","scope":"project","note":"source-level settings mutation coverage"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:15:41.145Z"}],"before_hash":"e04c1c63395e22226bc2072507508e36e71547a5beec1b453c2b894e5f51868c","after_hash":"8e07f8371b3bc4690b91f83b2fb0ce0878c7d8289137b15636b365bec631b82f","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":"35de56a900d4c742ca38e4a0e421bc3595f5bb75470561aad9563f80137849a5"} +{"hash_algorithm":"sha256","ts":"2026-09-29T06:15:41.959Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"tests_add","patch":[{"op":"add","path":"/metadata/tests/1","value":{"command":"node scripts/run-tests.mjs test -- tests/unit/sdk/sdk-extension-mutation-platform.spec.ts","scope":"project","timeout_seconds":600,"provenance":{"author":"harness:codex","created_at":"2026-09-29T06:15:41.923Z","source_kind":"local_mutation","source_ref":"fix/audited-workspace-mutations-bun-merge"}}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:15:41.959Z"}],"before_hash":"8e07f8371b3bc4690b91f83b2fb0ce0878c7d8289137b15636b365bec631b82f","after_hash":"3c50071ed4596e64ea37ec5e4a8cdd554149862ee6d5f1f02758c71b890f8616","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":"d3603b7dba9e2836b4d618c5b3bd4f0a2e7d7855d62a233538036917cc1584a2"} +{"hash_algorithm":"sha256","ts":"2026-09-29T06:15:42.645Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/7/note","value":"history atomicity regression"},{"op":"replace","path":"/metadata/files/7/path","value":"tests/unit/core/history/workspace-history.spec.ts"},{"op":"replace","path":"/metadata/files/6/note","value":"instrumented operation census timeout"},{"op":"replace","path":"/metadata/files/6/path","value":"tests/unit/core/history/operation-source-contract.spec.ts"},{"op":"add","path":"/metadata/files/8","value":{"path":"tests/unit/sdk/sdk-extension-mutation-platform.spec.ts","scope":"project","note":"source-level settings mutation coverage"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:15:42.645Z"}],"before_hash":"3c50071ed4596e64ea37ec5e4a8cdd554149862ee6d5f1f02758c71b890f8616","after_hash":"6568751cb483dd1e777a47818115010c09e2e2d1f342e6ada7d69fb44a498d0b","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":"d30d1ecd69d163293f599024bfd444bc3828dc4d92d492fa1620d155de1fb970"} +{"hash_algorithm":"sha256","ts":"2026-09-29T06:48:29.905Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"test_run_track","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:48:29.905Z"},{"op":"add","path":"/metadata/test_runs","value":[{"run_id":"test-local-mumbeylv-rahy0q","kind":"test","status":"passed","started_at":"2026-09-29T06:47:49.882Z","finished_at":"2026-09-29T06:48:29.875Z","recorded_at":"2026-09-29T06:48:29.875Z","passed":2,"failed":0,"skipped":0,"executions":[{"command":"node scripts/run-tests.mjs test -- tests/integration/extension-host-contracts.integration.spec.ts tests/unit/core/history/workspace-history.spec.ts","requested_pm_context_mode":"schema","pm_context_mode":"schema","workspace_context_mode":"source","trust_reason":"local_source_ref"},{"command":"node scripts/run-tests.mjs test -- tests/unit/sdk/sdk-extension-mutation-platform.spec.ts","requested_pm_context_mode":"schema","pm_context_mode":"schema","workspace_context_mode":"source","trust_reason":"local_source_ref"}]}]}],"before_hash":"6568751cb483dd1e777a47818115010c09e2e2d1f342e6ada7d69fb44a498d0b","after_hash":"7ae6a6181e72d761d42b9c874818aa10df42803964f7e9c3baed6da3892bfe12","item_hash_version":3,"message":"Track test run summary (test-local-mumbeylv-rahy0q)","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":"dee3da87040a7bc3a69706826da81169c0b5cff29f0765726113c5675601c271"} +{"hash_algorithm":"sha256","ts":"2026-09-29T06:50:38.926Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/2","value":{"created_at":"2026-09-29T06:50:38.926Z","author":"harness:codex","text":"Acceptance evidence: extension command SDK now exposes mutateWorkspaceSettings with a fixed host tracker path, lock-scoped read/validate/write, workspace history, author attribution, dry-run, and replay-safe operation IDs. Copied extension integration runs without a local SDK and passes strict health/history verification. Source-level tests cover preview, replay, no-op, invalid current/result, drift, and history failure compensation. Linked tests passed; full suite 9414 passed; exact V8 coverage 100/100/100/100; pnpm quality:static passed. Public SDK surface snapshot acknowledges the added host capability and optional controls."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:50:38.926Z"}],"before_hash":"7ae6a6181e72d761d42b9c874818aa10df42803964f7e9c3baed6da3892bfe12","after_hash":"936f49a137b569905109d1f09dee1252d1c3147500609bf11aac9fa4e1fc4857","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":"cf2708538a5c640d6333cdc58111d9cd5c2bca7965f1739aabfda26d8f70c6ec"} +{"hash_algorithm":"sha256","ts":"2026-09-29T06:50:39.821Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"learning_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:50:39.821Z"},{"op":"add","path":"/metadata/learnings","value":[{"created_at":"2026-09-29T06:50:39.821Z","author":"harness:codex","text":"Copied extensions must receive governed workspace mutations through host-injected SDK services; resolving local SDK packages or writing settings.json directly breaks portability and audit attribution."}]}],"before_hash":"936f49a137b569905109d1f09dee1252d1c3147500609bf11aac9fa4e1fc4857","after_hash":"283adf943e98259be91d383c4daebe3c675a16a6653a7722e7df6c68e1fcdf3b","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":"8b89b81c6f14d1b942926bdb42252e2c86fdc06cac96153996a1bc0f24d24c69"} +{"hash_algorithm":"sha256","ts":"2026-09-29T06:50:55.170Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-vks66s","claim:pm-wtqltn","release:pm-2589e6"]},"topic":{"value":"workset:pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-vks66s","claim:pm-wtqltn","release:pm-2589e6"]}},"op":"close","patch":[{"op":"replace","path":"/metadata/actual_result","value":"Copied-extension integration and source-level failure/preview/replay tests pass; linked tests, full suite, exact coverage, and static quality pass."},{"op":"replace","path":"/metadata/expected_result","value":"A copied extension can change governed settings without a local SDK while retaining strict history and health."},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:50:55.170Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/closed_at","value":"2026-09-29T06:50:55.143Z"},{"op":"add","path":"/metadata/completed_at","value":"2026-09-29T06:50:55.143Z"},{"op":"add","path":"/metadata/resolution","value":"Added typed injected SDK settings mutation with fixed project scope, validation, locking, workspace history, dry-run, idempotent replay, and caller attribution."},{"op":"add","path":"/metadata/close_reason","value":"Deliver audited host-bound extension settings mutations"}],"before_hash":"283adf943e98259be91d383c4daebe3c675a16a6653a7722e7df6c68e1fcdf3b","after_hash":"561094b23ee82bc2cb7e8d4e0c9f3871abc4948a17bd96ad35c44f4565606772","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":"f4c2ecc273f33eb8b01afa611818c5a3d85847c0bafe296a82d214903d4e4d7d"} +{"hash_algorithm":"sha256","ts":"2026-09-29T06:50:56.009Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-vks66s","claim:pm-wtqltn","release:pm-2589e6"]},"topic":{"value":"workset:pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-vks66s","claim:pm-wtqltn","release:pm-2589e6"]}},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:50:56.009Z"}],"before_hash":"561094b23ee82bc2cb7e8d4e0c9f3871abc4948a17bd96ad35c44f4565606772","after_hash":"85a61fd2ed77368b05f17ecace5320645e86e9f337957487a9b8d1d31db90cd3","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":"cfcf6d55e9b517553b77918a1133d3ce1ce91ab03f30f1ff11676d68cdb4f8e2"} +{"hash_algorithm":"sha256","ts":"2026-09-29T06:53:30.498Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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/1/note","value":"documentation index"},{"op":"replace","path":"/metadata/docs/1/path","value":"docs/README.md"},{"op":"replace","path":"/metadata/docs/0/note","value":"generated release entry"},{"op":"replace","path":"/metadata/docs/0/path","value":"CHANGELOG.md"},{"op":"add","path":"/metadata/docs/2","value":{"path":"docs/SDK_AUDITED_MUTATIONS.md","scope":"project","note":"extension settings API"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:53:30.498Z"}],"before_hash":"85a61fd2ed77368b05f17ecace5320645e86e9f337957487a9b8d1d31db90cd3","after_hash":"0e8b6389a0ab9d60e036ac2ab8a31a20b0c098caf8332e8dc7adb678e8df5981","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":"bbd0d703d1bc9df77d7580ebaeea30b3f06febfdb99e4f56835609297bdc439e"} +{"hash_algorithm":"sha256","ts":"2026-09-29T06:53:32.125Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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/8/note","value":"history atomicity regression"},{"op":"replace","path":"/metadata/files/8/path","value":"tests/unit/core/history/workspace-history.spec.ts"},{"op":"replace","path":"/metadata/files/7/note","value":"instrumented operation census timeout"},{"op":"replace","path":"/metadata/files/7/path","value":"tests/unit/core/history/operation-source-contract.spec.ts"},{"op":"replace","path":"/metadata/files/6/note","value":"copied-extension acceptance"},{"op":"replace","path":"/metadata/files/6/path","value":"tests/integration/extension-host-contracts.integration.spec.ts"},{"op":"replace","path":"/metadata/files/5/note","value":"caller attribution"},{"op":"replace","path":"/metadata/files/5/path","value":"src/sdk/runtime.ts"},{"op":"replace","path":"/metadata/files/4/note","value":"host-bound mutation"},{"op":"replace","path":"/metadata/files/4/path","value":"src/sdk/extension-command-context.ts"},{"op":"replace","path":"/metadata/files/3/note","value":"audited preview and replay"},{"op":"replace","path":"/metadata/files/3/path","value":"src/core/history/workspace-history.ts"},{"op":"replace","path":"/metadata/files/2/note","value":"typed host capability"},{"op":"replace","path":"/metadata/files/2/path","value":"src/core/extensions/extension-types.ts"},{"op":"replace","path":"/metadata/files/1/note","value":"versioned public SDK capability"},{"op":"replace","path":"/metadata/files/1/path","value":"sdk/public-surface.json"},{"op":"replace","path":"/metadata/files/0/note","value":"latest pm-changelog package install metadata for reviewed changelog"},{"op":"replace","path":"/metadata/files/0/path","value":".agents/pm/extensions/.managed-extensions.json"},{"op":"add","path":"/metadata/files/9","value":{"path":"tests/unit/sdk/sdk-extension-mutation-platform.spec.ts","scope":"project","note":"source-level settings mutation coverage"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:53:32.125Z"}],"before_hash":"0e8b6389a0ab9d60e036ac2ab8a31a20b0c098caf8332e8dc7adb678e8df5981","after_hash":"1f7c130aef1c4d0bd941345d8c4fea48b602da17a99fc92ff64088eca6e465ca","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":"2c3148c2b29bd8d8f4ec2f4a8f1c911ca9c1907d4c6da97e07e32cdc33165914"} diff --git a/.agents/pm/issues/pm-2589e6.toon b/.agents/pm/issues/pm-2589e6.toon index c8fb4dabf9..613fa67a46 100644 --- a/.agents/pm/issues/pm-2589e6.toon +++ b/.agents/pm/issues/pm-2589e6.toon @@ -2,11 +2,13 @@ id: pm-2589e6 title: Full-item stdin JSON update silently ignores edited notes description: Published 2026.9.28 returns changed_field_count=0 for a changed notes array while preserving the old note; successful no-op conceals stale evidence. type: Issue -status: open +status: closed priority: 2 tags: [] created_at: "2026-09-29T00:19:58.543Z" -updated_at: "2026-09-29T00:31:43.731Z" +updated_at: "2026-09-29T06:53:29.820Z" +closed_at: "2026-09-29T06:50:53.619Z" +completed_at: "2026-09-29T06:50:53.619Z" author: "harness:codex" estimated_minutes: 180 acceptance_criteria: Changed notes are atomically audited or rejected with a field-specific error; Disposable get-modify-update regression proves no false successful no-op @@ -14,13 +16,44 @@ parent: pm-mpbb risk: medium confidence: 85 severity: medium -expected_result: Changed notes are applied with history or explicitly rejected. -actual_result: Update succeeds with changed_field_count=0 and retains the original note. +resolution: "Full-item JSON updates now compare persisted comments, notes, and learnings under the item lock and return a stable actionable refusal for edits or removals; new entries still append." +expected_result: Edited existing notes cannot silently succeed or change item state. +actual_result: "Disposable structured-mutation regression rejects edited note with unchanged verified history; linked test, full suite, exact coverage, and static quality pass." affected_version: 2026.9.28 component: CLI structured mutations dependencies[2]{id,kind,created_at,author,source_kind,author_source}: pm-a2h3,related,"2026-09-29T00:19:58.543Z","harness:codex","cli:create:dep",detected pm-kipd,discovered_from,"2026-09-29T00:19:58.543Z","harness:codex","cli:create:dep",detected -comments[1]{created_at,author,text}: +comments[3]{created_at,author,text}: "2026-09-29T00:19:58.543Z","harness:codex","Duplicate check: searched all-status PM items for stdin-json and notes; closed pm-kipd delivered the original round-trip feature and pm-a2h3 delivered direct note editing, but neither tracks this new silent no-op defect. GitHub issue #1343 has no prior PM link." + "2026-09-29T04:56:30.520Z","harness:codex","Test-audit contract: a changed persisted note in get->modify->update --stdin-json must never return a successful no-op. Existing structured round-trip test only appends a new note and does not detect edits to a persisted annotation. Primary proof will use a disposable tracker and history verification; no test-only production seam." + "2026-09-29T06:50:36.685Z","harness:codex","Acceptance evidence: full-item get to update --stdin-json now preserves persisted comments, notes, and learnings as immutable snapshots and rejects edited or removed entries under the item lock with stdin_json_persisted_annotation_changed. The structured-mutation integration test proves the formerly silent edited-note no-op fails without changing item or history, while legitimate new annotations still append. Linked test passed; full suite 9414 passed; exact V8 coverage 100/100/100/100; pnpm quality:static passed. Manual correction remains pm notes/comments/learnings --edit with audit history." +learnings[1]{created_at,author,text}: + "2026-09-29T06:50:37.375Z","harness:codex","A full-item JSON round trip must distinguish new annotation entries from persisted audit entries and verify the persisted snapshot inside the locked mutation, including annotation-only updates." +files[5]{path,scope,note}: + src/sdk/generated/generated-error-code-catalog-part-2.ts,project,stable annotation refusal catalog + src/sdk/lifecycle/update.ts,project,locked annotation integrity + src/sdk/structured-mutations.ts,project,full-item annotation snapshot + tests/fixtures/contracts/full.json,project,generated command contract snapshot + tests/integration/structured-mutation-io.integration.spec.ts,project,regression acceptance +tests[1]{command,scope,timeout_seconds,provenance{author,created_at,source_kind,source_ref}}: + node scripts/run-tests.mjs test -- tests/integration/structured-mutation-io.integration.spec.ts,project,600,"harness:codex","2026-09-29T05:13:55.996Z",local_mutation,fix/audited-workspace-mutations-bun-merge +test_runs[1]: + - run_id: test-local-mumbe394-q4gosn + kind: test + status: passed + started_at: "2026-09-29T06:47:32.094Z" + finished_at: "2026-09-29T06:47:49.240Z" + recorded_at: "2026-09-29T06:47:49.240Z" + 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/structured-mutation-io.integration.spec.ts,schema,schema,source,local_source_ref +docs[4]{path,scope,note}: + CHANGELOG.md,project,generated release entry + docs/generated/REFUSAL_CLOSURE_CENSUS.md,project,generated refusal recovery contract + docs/README.md,project,documentation index + docs/SDK_AUDITED_MUTATIONS.md,project,full-item JSON correction contract +close_reason: Reject persisted annotation edits in full-item JSON updates body: "GitHub: https://github.com/unbraind/pm-cli/issues/1343\n\nReproduce with a disposable tracker: create an item with a note, get the full JSON item, edit notes[0].text, and pipe it to update --stdin-json. Require either an atomic audited note correction or a nonzero field-specific rejection with pm notes --edit guidance." diff --git a/.agents/pm/issues/pm-vks66s.toon b/.agents/pm/issues/pm-vks66s.toon index 1037136c55..6132f4524a 100644 --- a/.agents/pm/issues/pm-vks66s.toon +++ b/.agents/pm/issues/pm-vks66s.toon @@ -2,11 +2,13 @@ id: pm-vks66s title: Bun merge-driver installation disagrees with strict health description: "Published 2026.9.28 bunx --bun init and merge install configure merge drivers that strict health immediately reports drifted, even after reinstall." type: Issue -status: open +status: closed priority: 2 tags: [] created_at: "2026-09-29T00:20:04.660Z" -updated_at: "2026-09-29T00:31:44.903Z" +updated_at: "2026-09-29T06:58:37.384Z" +closed_at: "2026-09-29T06:50:57.064Z" +completed_at: "2026-09-29T06:50:57.064Z" author: "harness:codex" estimated_minutes: 240 acceptance_criteria: Bun init and merge install pass strict health on a fresh Git workspace; Repeated install is idempotent and a real two-branch merge executes the driver @@ -14,12 +16,39 @@ parent: pm-u9d0 risk: high confidence: 85 severity: high -expected_result: "Bun init registers stable, executable merge drivers accepted by strict health." -actual_result: All five driver keys are reported drifted after init and merge install. +resolution: Merge installation now selects a version-matched stable absolute Bun launcher and driver audit accepts valid Node or Bun identities. +expected_result: bunx-installed pm passes strict merge-driver health and performs semantic Git merges. +actual_result: "Disposable packed Bun consumer passed strict health twice and real item/history Git merge with verified chain; npx and bunx executable checks, linked test, full suite, exact coverage, and static quality pass." affected_version: 2026.9.28 component: Bun merge driver dependencies[1]{id,kind,created_at,author,source_kind,author_source}: pm-rnl3sa,related,"2026-09-29T00:20:04.660Z","harness:codex","cli:create:dep",detected -comments[1]{created_at,author,text}: +comments[4]{created_at,author,text}: "2026-09-29T00:20:04.660Z","harness:codex","Duplicate check: all-status PM search for Bun merge-driver drift found the closed package-runner dispatch issue pm-rnl3sa and other merge work, but no owner for this Bun-specific install/health mismatch." + "2026-09-29T04:56:31.027Z","harness:codex","Test-audit contract: Bun-installed merge drivers must pass strict health, remain idempotent, and execute a real merge. Existing merge safety tests cover Node driver syntax and worktree identity, not Bun's executable name or bunx runtime path. Primary proof will use a disposable Git/Bun workspace." + "2026-09-29T06:50:40.724Z","harness:codex","Acceptance evidence: actual Bun 1.4.2 in a disposable npm-packed consumer installed an absolute /usr/bin/bun merge driver; bunx --bun init and repeated merge install both passed strict health with zero findings. A real Git branch merge invoked item and history drivers, retained the left note and right description, and pm history --verify returned three valid entries. npx --no-install pm and bunx --no-install pm both returned the packed CLI version. Linked regression test passed; full suite 9414 passed; exact V8 coverage 100/100/100/100; pnpm quality:static passed. Repository source npm audit found zero advisories and GitHub open Dependabot, code, and secret scanning alerts were empty." + "2026-09-29T06:58:37.384Z","harness:codex","Post-rebase packed acceptance on v2026.9.29: fresh tarball installed with Bun in a disposable Git project, bunx --bun pm init succeeded, bunx --bun pm health --strict-exit reported ok with zero findings, and both npx --no-install pm --version and bunx --no-install pm --version returned 2026.9.29. The earlier disposable project also completed a real semantic item/history Git merge with a verified chain." +learnings[1]{created_at,author,text}: + "2026-09-29T06:50:41.415Z","harness:codex",Bun can report a versioned executable path while Git later needs a durable absolute launcher; resolve and validate bun from absolute PATH entries at install time and audit both Node and Bun driver identities. +files[2]{path,scope,note}: + src/sdk/merge/install.ts,project,Bun launcher selection and audit + tests/unit/sdk/merge/worktree-config.spec.ts,project,launcher regression +tests[1]{command,scope,timeout_seconds,provenance{author,created_at,source_kind,source_ref}}: + node scripts/run-tests.mjs test -- tests/unit/sdk/merge/worktree-config.spec.ts,project,600,"harness:codex","2026-09-29T05:14:02.131Z",local_mutation,fix/audited-workspace-mutations-bun-merge +test_runs[1]: + - run_id: test-local-mumbfe6g-zm3zsz + kind: test + status: passed + started_at: "2026-09-29T06:48:31.465Z" + finished_at: "2026-09-29T06:48:50.056Z" + recorded_at: "2026-09-29T06:48:50.056Z" + 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/unit/sdk/merge/worktree-config.spec.ts,schema,schema,source,local_source_ref +docs[2]{path,scope,note}: + CHANGELOG.md,project,generated release entry + docs/MERGE_SAFETY.md,project,Bun merge-driver contract +close_reason: Repair Bun merge-driver launcher identity body: "GitHub: https://github.com/unbraind/pm-cli/issues/1346\n\nReproduce in a disposable Bun Git project; compare installed Git driver commands against strict health expectation, prove idempotent reinstall, and execute a real two-branch item merge using the registered driver." diff --git a/.agents/pm/issues/pm-wtqltn.toon b/.agents/pm/issues/pm-wtqltn.toon index 5b3f8395b7..f93f003957 100644 --- a/.agents/pm/issues/pm-wtqltn.toon +++ b/.agents/pm/issues/pm-wtqltn.toon @@ -2,11 +2,13 @@ id: pm-wtqltn title: Expose host-bound audited workspace settings mutation to extension commands description: "A packed extension can apply settings directly but lacks a portable host SDK transaction that preserves the workspace audit chain, leaving strict health with settings history drift." type: Issue -status: open +status: closed priority: 2 tags: [] created_at: "2026-09-29T00:20:00.805Z" -updated_at: "2026-09-29T00:31:44.311Z" +updated_at: "2026-09-29T06:53:32.125Z" +closed_at: "2026-09-29T06:50:55.143Z" +completed_at: "2026-09-29T06:50:55.143Z" author: "harness:codex" estimated_minutes: 360 acceptance_criteria: Host-bound extension SDK mutation writes settings and workspace history atomically; Packed extension without local CLI SDK dependency passes strict health and rollback tests @@ -14,13 +16,50 @@ parent: pm-o2kc risk: high confidence: 80 severity: high -expected_result: "Extension settings updates are atomic, audited and pass strict health." -actual_result: pm-presets applies directly to settings.json and strict health reports workspace history drift. +resolution: "Added typed injected SDK settings mutation with fixed project scope, validation, locking, workspace history, dry-run, idempotent replay, and caller attribution." +expected_result: A copied extension can change governed settings without a local SDK while retaining strict history and health. +actual_result: "Copied-extension integration and source-level failure/preview/replay tests pass; linked tests, full suite, exact coverage, and static quality pass." affected_version: 2026.9.28 component: SDK extension command context dependencies[2]{id,kind,created_at,author,source_kind,author_source}: pm-klo8,discovered_from,"2026-09-29T00:20:00.805Z","harness:codex","cli:create:dep",detected pm-lvea,related,"2026-09-29T00:20:00.805Z","harness:codex","cli:create:dep",detected -comments[1]{created_at,author,text}: +comments[3]{created_at,author,text}: "2026-09-29T00:20:00.805Z","harness:codex","Duplicate check: all-status PM search for audited extension settings mutation found related closed workspace-audit and SDK audits but no current owner of this missing host-bound contract. GitHub #1345 cites pm-presets pm-1vx6 in that package's tracker; that is a separate consumer defect." + "2026-09-29T04:56:31.683Z","harness:codex","Test-audit contract: a copied extension with no locally resolvable pm SDK can atomically mutate governed settings through its injected host SDK while retaining strict workspace history. Existing direct SDK singleton tests do not prove extension portability or failure compensation. Primary proof will use a disposable packed consumer." + "2026-09-29T06:50:38.926Z","harness:codex","Acceptance evidence: extension command SDK now exposes mutateWorkspaceSettings with a fixed host tracker path, lock-scoped read/validate/write, workspace history, author attribution, dry-run, and replay-safe operation IDs. Copied extension integration runs without a local SDK and passes strict health/history verification. Source-level tests cover preview, replay, no-op, invalid current/result, drift, and history failure compensation. Linked tests passed; full suite 9414 passed; exact V8 coverage 100/100/100/100; pnpm quality:static passed. Public SDK surface snapshot acknowledges the added host capability and optional controls." +learnings[1]{created_at,author,text}: + "2026-09-29T06:50:39.821Z","harness:codex",Copied extensions must receive governed workspace mutations through host-injected SDK services; resolving local SDK packages or writing settings.json directly breaks portability and audit attribution. +files[10]{path,scope,note}: + .agents/pm/extensions/.managed-extensions.json,project,latest pm-changelog package install metadata for reviewed changelog + sdk/public-surface.json,project,versioned public SDK capability + src/core/extensions/extension-types.ts,project,typed host capability + src/core/history/workspace-history.ts,project,audited preview and replay + src/sdk/extension-command-context.ts,project,host-bound mutation + src/sdk/runtime.ts,project,caller attribution + tests/integration/extension-host-contracts.integration.spec.ts,project,copied-extension acceptance + tests/unit/core/history/operation-source-contract.spec.ts,project,instrumented operation census timeout + tests/unit/core/history/workspace-history.spec.ts,project,history atomicity regression + tests/unit/sdk/sdk-extension-mutation-platform.spec.ts,project,source-level settings mutation coverage +tests[2]{command,scope,timeout_seconds,provenance{author,created_at,source_kind,source_ref}}: + node scripts/run-tests.mjs test -- tests/integration/extension-host-contracts.integration.spec.ts tests/unit/core/history/workspace-history.spec.ts,project,600,"harness:codex","2026-09-29T05:13:59.218Z",local_mutation,fix/audited-workspace-mutations-bun-merge + node scripts/run-tests.mjs test -- tests/unit/sdk/sdk-extension-mutation-platform.spec.ts,project,600,"harness:codex","2026-09-29T06:15:41.923Z",local_mutation,fix/audited-workspace-mutations-bun-merge +test_runs[1]: + - run_id: test-local-mumbeylv-rahy0q + kind: test + status: passed + started_at: "2026-09-29T06:47:49.882Z" + finished_at: "2026-09-29T06:48:29.875Z" + recorded_at: "2026-09-29T06:48:29.875Z" + passed: 2 + failed: 0 + skipped: 0 + executions[2]{command,requested_pm_context_mode,pm_context_mode,workspace_context_mode,trust_reason}: + node scripts/run-tests.mjs test -- tests/integration/extension-host-contracts.integration.spec.ts tests/unit/core/history/workspace-history.spec.ts,schema,schema,source,local_source_ref + node scripts/run-tests.mjs test -- tests/unit/sdk/sdk-extension-mutation-platform.spec.ts,schema,schema,source,local_source_ref +docs[3]{path,scope,note}: + CHANGELOG.md,project,generated release entry + docs/README.md,project,documentation index + docs/SDK_AUDITED_MUTATIONS.md,project,extension settings API +close_reason: Deliver audited host-bound extension settings mutations body: "GitHub: https://github.com/unbraind/pm-cli/issues/1345\n\nExpose a typed extension-command SDK operation for lock-scoped audited singleton mutation with invocation identity, atomic write and history, compensation, idempotence, and dry-run. Prove a packed extension without a local host SDK dependency can apply settings and pass strict health." diff --git a/CHANGELOG.md b/CHANGELOG.md index 84b51215fa..a379ec3c40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## Unreleased + +### Fixed + +- Bun merge-driver installation disagrees with strict health ([pm-vks66s](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/issues/pm-vks66s.toon)) +- Expose host-bound audited workspace settings mutation to extension commands ([pm-wtqltn](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/issues/pm-wtqltn.toon)) +- Full-item stdin JSON update silently ignores edited notes ([pm-2589e6](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/issues/pm-2589e6.toon)) + ## 2026.9.29 - 2026-09-29 ### Fixed diff --git a/docs/MERGE_SAFETY.md b/docs/MERGE_SAFETY.md index fd232a9cb1..2c83bcde92 100644 --- a/docs/MERGE_SAFETY.md +++ b/docs/MERGE_SAFETY.md @@ -13,6 +13,8 @@ compatibility is tracked by [pm-wn3ee5](../.agents/pm/issues/pm-wn3ee5.toon). Lossless concurrent acceptance-criteria composition is tracked by [pm-inn5y5](../.agents/pm/issues/pm-inn5y5.toon). +The Bun launcher and strict-health repair are tracked by +[pm-vks66s](../.agents/pm/issues/pm-vks66s.toon). Recency-aware scalar convergence, bounded fresh-clone decision evidence, and audited pre-durable receipt dispositions are tracked by [pm-7wzb6d](../.agents/pm/issues/pm-7wzb6d.toon), @@ -39,11 +41,14 @@ git commit -m "chore(pm): install tracker merge drivers" `pm merge install` writes an idempotent, fenced `.gitattributes` block and repository-local `git config` entries. The attributes are committed; the driver commands are clone-local, so every collaborator and fresh CI clone that performs merges must run the install command. The clone-local driver values record the absolute runtime launcher and bundled -`dist/cli.js` path resolved by the installing SDK. The installer preserves an -absolute PATH symlink when it resolves to the executing runtime, so a runtime -manager can retarget that launcher during upgrades without breaking every clone. -Relative PATH entries, inaccessible launchers, and different runtimes are ignored; -without a matching launcher, the installer retains the executing binary path. +`dist/cli.js` path resolved by the installing SDK. With Node, the installer +preserves an absolute PATH symlink when it resolves to the executing runtime, so +a runtime manager can retarget that launcher during upgrades without breaking +every clone. With Bun, it resolves an executable `bun` on an absolute PATH entry, +checks that its version matches the running Bun version, and records its stable +absolute target. Relative PATH entries, inaccessible launchers, and different +runtimes are ignored; without a matching launcher, the installer retains the +executing binary path. Git therefore does not depend on a bare `pm` command or the caller's later `PATH` when it merges tracker data. The item-path placeholder is stored as bare `%P`: Git performs the required @@ -53,7 +58,7 @@ remain reconcilable. `pm validate --check-storage-integrity` and `pm health` also compare every effective driver definition with the installed SDK. A command installed by another valid `@unbrained/pm-cli` package is -also accepted when its Node and `dist/cli.js` paths exist, its manifest owns the +also accepted when its Node or Bun launcher and `dist/cli.js` paths exist, its manifest owns the `pm` bin, the driver arguments are semantically identical, and its version matches any exact project runtime pins. A driver inside a sibling worktree of the same repository is rejected, even when that package is otherwise valid. diff --git a/docs/README.md b/docs/README.md index 26c8883092..ab35d2cd59 100644 --- a/docs/README.md +++ b/docs/README.md @@ -95,6 +95,7 @@ pm guide release --json - [Extension Lifecycle Contracts](EXTENSION_LIFECYCLE.md) - source identity, durable migrations, and scoped preflight ownership. - [Extension Author Contracts](EXTENSION_AUTHOR_CONTRACTS.md) - the stability guarantees and contract surface package authors build against. - [SDK](SDK.md) - public import surfaces and typed authoring examples. +- [Audited SDK Settings and Item Updates](SDK_AUDITED_MUTATIONS.md) - extension-owned settings changes, replay-safe previews, and full-item JSON annotation integrity. - [SDK execution and recovery contracts](SDK_EXECUTION_CONTRACTS.md): schema-driven scheduling, handoff, and command recovery. - [Multi-Branch Merge Safety](MERGE_SAFETY.md) - semantic tracker merge drivers, post-merge integrity gates, delete/modify policy, and recovery-receipt retention. - [Codex Plugin](CODEX_PLUGIN.md) - native MCP plugin install, tools, skills, and safety notes. diff --git a/docs/SDK_AUDITED_MUTATIONS.md b/docs/SDK_AUDITED_MUTATIONS.md new file mode 100644 index 0000000000..9ae62e7ad0 --- /dev/null +++ b/docs/SDK_AUDITED_MUTATIONS.md @@ -0,0 +1,47 @@ +# Audited SDK Settings and Item Updates + +The extension settings contract is tracked by +[pm-wtqltn](../.agents/pm/issues/pm-wtqltn.toon). Full-item JSON annotation +integrity is tracked by [pm-2589e6](../.agents/pm/issues/pm-2589e6.toon). + +## Extension settings + +An extension command receives a host-bound `sdk` in its `run` context. Use +`sdk.mutateWorkspaceSettings` to change the current project's `settings.json`. +The host chooses the tracker path and author, validates the complete result, +serializes concurrent changes under the workspace lock, and records the change +in workspace history. A copied extension needs no local SDK installation. + +```ts +api.registerCommand({ + name: "example hints", + flags: [{ long: "--operation-id", value_name: "id", value_type: "string" }], + run: ({ sdk, options }) => + sdk.mutateWorkspaceSettings({ + operationId: options.operationId, + mutate: (current) => ({ + ...current, + ux: { ...current.ux, deprecation_hints: true }, + }), + }), +}); +``` + +Supply a stable `operationId` for a logical operation. Retrying an already +recorded operation returns `replayed: true` without appending another history +event. `dryRun: true` calculates `changed` without changing settings or history; +the same operation ID can then be used for the real mutation. A normal return +also reports `changed` and `dry_run`. Invalid results fail before writing, and +a history-append failure restores the previous settings bytes. The callback +must derive its complete next settings object from the locked `current` value. + +## Full-item JSON updates + +`pm update --stdin-json` accepts a full item read document. New comments, +notes, and learnings in that document are appended through the normal item +mutation. Persisted entries are an audit trail: changing or removing one in +the submitted document now fails with +`stdin_json_persisted_annotation_changed` before any item fields are written. +This check also applies when the only submitted change is an annotation. Use +`pm comments`, `pm notes`, or `pm learnings` with `--edit` to correct an +existing entry; those commands write a history event for the correction. diff --git a/docs/generated/REFUSAL_CLOSURE_CENSUS.md b/docs/generated/REFUSAL_CLOSURE_CENSUS.md index ed7e217409..c45d237317 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: 389 +- Catalog error codes: 390 - 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: 370 -- Coverage fraction: 0.048843 +- Uncovered error codes: 371 +- Coverage fraction: 0.048718 - Closed-domain probes: 19 - Grammar probes: 117 @@ -296,6 +296,7 @@ Every catalog code is listed. An `uncovered` row is an explicit closure obligati | `status_invalid` | `status_invalid` | uncovered | none | 0 | | `status_lifecycle_role_required` | `status_lifecycle_role_required` | uncovered | none | 0 | | `stdin_json_invalid` | `stdin_json_invalid` | uncovered | none | 0 | +| `stdin_json_persisted_annotation_changed` | `stdin_json_persisted_annotation_changed` | uncovered | none | 0 | | `stdin_json_unknown_key` | `stdin_json_unknown_key` | uncovered | none | 0 | | `structured_event_immutable` | `structured_event_immutable` | uncovered | none | 0 | | `structured_event_input_conflict` | `structured_event_input_conflict` | uncovered | none | 0 | diff --git a/sdk/public-surface.json b/sdk/public-surface.json index 3232167547..b13b1dbe6a 100644 --- a/sdk/public-surface.json +++ b/sdk/public-surface.json @@ -2112,6 +2112,20 @@ ], "package_version": "2026.9.28", "reason": "Add optional typed Plan mutation receipt while preserving full SDK Plan results; default CLI and MCP acknowledgements become bounded, with fullChangedFields compatibility documented." + }, + { + "changes": [ + "signature ./sdk:createExtensionCommandSdk", + "signature ./sdk:ExtensionCommandSdk", + "signature ./sdk:mutateWorkspaceJsonWithHistory", + "signature ./sdk:UpdateCommandOptions", + "signature ./sdk:WorkspaceJsonMutationOptions", + "signature ./sdk:WorkspaceJsonWriteOptions", + "signature ./sdk/authoring:ExtensionCommandSdk", + "signature ./sdk/runtime:UpdateCommandOptions" + ], + "package_version": "2026.9.28", + "reason": "Add audited host-bound settings mutation and full-item annotation integrity; signatures add optional controls and result fields, while extension command SDK gains one required host-provided capability." } ], "entrypoints": { @@ -5464,7 +5478,7 @@ "classification": "advanced_export", "kind": "function", "name": "createExtensionCommandSdk", - "signature": "(pmRoot: string, client: PmClient) => ExtensionCommandSdk | calls: (pmRoot: string, client: PmClient) => ExtensionCommandSdk" + "signature": "(pmRoot: string, client: PmClient, invocationAuthor?: string) => ExtensionCommandSdk | calls: (pmRoot: string, client: PmClient, invocationAuthor?: string) => ExtensionCommandSdk" }, { "classification": "supported", @@ -7306,7 +7320,7 @@ "classification": "supported", "kind": "interface", "name": "ExtensionCommandSdk", - "signature": "export interface ExtensionCommandSdk { client: PmClient; isItemNotFoundError(error: unknown): boolean; getItemAt(id: string, target: string): Promise; openRelationshipEventStore(options: { nodes: Iterable; definitions: readonly RelationshipKindDefinition[]; relativePath?: string; }): Promise; createRelationshipGraph(options: { nodes: Iterable; edges: Iterable; definitions: readonly RelationshipKindDefinition[]; }): RelationshipGraph; analyzeRelationshipImpact(graph: RelationshipGraph, root: string, options?: RelationshipQueryOptions): RelationshipImpactAnalysis; validateRelationshipEvents(options: { nodes: Iterable; definitions: readonly RelationshipKindDefinition[]; events: readonly RelationshipEventInput[]; }): RelationshipEvent[]; commitWorkspaceTransaction(options: Omit): Promise; }" + "signature": "export interface ExtensionCommandSdk { client: PmClient; isItemNotFoundError(error: unknown): boolean; getItemAt(id: string, target: string): Promise; openRelationshipEventStore(options: { nodes: Iterable; definitions: readonly RelationshipKindDefinition[]; relativePath?: string; }): Promise; createRelationshipGraph(options: { nodes: Iterable; edges: Iterable; definitions: readonly RelationshipKindDefinition[]; }): RelationshipGraph; analyzeRelationshipImpact(graph: RelationshipGraph, root: string, options?: RelationshipQueryOptions): RelationshipImpactAnalysis; validateRelationshipEvents(options: { nodes: Iterable; definitions: readonly RelationshipKindDefinition[]; events: readonly RelationshipEventInput[]; }): RelationshipEvent[]; commitWorkspaceTransaction(options: Omit): Promise; mutateWorkspaceSettings(options: { operationId: string; dryRun?: boolean; mutate: (current: PmSettings) => PmSettings | Promise; }): Promise<{ changed: boolean; dry_run: boolean; replayed: boolean; }>; }" }, { "classification": "supported", @@ -10786,7 +10800,7 @@ "classification": "advanced_export", "kind": "function", "name": "mutateWorkspaceJsonWithHistory", - "signature": "(params: WorkspaceJsonMutationOptions) => Promise<{ changed: boolean; result: Result; }> | calls: (params: WorkspaceJsonMutationOptions) => Promise<{ changed: boolean; result: Result; }>" + "signature": "(params: WorkspaceJsonMutationOptions) => Promise<{ changed: boolean; result: Result; replayed?: boolean; }> | calls: (params: WorkspaceJsonMutationOptions) => Promise<{ changed: boolean; result: Result; replayed?: boolean; }>" }, { "classification": "advanced_export", @@ -19840,7 +19854,7 @@ "classification": "advanced_export", "kind": "interface", "name": "UpdateCommandOptions", - "signature": "export interface UpdateCommandOptions extends MutationMetadataCommandOptions, SharedLinkedResourceOptions, SharedLinkedResourceClearOptions { title?: string; description?: string; body?: string; status?: string; closeReason?: string; completedAt?: string; priority?: string | number; type?: string; tags?: string; addTags?: string[]; removeTags?: string[]; addAc?: string[]; removeAc?: string[]; force?: boolean; allowMissingParent?: boolean; allowUnresolvedDeps?: boolean; depRemove?: string[]; replaceDeps?: boolean; replaceTests?: boolean; replaceFiles?: boolean; replaceDocs?: boolean; runtimeFieldCommands?: Array<\"update\" | \"update_many\">; [key: string]: unknown; }" + "signature": "export interface UpdateCommandOptions extends MutationMetadataCommandOptions, SharedLinkedResourceOptions, SharedLinkedResourceClearOptions { title?: string; description?: string; body?: string; documentAnnotationSnapshot?: Partial>; status?: string; closeReason?: string; completedAt?: string; priority?: string | number; type?: string; tags?: string; addTags?: string[]; removeTags?: string[]; addAc?: string[]; removeAc?: string[]; force?: boolean; allowMissingParent?: boolean; allowUnresolvedDeps?: boolean; depRemove?: string[]; replaceDeps?: boolean; replaceTests?: boolean; replaceFiles?: boolean; replaceDocs?: boolean; runtimeFieldCommands?: Array<\"update\" | \"update_many\">; [key: string]: unknown; }" }, { "classification": "advanced_export", @@ -20482,7 +20496,7 @@ "classification": "advanced_export", "kind": "interface", "name": "WorkspaceJsonMutationOptions", - "signature": "export interface WorkspaceJsonMutationOptions extends Omit { mutate: (beforeRaw: string | null) => WorkspaceJsonMutation | Promise>; }" + "signature": "export interface WorkspaceJsonMutationOptions extends Omit { mutate: (beforeRaw: string | null) => WorkspaceJsonMutation | Promise>; dryRun?: boolean; }" }, { "classification": "advanced_export", @@ -20506,7 +20520,7 @@ "classification": "advanced_export", "kind": "interface", "name": "WorkspaceJsonWriteOptions", - "signature": "export interface WorkspaceJsonWriteOptions { pmRoot: string; filePath: string; raw: string; op: string; author: string; lockTtlSeconds: number; lockWaitMs: number; message?: string; recordCreation?: boolean; }" + "signature": "export interface WorkspaceJsonWriteOptions { pmRoot: string; filePath: string; raw: string; op: string; idempotencyKey?: string; author: string; lockTtlSeconds: number; lockWaitMs: number; message?: string; recordCreation?: boolean; }" }, { "classification": "supported", @@ -21232,7 +21246,7 @@ "classification": "supported", "kind": "interface", "name": "ExtensionCommandSdk", - "signature": "export interface ExtensionCommandSdk { client: PmClient; isItemNotFoundError(error: unknown): boolean; getItemAt(id: string, target: string): Promise; openRelationshipEventStore(options: { nodes: Iterable; definitions: readonly RelationshipKindDefinition[]; relativePath?: string; }): Promise; createRelationshipGraph(options: { nodes: Iterable; edges: Iterable; definitions: readonly RelationshipKindDefinition[]; }): RelationshipGraph; analyzeRelationshipImpact(graph: RelationshipGraph, root: string, options?: RelationshipQueryOptions): RelationshipImpactAnalysis; validateRelationshipEvents(options: { nodes: Iterable; definitions: readonly RelationshipKindDefinition[]; events: readonly RelationshipEventInput[]; }): RelationshipEvent[]; commitWorkspaceTransaction(options: Omit): Promise; }" + "signature": "export interface ExtensionCommandSdk { client: PmClient; isItemNotFoundError(error: unknown): boolean; getItemAt(id: string, target: string): Promise; openRelationshipEventStore(options: { nodes: Iterable; definitions: readonly RelationshipKindDefinition[]; relativePath?: string; }): Promise; createRelationshipGraph(options: { nodes: Iterable; edges: Iterable; definitions: readonly RelationshipKindDefinition[]; }): RelationshipGraph; analyzeRelationshipImpact(graph: RelationshipGraph, root: string, options?: RelationshipQueryOptions): RelationshipImpactAnalysis; validateRelationshipEvents(options: { nodes: Iterable; definitions: readonly RelationshipKindDefinition[]; events: readonly RelationshipEventInput[]; }): RelationshipEvent[]; commitWorkspaceTransaction(options: Omit): Promise; mutateWorkspaceSettings(options: { operationId: string; dryRun?: boolean; mutate: (current: PmSettings) => PmSettings | Promise; }): Promise<{ changed: boolean; dry_run: boolean; replayed: boolean; }>; }" }, { "classification": "supported", @@ -34390,7 +34404,7 @@ "classification": "advanced_export", "kind": "interface", "name": "UpdateCommandOptions", - "signature": "export interface UpdateCommandOptions extends MutationMetadataCommandOptions, SharedLinkedResourceOptions, SharedLinkedResourceClearOptions { title?: string; description?: string; body?: string; status?: string; closeReason?: string; completedAt?: string; priority?: string | number; type?: string; tags?: string; addTags?: string[]; removeTags?: string[]; addAc?: string[]; removeAc?: string[]; force?: boolean; allowMissingParent?: boolean; allowUnresolvedDeps?: boolean; depRemove?: string[]; replaceDeps?: boolean; replaceTests?: boolean; replaceFiles?: boolean; replaceDocs?: boolean; runtimeFieldCommands?: Array<\"update\" | \"update_many\">; [key: string]: unknown; }" + "signature": "export interface UpdateCommandOptions extends MutationMetadataCommandOptions, SharedLinkedResourceOptions, SharedLinkedResourceClearOptions { title?: string; description?: string; body?: string; documentAnnotationSnapshot?: Partial>; status?: string; closeReason?: string; completedAt?: string; priority?: string | number; type?: string; tags?: string; addTags?: string[]; removeTags?: string[]; addAc?: string[]; removeAc?: string[]; force?: boolean; allowMissingParent?: boolean; allowUnresolvedDeps?: boolean; depRemove?: string[]; replaceDeps?: boolean; replaceTests?: boolean; replaceFiles?: boolean; replaceDocs?: boolean; runtimeFieldCommands?: Array<\"update\" | \"update_many\">; [key: string]: unknown; }" }, { "classification": "supported", @@ -35470,6 +35484,7 @@ "status_invalid", "status_lifecycle_role_required", "stdin_json_invalid", + "stdin_json_persisted_annotation_changed", "stdin_json_unknown_key", "structured_event_immutable", "structured_event_input_conflict", diff --git a/src/core/extensions/extension-types.ts b/src/core/extensions/extension-types.ts index 66e26d7302..61e130c843 100644 --- a/src/core/extensions/extension-types.ts +++ b/src/core/extensions/extension-types.ts @@ -760,6 +760,15 @@ export interface ExtensionCommandSdk { commitWorkspaceTransaction( options: Omit, ): Promise; + /** Mutate governed project settings under the host's workspace audit lock. */ + mutateWorkspaceSettings(options: { + /** Stable identity for replay-safe retries of this invocation. */ + operationId: string; + /** Compute a preview without changing settings or workspace history. */ + dryRun?: boolean; + /** Derive the complete next settings tree from the locked current tree. */ + mutate: (current: PmSettings) => PmSettings | Promise; + }): Promise<{ changed: boolean; dry_run: boolean; replayed: boolean }>; } /** Documents the parser override context payload exchanged by command, SDK, and package integrations. */ diff --git a/src/core/history/workspace-history.ts b/src/core/history/workspace-history.ts index bc9014c9a4..a7d9684699 100644 --- a/src/core/history/workspace-history.ts +++ b/src/core/history/workspace-history.ts @@ -65,6 +65,8 @@ export interface WorkspaceJsonWriteOptions { raw: string; /** Stable operation name. */ op: string; + /** Optional operation identity that prevents replaying a completed write. */ + idempotencyKey?: string; /** Attributable mutation actor. */ author: string; /** Lock time-to-live in seconds. */ @@ -94,6 +96,8 @@ export interface WorkspaceJsonMutationOptions extends Omit< mutate: ( beforeRaw: string | null, ) => WorkspaceJsonMutation | Promise>; + /** Preview the locked mutation without writing the singleton or its history. */ + dryRun?: boolean; } /** Options for one append-only workspace audit event that leaves state unchanged. */ @@ -476,6 +480,7 @@ async function writeWorkspaceJsonWithHistoryLocked( before, after, op: params.op, + idempotencyKey: params.idempotencyKey, author: params.author, lockTtlSeconds: params.lockTtlSeconds, lockWaitMs: params.lockWaitMs, @@ -499,7 +504,8 @@ async function writeWorkspaceJsonWithHistoryLocked( */ export async function mutateWorkspaceJsonWithHistory( params: WorkspaceJsonMutationOptions, -): Promise<{ changed: boolean; result: Result }> { +): Promise<{ changed: boolean; result: Result; replayed?: boolean }> { + const { documentPath } = resolveGovernedDocumentPath(params.pmRoot, params.filePath); const release = await acquireLock( params.pmRoot, "workspace-history", @@ -511,7 +517,36 @@ export async function mutateWorkspaceJsonWithHistory( ); try { const beforeRaw = await readFileIfExists(params.filePath); + if (params.dryRun === true || params.idempotencyKey !== undefined) { + const entries = await readHistoryEntries( + getWorkspaceHistoryPath(params.pmRoot), + WORKSPACE_HISTORY_ID, + ); + const verification = verifyHistoryChain(entries); + if (!verification.ok) throwWorkspaceHistoryVerificationFailure(verification.errors); + const recorded = workspaceDocuments( + entries.length === 0 + ? (EMPTY_CANONICAL_DOCUMENT as unknown as ItemDocument) + : replayWorkspaceEntries(entries), + )[documentPath]; + if (recorded !== undefined && stableStringify(recorded) !== stableStringify(beforeRaw === null ? null : JSON.parse(beforeRaw))) { + throw new PmCliError( + `Workspace history state for "${documentPath}" changed outside the audited mutation path.`, + EXIT_CODE.CONFLICT, + { code: "workspace_history_state_conflict" }, + ); + } + if (params.idempotencyKey !== undefined && entries.some( + (entry) => entry.op === `${params.op}:${params.idempotencyKey}`, + )) { + return { changed: false, result: (await params.mutate(beforeRaw)).result, replayed: true }; + } + } const mutation = await params.mutate(beforeRaw); + JSON.parse(mutation.raw); + if (params.dryRun === true) { + return { changed: beforeRaw !== mutation.raw, result: mutation.result }; + } return { changed: await writeWorkspaceJsonWithHistoryLocked( { ...params, raw: mutation.raw }, diff --git a/src/sdk/extension-command-context.ts b/src/sdk/extension-command-context.ts index fe7f6d2725..010bee53e1 100644 --- a/src/sdk/extension-command-context.ts +++ b/src/sdk/extension-command-context.ts @@ -5,7 +5,16 @@ * package runtimes never need private imports or runtime package resolution. */ import type { ExtensionCommandSdk } from "../core/extensions/extension-types.js"; +import type { PmSettings } from "../types/index.js"; +import { mutateWorkspaceJsonWithHistory } from "../core/history/workspace-history.js"; +import { resolveAuthor } from "../core/shared/author.js"; import { EXIT_CODE } from "../core/shared/constants.js"; +import { PmCliError } from "../core/shared/errors.js"; +import { stableValueEquals } from "../core/shared/serialization.js"; +import { getSettingsPath } from "../core/store/paths.js"; +import { readSettings } from "../core/store/settings.js"; +import { clearSettingsReadCache } from "../core/store/settings-read-cache.js"; +import { validateSettings } from "../core/store/settings-validator.js"; import { isPmCliExpectedError } from "./errors.js"; import { getItemAt } from "./history-read.js"; import { @@ -37,6 +46,7 @@ function buildRelationshipKindRegistry( export function createExtensionCommandSdk( pmRoot: string, client: PmClient, + invocationAuthor?: string, ): ExtensionCommandSdk { return { client, @@ -71,5 +81,56 @@ export function createExtensionCommandSdk( }, commitWorkspaceTransaction: (options) => commitWorkspaceTransaction({ ...options, pmRoot }), + mutateWorkspaceSettings: async (options) => { + if (!/^[a-zA-Z0-9._-]{1,128}$/u.test(options.operationId)) { + throw new PmCliError( + "Workspace settings operationId must be 1-128 letters, digits, dots, underscores, or hyphens.", + EXIT_CODE.USAGE, + ); + } + const settings = await readSettings(pmRoot); + try { + const mutation = await mutateWorkspaceJsonWithHistory({ + pmRoot, + filePath: getSettingsPath(pmRoot), + op: "extension:pm:settings", + idempotencyKey: options.operationId, + author: invocationAuthor ?? resolveAuthor(undefined, settings.author_default), + lockTtlSeconds: settings.locks.ttl_seconds, + lockWaitMs: settings.locks.wait_ms, + recordCreation: false, + dryRun: options.dryRun === true, + mutate: async (beforeRaw) => { + const current: unknown = beforeRaw === null ? null : JSON.parse(beforeRaw); + if (!validateSettings(current).success) { + throw new PmCliError( + "Workspace settings mutation requires a valid initialized settings.json.", + EXIT_CODE.USAGE, + ); + } + const next = await options.mutate(structuredClone(current as PmSettings)); + if (!validateSettings(next).success) { + throw new PmCliError( + "Workspace settings mutation returned invalid settings.json.", + EXIT_CODE.USAGE, + ); + } + return { + raw: stableValueEquals(current, next) + ? beforeRaw! + : `${JSON.stringify(next, null, 2)}\n`, + result: undefined, + }; + }, + }); + return { + changed: mutation.changed, + dry_run: options.dryRun === true, + replayed: mutation.replayed === true, + }; + } finally { + clearSettingsReadCache(pmRoot); + } + }, }; } 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 606fd8ef7a..d33a169da2 100644 --- a/src/sdk/generated/generated-error-code-catalog-part-2.ts +++ b/src/sdk/generated/generated-error-code-catalog-part-2.ts @@ -1143,6 +1143,19 @@ export const PM_ERROR_CODE_CATALOG_PART_2: PmErrorCodeContract[] = [ canonical_code: "stdin_json_invalid", aliases: [], }, + { + code: "stdin_json_persisted_annotation_changed", + meaning: "Stdin json persisted annotation changed condition.", + stability: "provisional", + exit_code: 2, + class: "usage", + recovery: + "Inspect the structured error guidance and retry the suggested command.", + sources: ["sdk/lifecycle/update.ts"], + emitting_commands: ["update"], + canonical_code: "stdin_json_persisted_annotation_changed", + aliases: [], + }, { code: "stdin_json_unknown_key", meaning: "Stdin json unknown key condition.", diff --git a/src/sdk/lifecycle/update.ts b/src/sdk/lifecycle/update.ts index 44ffb06e70..d70bcd6b81 100644 --- a/src/sdk/lifecycle/update.ts +++ b/src/sdk/lifecycle/update.ts @@ -152,6 +152,8 @@ export interface UpdateCommandOptions description?: string; /** Value that configures or reports body for this contract. */ body?: string; + /** Persisted annotations in a full-item JSON round trip, checked under the item lock. */ + documentAnnotationSnapshot?: Partial>; /** Lifecycle state reported for status. */ status?: string; /** Value that configures or reports close reason for this contract. */ @@ -1867,6 +1869,7 @@ function buildUpdateFieldFlags( flags.clearReminders = options.clearReminders === true; flags.clearEvents = options.clearEvents === true; flags.clearTypeOptions = options.clearTypeOptions === true; + flags.documentAnnotationSnapshot = options.documentAnnotationSnapshot !== undefined; flags.runtimeFields = Object.keys(runtimeFieldUpdates).length > 0; return flags; } @@ -3042,10 +3045,41 @@ function clearDynamicFields( } } +/** Reject edits to existing audit entries in a submitted full-item document. */ +function assertPersistedAnnotationSnapshot( + document: ItemDocument, + snapshot: UpdateCommandOptions["documentAnnotationSnapshot"], +): void { + if (snapshot === undefined) return; + const current = toItemRecord(document.metadata); + const key = (["comments", "notes", "learnings"] as const).find((field) => { + const submitted = snapshot[field]; + return submitted !== undefined && + (!Array.isArray(submitted) || + !stableValueEquals(submitted, current[field] ?? [])); + }); + if (key === undefined) return; + throw new PmCliError( + `Full-item --stdin-json update changed persisted ${key}; no item fields were written.`, + EXIT_CODE.USAGE, + { + code: "stdin_json_persisted_annotation_changed", + reason: `${key}_requires_annotation_command`, + required: `Use pm ${key} ${document.metadata.id} --edit --add to correct an existing entry.`, + why: "Full-item update appends new annotations but cannot replace or remove existing audit entries.", + nextSteps: [ + `Inspect the current ${key} with pm ${key} ${document.metadata.id}.`, + `Apply the correction with pm ${key} ${document.metadata.id} --edit --add .`, + ], + }, + ); +} + function mutateUpdateDocument( document: ItemDocument, context: UpdateMutationContext, ): { changedFields: string[]; warnings: string[] } { + assertPersistedAnnotationSnapshot(document, context.options.documentAnnotationSnapshot); const beforeMetadata = structuredClone(toItemRecord(document.metadata)); const beforeBody = document.body; const changedFields: string[] = []; diff --git a/src/sdk/merge/install.ts b/src/sdk/merge/install.ts index c4583f9f97..d5c3ba24fc 100644 --- a/src/sdk/merge/install.ts +++ b/src/sdk/merge/install.ts @@ -74,11 +74,27 @@ async function resolveMergeDriverCliCommand(): Promise { ]); let executable = process.execPath; const runtimeIdentity = await realpath(process.execPath); + const bunVersion = (process.versions as Record).bun; for (const directory of (process.env.PATH ?? "").split(path.delimiter)) { if (!path.isAbsolute(directory)) continue; - const candidate = path.join(directory, path.basename(process.execPath)); + const candidate = path.join( + directory, + bunVersion === undefined ? path.basename(process.execPath) : "bun", + ); try { await access(candidate, constants.X_OK); + if (bunVersion !== undefined) { + const { stdout } = await execFileAsync(candidate, ["--version"], { + encoding: "utf8", + windowsHide: true, + timeout: 10_000, + }); + if (stdout.trim() === bunVersion) { + executable = await realpath(candidate); + break; + } + continue; + } if ( candidate !== runtimeIdentity && (await realpath(candidate)) === runtimeIdentity @@ -342,7 +358,7 @@ async function isPortableMergeDriverCommand( if ( cliArgument === null || configured.slice(cliArgument.nextOffset) !== expectedSuffix || - !/^node(?:\.exe)?$/iu.test(path.basename(nodeArgument.value)) || + !/^(?:node|bun)(?:\.exe)?$/iu.test(path.basename(nodeArgument.value)) || path.basename(cliArgument.value) !== "cli.js" || path.basename(path.dirname(cliArgument.value)) !== "dist" ) { diff --git a/src/sdk/runtime.ts b/src/sdk/runtime.ts index a0e1b3ac06..c7a6980fa1 100644 --- a/src/sdk/runtime.ts +++ b/src/sdk/runtime.ts @@ -2759,6 +2759,10 @@ async function dispatchActiveExtensionAction( EXIT_CODE.USAGE, ); } + const extensionAuthor = + typeof global.author === "string" && global.author.trim() + ? global.author.trim() + : "pm-extension"; const handlerResult = await runActiveCommandHandler({ command: normalizeCommandPath(command), args: readStringArray(options.args ?? args.args), @@ -2769,11 +2773,9 @@ async function dispatchActiveExtensionAction( active.pmRoot, PmClient.forActiveExtensionHost({ pmRoot: active.pmRoot, - author: - typeof global.author === "string" && global.author.trim() - ? global.author.trim() - : "pm-extension", + author: extensionAuthor, }), + extensionAuthor, ), }); if (!handlerResult.handled) { diff --git a/src/sdk/structured-mutations.ts b/src/sdk/structured-mutations.ts index 8bdd28308f..ae14443472 100644 --- a/src/sdk/structured-mutations.ts +++ b/src/sdk/structured-mutations.ts @@ -844,6 +844,21 @@ export function itemDocumentToMutationOptions( } appendFacetOptions(documentOptions, item, mode === "update"); if (linked !== undefined) appendFacetOptions(documentOptions, linked); + if (mode === "update") { + const annotations = Object.fromEntries( + (["comments", "notes", "learnings"] as const) + .filter((key) => Object.hasOwn(item, key)) + .map((key) => [ + key, + (item[key] as unknown[]).filter( + (entry) => isPlainObject(entry) && entry.created_at !== undefined, + ), + ]), + ); + if (Object.keys(annotations).length > 0) { + documentOptions.documentAnnotationSnapshot = annotations; + } + } return { ...documentOptions, ...Object.fromEntries( diff --git a/tests/fixtures/contracts/full.json b/tests/fixtures/contracts/full.json index ebbaf2d1c5..6dd590c1df 100644 --- a/tests/fixtures/contracts/full.json +++ b/tests/fixtures/contracts/full.json @@ -24779,6 +24779,23 @@ ], "stability": "stable" }, + { + "aliases": [], + "canonical_code": "stdin_json_persisted_annotation_changed", + "class": "usage", + "code": "stdin_json_persisted_annotation_changed", + "emitting_commands": [ + "update" + ], + "exit_code": 2, + "meaning": "Stdin json persisted annotation changed condition.", + "owned_states": [], + "recovery": "Inspect the structured error guidance and retry the suggested command.", + "sources": [ + "sdk/lifecycle/update.ts" + ], + "stability": "provisional" + }, { "aliases": [], "canonical_code": "stdin_json_unknown_key", diff --git a/tests/integration/extension-host-contracts.integration.spec.ts b/tests/integration/extension-host-contracts.integration.spec.ts index 3530e944c9..afd4777a77 100644 --- a/tests/integration/extension-host-contracts.integration.spec.ts +++ b/tests/integration/extension-host-contracts.integration.spec.ts @@ -33,6 +33,63 @@ async function installHostContractExtension( } describe("extension host contracts", () => { + it("audits host-bound settings mutations from a copied extension without a local SDK", async () => { + await withTempPmPath(async (context) => { + await installHostContractExtension( + context.pmPath, + [ + "export default { activate(api) {", + " api.registerCommand({", + " name: 'host settings',", + " flags: [{ long: '--operation-id', value_name: 'id', value_type: 'string' }, { long: '--enabled', value_name: 'value', value_type: 'string' }, { long: '--preview', value_type: 'boolean' }],", + " run: ({ sdk, options }) => sdk.mutateWorkspaceSettings({", + " operationId: options.operationId, dryRun: options.preview === true,", + " mutate: (current) => options.enabled === 'invalid' ? { broken: true } : ({ ...current, ux: { ...current.ux, deprecation_hints: options.enabled === 'true' } }),", + " }),", + " });", + "} };", + ].join("\n"), + ["commands", "schema"], + ["host settings"], + ); + const manifestPath = path.join(context.pmPath, "extensions", "host-contract-test", "manifest.json"); + const manifest = JSON.parse(await readFile(manifestPath, "utf8")) as Record; + await writeFile(manifestPath, `${JSON.stringify({ ...manifest, pm_min_version: "2026.9.28" }, null, 2)}\n`); + expect(context.runCli(["extension", "adopt-all", "--project", "--json"]).code).toBe(0); + const settingsPath = path.join(context.pmPath, "settings.json"); + const before = await readFile(settingsPath, "utf8"); + const preview = context.runCli( + ["host", "settings", "--operation-id", "preview-1", "--enabled", "false", "--preview", "--json"], + { expectJson: true }, + ); + expect(preview, preview.stderr).toMatchObject({ code: 0, json: { changed: true, dry_run: true, replayed: false } }); + expect(await readFile(settingsPath, "utf8")).toBe(before); + + const applied = context.runCli( + ["host", "settings", "--operation-id", "apply-1", "--enabled", "false", "--json"], + { expectJson: true }, + ); + expect(applied).toMatchObject({ code: 0, json: { changed: true, dry_run: false, replayed: false } }); + const persisted = await readFile(settingsPath, "utf8"); + expect(JSON.parse(persisted)).toMatchObject({ ux: { deprecation_hints: false } }); + const replay = context.runCli( + ["host", "settings", "--operation-id", "apply-1", "--enabled", "true", "--json"], + { expectJson: true }, + ); + expect(replay).toMatchObject({ code: 0, json: { changed: false, replayed: true } }); + expect(await readFile(settingsPath, "utf8")).toBe(persisted); + + const invalid = context.runCli( + ["host", "settings", "--operation-id", "invalid-1", "--enabled", "invalid", "--json"], + ); + expect(invalid.code).toBe(2); + expect(await readFile(settingsPath, "utf8")).toBe(persisted); + expect(context.runCli(["history", "_workspace", "--verify", "--json"], { expectJson: true }).code).toBe(0); + const health = context.runCli(["health", "--strict-exit", "--json"], { expectJson: true }); + expect(health.code, JSON.stringify(health.json)).toBe(0); + }); + }); + it("preserves package-local explain and rejects reserved-flag activation atomically", async () => { await withTempPmPath(async (context) => { await installHostContractExtension(context.pmPath, [ diff --git a/tests/integration/structured-mutation-io.integration.spec.ts b/tests/integration/structured-mutation-io.integration.spec.ts index fa2dbbbdba..6ac49ecb1d 100644 --- a/tests/integration/structured-mutation-io.integration.spec.ts +++ b/tests/integration/structured-mutation-io.integration.spec.ts @@ -334,6 +334,25 @@ describe("structured SDK/CLI/MCP mutation IO", () => { ]), }, }); + const editedDocument = structuredClone(roundTripped.json) as { + item: { notes: Array<{ text: string }> }; + }; + editedDocument.item.notes[0]!.text = "Corrected evidence"; + const rejected = context.runCli( + ["update", id, "--stdin-json", "--json"], + { input: JSON.stringify(editedDocument), expectJson: true }, + ); + expect(rejected.code).toBe(2); + expect(JSON.parse(rejected.stderr)).toMatchObject({ + code: "stdin_json_persisted_annotation_changed", + required: expect.stringContaining("pm notes"), + }); + expect(context.runCli(["get", id, "--full", "--json"], { + expectJson: true, + }).json).toMatchObject({ item: { notes: [expect.objectContaining({ text: "-" })] } }); + expect(context.runCli(["history", id, "--verify", "--json"], { + expectJson: true, + }).code).toBe(0); const full = context.runCli( [ diff --git a/tests/unit/core/history/operation-source-contract.spec.ts b/tests/unit/core/history/operation-source-contract.spec.ts index 57926d0059..5c85d7c84b 100644 --- a/tests/unit/core/history/operation-source-contract.spec.ts +++ b/tests/unit/core/history/operation-source-contract.spec.ts @@ -164,5 +164,5 @@ describe("native operation write-site contract", () => { ), ); expect(findings.flat()).toEqual([]); - }); + }, 120_000); }); diff --git a/tests/unit/core/history/workspace-history.spec.ts b/tests/unit/core/history/workspace-history.spec.ts index dbf5db467b..bbc7bbae87 100644 --- a/tests/unit/core/history/workspace-history.spec.ts +++ b/tests/unit/core/history/workspace-history.spec.ts @@ -16,6 +16,7 @@ import { inspectWorkspaceHistoryState, reconcileWorkspaceJsonHistory, restoreWorkspaceJsonFromHistory, + mutateWorkspaceJsonWithHistory, writeWorkspaceJsonWithHistory, WORKSPACE_HISTORY_ID, } from "../../../../src/core/history/workspace-history.js"; @@ -717,6 +718,75 @@ describe("workspace history", () => { }); }); + it("previews, replays, and compensates lock-scoped singleton mutations", async () => { + await withTempPmPath(async (context) => { + const filePath = path.join(context.pmPath, "extension-state.json"); + const common = { + pmRoot: context.pmPath, + filePath, + op: "extension:state", + author: "extension-agent", + lockTtlSeconds: 30, + lockWaitMs: 1000, + }; + await writeWorkspaceJsonWithHistory({ ...common, raw: '{"enabled":false}\n' }); + const initialHistory = await readFile(getWorkspaceHistoryPath(context.pmPath), "utf8"); + const change = (enabled: boolean) => ({ raw: `${JSON.stringify({ enabled })}\n`, result: enabled }); + await expect(mutateWorkspaceJsonWithHistory({ + ...common, dryRun: true, idempotencyKey: "preview-1", mutate: () => change(true), + })).resolves.toMatchObject({ changed: true, result: true }); + expect(await readFile(filePath, "utf8")).toBe('{"enabled":false}\n'); + expect(await readFile(getWorkspaceHistoryPath(context.pmPath), "utf8")).toBe(initialHistory); + + await expect(mutateWorkspaceJsonWithHistory({ + ...common, idempotencyKey: "apply-1", mutate: () => change(true), + })).resolves.toMatchObject({ changed: true, result: true }); + const appliedHistory = await readFile(getWorkspaceHistoryPath(context.pmPath), "utf8"); + await expect(mutateWorkspaceJsonWithHistory({ + ...common, idempotencyKey: "apply-1", mutate: () => change(false), + })).resolves.toMatchObject({ changed: false, replayed: true }); + expect(await readFile(filePath, "utf8")).toBe('{"enabled":true}\n'); + expect(await readFile(getWorkspaceHistoryPath(context.pmPath), "utf8")).toBe(appliedHistory); + + const appendSpy = vi.spyOn(historyModule, "appendHistoryEntry").mockRejectedValueOnce(new Error("injected-history-failure")); + try { + await expect(mutateWorkspaceJsonWithHistory({ + ...common, idempotencyKey: "apply-2", mutate: () => change(false), + })).rejects.toThrow("injected-history-failure"); + } finally { + appendSpy.mockRestore(); + } + expect(await readFile(filePath, "utf8")).toBe('{"enabled":true}\n'); + expect(await readFile(getWorkspaceHistoryPath(context.pmPath), "utf8")).toBe(appliedHistory); + + await appendFile(getWorkspaceHistoryPath(context.pmPath), '{"ts":"broken"}\n'); + await expect(mutateWorkspaceJsonWithHistory({ + ...common, dryRun: true, mutate: () => change(false), + })).rejects.toMatchObject({ code: "workspace_history_chain_invalid" }); + }); + }); + + it("previews a legacy singleton before its workspace audit stream exists", async () => { + await withTempPmPath(async (context) => { + const historyPath = getWorkspaceHistoryPath(context.pmPath); + await rm(historyPath, { force: true }); + const filePath = path.join(context.pmPath, "legacy-state.json"); + await writeFile(filePath, '{"enabled":false}\n'); + await expect(mutateWorkspaceJsonWithHistory({ + pmRoot: context.pmPath, + filePath, + op: "extension:state", + author: "extension-agent", + lockTtlSeconds: 30, + lockWaitMs: 1000, + dryRun: true, + mutate: () => ({ raw: '{"enabled":true}\n', result: true }), + })).resolves.toMatchObject({ changed: true, result: true }); + expect(await readFile(filePath, "utf8")).toBe('{"enabled":false}\n'); + await expect(readFile(historyPath, "utf8")).rejects.toMatchObject({ code: "ENOENT" }); + }); + }); + it("serializes competing settings snapshots with their audit entries", async () => { await withTempPmPath(async (context) => { const settings = await readSettings(context.pmPath); diff --git a/tests/unit/sdk/merge/worktree-config.spec.ts b/tests/unit/sdk/merge/worktree-config.spec.ts index b798f2f6b1..a8b5b39736 100644 --- a/tests/unit/sdk/merge/worktree-config.spec.ts +++ b/tests/unit/sdk/merge/worktree-config.spec.ts @@ -1,5 +1,5 @@ import { execFileSync } from "node:child_process"; -import { mkdir, writeFile, rm } from "node:fs/promises"; +import { chmod, mkdir, writeFile, rm } from "node:fs/promises"; import { gitWorkspaceEnvironment, resolveMergeDriverConfigScope } from "../../../../src/sdk/merge/worktree-config.js"; import path from "node:path"; import { describe, expect, it } from "vitest"; @@ -7,6 +7,35 @@ import { installMergeFence, auditMergeDriverConfiguration, findGitWorkspaceRoot import { withTempPmPath } from "../../../helpers/withTempPmPath.js"; describe("worktree-local merge drivers", () => { + it("selects a stable Bun launcher and accepts its installed driver commands", async () => { + if (process.platform === "win32") return; + await withTempPmPath(async ({ tempRoot, pmPath }) => { + execFileSync("git", ["init", "-q"], { cwd: tempRoot, env: gitWorkspaceEnvironment() }); + const staleDir = path.join(tempRoot, "stale-runtime"); + const stableDir = path.join(tempRoot, "stable-runtime"); + await mkdir(staleDir); + await mkdir(stableDir); + await writeFile(path.join(staleDir, "bun"), "#!/bin/sh\nprintf 'old\\n'\n"); + await writeFile(path.join(stableDir, "bun"), `#!/bin/sh\nprintf '${process.version}\\n'\n`); + await chmod(path.join(staleDir, "bun"), 0o755); + await chmod(path.join(stableDir, "bun"), 0o755); + const originalPath = process.env.PATH; + const bunDescriptor = Object.getOwnPropertyDescriptor(process.versions, "bun"); + try { + process.env.PATH = [staleDir, stableDir, originalPath].join(path.delimiter); + Object.defineProperty(process.versions, "bun", { value: process.version, configurable: true }); + const installed = await installMergeFence({ workspaceRoot: tempRoot, pmRoot: pmPath }); + expect(installed.git_config.find((entry) => entry.key === "merge.pm-history.driver")?.value) + .toContain(`'${path.join(stableDir, "bun")}'`); + expect(await auditMergeDriverConfiguration(tempRoot)).toMatchObject({ status: "ok" }); + } finally { + process.env.PATH = originalPath; + if (bunDescriptor === undefined) delete (process.versions as Record).bun; + else Object.defineProperty(process.versions, "bun", bunDescriptor); + } + }); + }); + it("binds discovery, installation and auditing to the requested repository despite inherited Git locations", async () => { await withTempPmPath(async ({ tempRoot, pmPath }) => { const foreign = path.join(tempRoot, "foreign"); diff --git a/tests/unit/sdk/sdk-extension-mutation-platform.spec.ts b/tests/unit/sdk/sdk-extension-mutation-platform.spec.ts index b5279f5077..2cec3d4506 100644 --- a/tests/unit/sdk/sdk-extension-mutation-platform.spec.ts +++ b/tests/unit/sdk/sdk-extension-mutation-platform.spec.ts @@ -1,4 +1,4 @@ -import { readFile } from "node:fs/promises"; +import { readFile, rm, writeFile } from "node:fs/promises"; import path from "node:path"; import { describe, expect, it, vi } from "vitest"; import { PmCliError } from "../../../src/core/shared/errors.js"; @@ -17,8 +17,111 @@ import { } from "../../../src/sdk/index.js"; import { withTempPmPath } from "../../helpers/withTempPmPath.js"; import { writeTestExtension } from "../../helpers/extensions.js"; +import { readHistoryEntries } from "../../../src/core/history/read.js"; +import { + getWorkspaceHistoryPath, + WORKSPACE_HISTORY_ID, +} from "../../../src/core/history/workspace-history.js"; +import type { PmSettings } from "../../../src/types/index.js"; describe("extension mutation platform", () => { + it("previews, audits, and replays host-bound settings without losing invalid-state refusals", async () => { + await withTempPmPath(async ({ pmPath }) => { + const settingsPath = path.join(pmPath, "settings.json"); + const before = await readFile(settingsPath, "utf8"); + const historyPath = getWorkspaceHistoryPath(pmPath); + const sdk = createExtensionCommandSdk( + pmPath, + new PmClient({ pmRoot: pmPath, noExtensions: true }), + "extension-test-actor", + ); + const nextAuthor = (current: PmSettings): PmSettings => ({ + ...current, + author_default: "extension-owned-author", + }); + await expect(sdk.mutateWorkspaceSettings({ + operationId: "bad key", + mutate: nextAuthor, + })).rejects.toMatchObject({ exitCode: 2 }); + expect(await readFile(settingsPath, "utf8")).toBe(before); + + await expect(sdk.mutateWorkspaceSettings({ + operationId: "author-v1", + dryRun: true, + mutate: nextAuthor, + })).resolves.toEqual({ changed: true, dry_run: true, replayed: false }); + expect(await readFile(settingsPath, "utf8")).toBe(before); + expect(await readHistoryEntries(historyPath, WORKSPACE_HISTORY_ID)).toHaveLength(0); + + await expect(sdk.mutateWorkspaceSettings({ + operationId: "author-v1", + mutate: nextAuthor, + })).resolves.toEqual({ changed: true, dry_run: false, replayed: false }); + expect(JSON.parse(await readFile(settingsPath, "utf8"))).toMatchObject({ + author_default: "extension-owned-author", + }); + await expect(sdk.mutateWorkspaceSettings({ + operationId: "author-v1", + mutate: nextAuthor, + })).resolves.toEqual({ changed: false, dry_run: false, replayed: true }); + await expect(sdk.mutateWorkspaceSettings({ + operationId: "author-noop", + mutate: (current) => current, + })).resolves.toEqual({ changed: false, dry_run: false, replayed: false }); + const history = await readHistoryEntries(historyPath, WORKSPACE_HISTORY_ID); + expect(history).toHaveLength(1); + expect(history[0]).toMatchObject({ + op: "extension:pm:settings:author-v1", + author: "extension-test-actor", + }); + + await expect(sdk.mutateWorkspaceSettings({ + operationId: "invalid-next", + mutate: () => ({ broken: true }) as unknown as PmSettings, + })).rejects.toThrow(/invalid settings/); + expect(await readHistoryEntries(historyPath, WORKSPACE_HISTORY_ID)).toHaveLength(1); + + const defaultActorSdk = createExtensionCommandSdk( + pmPath, + new PmClient({ pmRoot: pmPath, noExtensions: true }), + ); + await expect(defaultActorSdk.mutateWorkspaceSettings({ + operationId: "author-v2", + mutate: (current) => ({ ...current, author_default: "next-default" }), + })).resolves.toMatchObject({ changed: true, replayed: false }); + expect((await readHistoryEntries(historyPath, WORKSPACE_HISTORY_ID))[1]).toMatchObject({ + author: "test-author", + }); + + await writeFile(settingsPath, "{}\n"); + await expect(sdk.mutateWorkspaceSettings({ + operationId: "invalid-current", + mutate: nextAuthor, + })).rejects.toMatchObject({ code: "workspace_history_state_conflict" }); + await rm(settingsPath); + await expect(sdk.mutateWorkspaceSettings({ + operationId: "missing-current", + mutate: nextAuthor, + })).rejects.toMatchObject({ code: "workspace_history_state_conflict" }); + }); + await withTempPmPath(async ({ pmPath }) => { + await rm(path.join(pmPath, "settings.json")); + const sdk = createExtensionCommandSdk( + pmPath, + new PmClient({ pmRoot: pmPath, noExtensions: true }), + ); + await expect(sdk.mutateWorkspaceSettings({ + operationId: "missing-initial-settings", + mutate: (current) => current, + })).rejects.toThrow(/valid initialized settings/); + await writeFile(path.join(pmPath, "settings.json"), "{}\n"); + await expect(sdk.mutateWorkspaceSettings({ + operationId: "invalid-initial-settings", + mutate: (current) => current, + })).rejects.toThrow(/valid initialized settings/); + }); + }); + it("injects a real host-bound SDK into command harness dispatch", async () => { await withTempPmPath(async ({ pmPath }) => { const client = new PmClient({ pmRoot: pmPath, noExtensions: true }); From 78bd2065916e460030b9119a0bd78be9163ba280 Mon Sep 17 00:00:00 2001 From: Stefan Preu Date: Tue, 29 Sep 2026 10:10:37 +0200 Subject: [PATCH 2/2] Harden audited settings mutations after review Keep replay callbacks inert, scope extension operation IDs to their command, serialize settings through the canonical writer, and notify write hooks for changed state. Record the public SDK receipt contract and regression acceptance for copied extensions and malformed annotation input. Complete the linked PM defect gate evidence, exact coverage and static verification, and refreshed reviewed documentation. --- .agents/pm/history/pm-2589e6.jsonl | 7 ++ .agents/pm/history/pm-vks66s.jsonl | 4 + .agents/pm/history/pm-wtqltn.jsonl | 6 ++ .agents/pm/issues/pm-2589e6.toon | 71 +++++++++++++++--- .agents/pm/issues/pm-vks66s.toon | 14 +++- .agents/pm/issues/pm-wtqltn.toon | 75 +++++++++++++++---- docs/SDK_AUDITED_MUTATIONS.md | 11 ++- sdk/public-surface.json | 12 ++- src/cli/main.ts | 8 +- src/core/history/workspace-history.ts | 18 ++++- src/sdk/extension-command-context.ts | 18 ++++- src/sdk/runtime.ts | 1 + src/sdk/testing.ts | 2 +- ...tension-host-contracts.integration.spec.ts | 16 +++- .../core/history/workspace-history.spec.ts | 4 +- .../sdk-extension-mutation-platform.spec.ts | 33 +++++++- tests/unit/sdk/structured-mutations.spec.ts | 13 ++++ 17 files changed, 265 insertions(+), 48 deletions(-) diff --git a/.agents/pm/history/pm-2589e6.jsonl b/.agents/pm/history/pm-2589e6.jsonl index 29771b9dc8..029ae1fab3 100644 --- a/.agents/pm/history/pm-2589e6.jsonl +++ b/.agents/pm/history/pm-2589e6.jsonl @@ -15,3 +15,10 @@ {"hash_algorithm":"sha256","ts":"2026-09-29T06:50:53.644Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"close","patch":[{"op":"replace","path":"/metadata/actual_result","value":"Disposable structured-mutation regression rejects edited note with unchanged verified history; linked test, full suite, exact coverage, and static quality pass."},{"op":"replace","path":"/metadata/expected_result","value":"Edited existing notes cannot silently succeed or change item state."},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:50:53.644Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/closed_at","value":"2026-09-29T06:50:53.619Z"},{"op":"add","path":"/metadata/completed_at","value":"2026-09-29T06:50:53.619Z"},{"op":"add","path":"/metadata/resolution","value":"Full-item JSON updates now compare persisted comments, notes, and learnings under the item lock and return a stable actionable refusal for edits or removals; new entries still append."},{"op":"add","path":"/metadata/close_reason","value":"Reject persisted annotation edits in full-item JSON updates"}],"before_hash":"1056074ba76b888bb68bc59166a421f48c56a84f350947a4af393fb8b3d7ed87","after_hash":"f9ecc88fc6f648bff3bcb2ef7012c3bc137f287196019198810f1b2292772450","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":"b751179dea2adbe715859a81caa96c58f83ab9bf04052c3787f21cefd69a92bc"} {"hash_algorithm":"sha256","ts":"2026-09-29T06:50:54.234Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-2589e6+pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-2589e6","claim:pm-vks66s","claim:pm-wtqltn","lineage:pm-vks66s","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:50:54.234Z"}],"before_hash":"f9ecc88fc6f648bff3bcb2ef7012c3bc137f287196019198810f1b2292772450","after_hash":"4293eec66cf0444495919842a264ca028f610203c44a40db6a9d1a6cea04bed4","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":"601700e456bc705604594176db45533ba8124c4b1f71803f54c899be3ebe6dea"} {"hash_algorithm":"sha256","ts":"2026-09-29T06:53:29.820Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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/2/note","value":"documentation index"},{"op":"replace","path":"/metadata/docs/2/path","value":"docs/README.md"},{"op":"replace","path":"/metadata/docs/1/note","value":"generated refusal recovery contract"},{"op":"replace","path":"/metadata/docs/1/path","value":"docs/generated/REFUSAL_CLOSURE_CENSUS.md"},{"op":"replace","path":"/metadata/docs/0/note","value":"generated release entry"},{"op":"replace","path":"/metadata/docs/0/path","value":"CHANGELOG.md"},{"op":"add","path":"/metadata/docs/3","value":{"path":"docs/SDK_AUDITED_MUTATIONS.md","scope":"project","note":"full-item JSON correction contract"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:53:29.820Z"}],"before_hash":"4293eec66cf0444495919842a264ca028f610203c44a40db6a9d1a6cea04bed4","after_hash":"641f44ae5fa16c25bf1a087b6c666259291d206dab7259c9cf00dbab82585a60","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":"b323cea78d6758d1e44df84b5b6ef44a17bd0815c96cebce0d3973fc076da75b"} +{"hash_algorithm":"sha256","ts":"2026-09-29T07:13:46.203Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-29T07:13:46.203Z"},{"op":"add","path":"/metadata/escape_class","value":"production_defect"},{"op":"add","path":"/metadata/gate_evidence","value":{"disposition":"gate_strengthened","gate_id":"Gates (static)","negative_control":"node scripts/run-tests.mjs test -- tests/unit/sdk/structured-mutations.spec.ts","local_checks":["node scripts/run-tests.mjs test -- tests/unit/sdk/structured-mutations.spec.ts","node scripts/run-tests.mjs coverage"],"hosted_checks":["Gates (static)","Gates (typecheck)"],"owner":"pm-2589e6"}}],"before_hash":"641f44ae5fa16c25bf1a087b6c666259291d206dab7259c9cf00dbab82585a60","after_hash":"45c06f11805425ef81ae6530d32175fd0cf2763da6ed642262ffbc8e408cfdb2","item_hash_version":3,"message":"Review gate evidence: full-item annotation mutation regression is linked to executable local and hosted checks.","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":"a4037fafc1abcc447ec28d7497c5b8a172ce3b0c01226e6b55fe6baa990240a1"} +{"hash_algorithm":"sha256","ts":"2026-09-29T07:17:18.050Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-29T07:17:18.050Z","author":"harness:codex","text":"PR #1348 review follow-up: malformed full-item annotation arrays now return a PM usage error instead of an unhandled TypeError. Focused parser tests cover all three annotation fields with user-visible diagnostics; these exercise an independent invalid-input boundary rather than mirroring the implementation."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T07:17:18.050Z"}],"before_hash":"45c06f11805425ef81ae6530d32175fd0cf2763da6ed642262ffbc8e408cfdb2","after_hash":"c970450e77f5b5d5b34b2d350f69e2f43614ffeac6c1c7a58491f4f3aef057f8","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":"2ac3257879363dfdd892c6d7787c5b993458e0ca0021d8e9d97a1e6e402221ab"} +{"hash_algorithm":"sha256","ts":"2026-09-29T07:17:47.690Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"add","path":"/metadata/files/5","value":{"path":"tests/unit/sdk/structured-mutations.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T07:17:47.690Z"}],"before_hash":"c970450e77f5b5d5b34b2d350f69e2f43614ffeac6c1c7a58491f4f3aef057f8","after_hash":"7f82c05094d3b1912eaf4b3714660ef3c5f88537803f97b75f11731aaea326a1","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":"9428aefa08760d837db90cc24dfff93152941249820c0f593a8bb53397eae62c"} +{"hash_algorithm":"sha256","ts":"2026-09-29T07:17:48.305Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"tests_add","patch":[{"op":"add","path":"/metadata/tests/1","value":{"command":"node scripts/run-tests.mjs test -- tests/unit/sdk/structured-mutations.spec.ts","scope":"project","provenance":{"author":"harness:codex","created_at":"2026-09-29T07:17:48.246Z","source_kind":"local_mutation","source_ref":"fix/audited-workspace-mutations-bun-merge"}}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T07:17:48.305Z"}],"before_hash":"7f82c05094d3b1912eaf4b3714660ef3c5f88537803f97b75f11731aaea326a1","after_hash":"910b9e0a380ebdb5aa8662b119f3f50ca61390d136017bbf04165fd19c632460","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":"5bd753c2c54697358e3cf9be97c9e0b7a5fc24c9d96c5fe0e9eae4c450ed10b0"} +{"hash_algorithm":"sha256","ts":"2026-09-29T07:42:04.396Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-29T07:42:04.396Z","author":"harness:codex","text":"Correction to prior review note: the existing appendFacetOptions -> serializeObjectArray path already refuses null comments/notes/learnings with a field-specific PmCliError before the persisted-snapshot filter. The suggested extra guard was unreachable and failed the exact 100% coverage gate, so it was removed. New regression assertions verify the existing usage code and field guidance on all three inputs; no production behavior change was needed for this bot finding."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T07:42:04.396Z"}],"before_hash":"910b9e0a380ebdb5aa8662b119f3f50ca61390d136017bbf04165fd19c632460","after_hash":"bb9eabcce0e82d85e17afe54fff58bcafda32dc68b8f96bb0af9e7932871b148","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":"bca794a28de36d8ff9ae9efc5ac1953af6f5dac6d09d953385daed7cbe17c391"} +{"hash_algorithm":"sha256","ts":"2026-09-29T08:06:25.154Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"test_run_track","patch":[{"op":"add","path":"/metadata/test_runs/1","value":{"run_id":"test-local-mume762d-n9s2b9","kind":"test","status":"passed","started_at":"2026-09-29T08:05:55.987Z","finished_at":"2026-09-29T08:06:25.141Z","recorded_at":"2026-09-29T08:06:25.141Z","passed":2,"failed":0,"skipped":0,"executions":[{"command":"node scripts/run-tests.mjs test -- tests/integration/structured-mutation-io.integration.spec.ts","requested_pm_context_mode":"schema","pm_context_mode":"schema","workspace_context_mode":"source","trust_reason":"local_source_ref"},{"command":"node scripts/run-tests.mjs test -- tests/unit/sdk/structured-mutations.spec.ts","requested_pm_context_mode":"schema","pm_context_mode":"schema","workspace_context_mode":"source","trust_reason":"local_source_ref"}]}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T08:06:25.154Z"}],"before_hash":"bb9eabcce0e82d85e17afe54fff58bcafda32dc68b8f96bb0af9e7932871b148","after_hash":"84b21385f5da2a0d8cd4fad8b9b49214b6227760e45551ada57cd691074916b8","item_hash_version":3,"message":"Track test run summary (test-local-mume762d-n9s2b9)","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":"6fb36a028a0cbbccb4d41eac28ac8585229e271794a994c177243c9bd0963e55"} +{"hash_algorithm":"sha256","ts":"2026-09-29T08:09:24.273Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-29T08:09:24.273Z","author":"harness:codex","text":"Final PR #1348 review verification: all 9414 tests passed, exact V8 coverage is 100/100/100/100, pnpm quality:static and pnpm security:scan passed, and both linked structured-mutation integration and parser unit tests passed through pm test in an isolated schema tracker. Null annotation arrays produce the existing typed usage refusal; the extra guard suggested in review was removed as unreachable."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T08:09:24.273Z"}],"before_hash":"84b21385f5da2a0d8cd4fad8b9b49214b6227760e45551ada57cd691074916b8","after_hash":"171617b583df375b2017a8e9e5beeaf2508621528a3264652a17ccae68b2f2ab","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":"2d00c3fd225ba112422738a6a6ec519bbd94f4f1060ae2c3ee6e7b081456e739"} diff --git a/.agents/pm/history/pm-vks66s.jsonl b/.agents/pm/history/pm-vks66s.jsonl index 9f8984c11e..6b42831f01 100644 --- a/.agents/pm/history/pm-vks66s.jsonl +++ b/.agents/pm/history/pm-vks66s.jsonl @@ -14,3 +14,7 @@ {"hash_algorithm":"sha256","ts":"2026-09-29T06:50:57.744Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-vks66s","release:pm-wtqltn"]},"topic":{"value":"pm-vks66s","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-vks66s","release:pm-wtqltn"]}},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:50:57.744Z"}],"before_hash":"3ad6fcc5cf4f6a755467b3e4d1e9a1182bc96daf2023f6b03965aa0d10ca6491","after_hash":"439947347e270f0e75c7a41fa6a36720ccc85fcfb0d52ffb4c82075a4e4676ea","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":"69b29a8ec9d089a92393ab0f1d56b47d31f118eb3601a28b16f944522fd306ab"} {"hash_algorithm":"sha256","ts":"2026-09-29T06:53:31.228Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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 release entry"},{"op":"replace","path":"/metadata/docs/0/path","value":"CHANGELOG.md"},{"op":"add","path":"/metadata/docs/1","value":{"path":"docs/MERGE_SAFETY.md","scope":"project","note":"Bun merge-driver contract"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:53:31.228Z"}],"before_hash":"439947347e270f0e75c7a41fa6a36720ccc85fcfb0d52ffb4c82075a4e4676ea","after_hash":"c65008574240b5cca12f2bc658fa71ae44e2491b6af707b1d1961087d61cd02b","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":"b437ebc01a0122ad745313ae889000266bd41b3fe8055bcd8da30d087defa805"} {"hash_algorithm":"sha256","ts":"2026-09-29T06:58:37.384Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-29T06:58:37.384Z","author":"harness:codex","text":"Post-rebase packed acceptance on v2026.9.29: fresh tarball installed with Bun in a disposable Git project, bunx --bun pm init succeeded, bunx --bun pm health --strict-exit reported ok with zero findings, and both npx --no-install pm --version and bunx --no-install pm --version returned 2026.9.29. The earlier disposable project also completed a real semantic item/history Git merge with a verified chain."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:58:37.384Z"}],"before_hash":"c65008574240b5cca12f2bc658fa71ae44e2491b6af707b1d1961087d61cd02b","after_hash":"c6fec14a8858fe970d5527645b900bef1cf52127a943ba6665e2b8e1905140cf","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":"a964a8edeb9c75d6793eb2141c68d89c24449b853b92d1cfadd7230753496567"} +{"hash_algorithm":"sha256","ts":"2026-09-29T07:13:46.236Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-29T07:13:46.236Z"},{"op":"add","path":"/metadata/escape_class","value":"production_defect"},{"op":"add","path":"/metadata/gate_evidence","value":{"disposition":"gate_strengthened","gate_id":"Gates (static)","negative_control":"node scripts/run-tests.mjs test -- tests/unit/sdk/merge-install.spec.ts","local_checks":["node scripts/run-tests.mjs test -- tests/unit/sdk/merge-install.spec.ts","node scripts/run-tests.mjs coverage"],"hosted_checks":["Gates (static)","Gates (typecheck)"],"owner":"pm-vks66s"}}],"before_hash":"c6fec14a8858fe970d5527645b900bef1cf52127a943ba6665e2b8e1905140cf","after_hash":"869248437d519a5a770af1df413b802f9f6162c5880d057658829b6963c94841","item_hash_version":3,"message":"Review gate evidence: Bun merge-driver regression is linked to executable local and hosted checks.","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":"ec3f4f003c4227f84e270245af8505489106d5e89023e91ea368aaae9e1a276b"} +{"hash_algorithm":"sha256","ts":"2026-09-29T07:15:21.324Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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/gate_evidence/local_checks/0","value":"node scripts/run-tests.mjs test -- tests/unit/sdk/merge/runtime-launcher.spec.ts"},{"op":"replace","path":"/metadata/gate_evidence/negative_control","value":"node scripts/run-tests.mjs test -- tests/unit/sdk/merge/runtime-launcher.spec.ts"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T07:15:21.324Z"}],"before_hash":"869248437d519a5a770af1df413b802f9f6162c5880d057658829b6963c94841","after_hash":"7873d59b8aa5ca24f1c91329cb571d74c030161eafe7434ea8621a0bb95eec2e","item_hash_version":3,"message":"Correct the executable Bun merge launcher gate path to the committed runtime launcher spec.","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":"077e67393aeaa4d9582f12f48d8004ff4d8d59ecf73af919581f4811f2886257"} +{"hash_algorithm":"sha256","ts":"2026-09-29T07:17:18.123Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-29T07:17:18.123Z","author":"harness:codex","text":"PR #1348 static gate follow-up: added structured production-defect escape and executable Bun merge-launcher regression evidence; corrected the linked gate path to the committed spec. Repository defect-evidence gate passes with zero findings across 262 governed terminal defects."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T07:17:18.123Z"}],"before_hash":"7873d59b8aa5ca24f1c91329cb571d74c030161eafe7434ea8621a0bb95eec2e","after_hash":"eba5671f2f1b7812a42042320071a8de179a0ef8d7c5b9dc3f02d159ecf6becf","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":"4c8dcd198506ce88acb06b3403f385ccbc957dd8aab1676013d460242f51bc0f"} +{"hash_algorithm":"sha256","ts":"2026-09-29T08:09:24.178Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-29T08:09:24.177Z","author":"harness:codex","text":"Final PR #1348 review verification: full suite 9414 passed, exact V8 coverage 100/100/100/100, static and security gates passed. Fresh npm-packed CLI installed under /tmp/pm-1348-packed-aE4P4Y; npx and bunx both reported 2026.9.29, and Bun strict health returned zero findings. Earlier temporary Git merge acceptance proved the Bun item/history drivers preserve both sides and verify history."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T08:09:24.178Z"}],"before_hash":"eba5671f2f1b7812a42042320071a8de179a0ef8d7c5b9dc3f02d159ecf6becf","after_hash":"b7d556e12652b1e0d0486bda0dc1f97c0dc5005489e69a6863ae827f9bacfe49","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":"1eca11d946a609883586c63b93f90730d4ee75a0068f5af155c91912e0ceaa2d"} diff --git a/.agents/pm/history/pm-wtqltn.jsonl b/.agents/pm/history/pm-wtqltn.jsonl index ee22a1e434..2740943914 100644 --- a/.agents/pm/history/pm-wtqltn.jsonl +++ b/.agents/pm/history/pm-wtqltn.jsonl @@ -17,3 +17,9 @@ {"hash_algorithm":"sha256","ts":"2026-09-29T06:50:56.009Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-vks66s","claim:pm-wtqltn","release:pm-2589e6"]},"topic":{"value":"workset:pm-vks66s+pm-wtqltn","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-vks66s","claim:pm-wtqltn","release:pm-2589e6"]}},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:50:56.009Z"}],"before_hash":"561094b23ee82bc2cb7e8d4e0c9f3871abc4948a17bd96ad35c44f4565606772","after_hash":"85a61fd2ed77368b05f17ecace5320645e86e9f337957487a9b8d1d31db90cd3","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":"cfcf6d55e9b517553b77918a1133d3ce1ce91ab03f30f1ff11676d68cdb4f8e2"} {"hash_algorithm":"sha256","ts":"2026-09-29T06:53:30.498Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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/1/note","value":"documentation index"},{"op":"replace","path":"/metadata/docs/1/path","value":"docs/README.md"},{"op":"replace","path":"/metadata/docs/0/note","value":"generated release entry"},{"op":"replace","path":"/metadata/docs/0/path","value":"CHANGELOG.md"},{"op":"add","path":"/metadata/docs/2","value":{"path":"docs/SDK_AUDITED_MUTATIONS.md","scope":"project","note":"extension settings API"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:53:30.498Z"}],"before_hash":"85a61fd2ed77368b05f17ecace5320645e86e9f337957487a9b8d1d31db90cd3","after_hash":"0e8b6389a0ab9d60e036ac2ab8a31a20b0c098caf8332e8dc7adb678e8df5981","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":"bbd0d703d1bc9df77d7580ebaeea30b3f06febfdb99e4f56835609297bdc439e"} {"hash_algorithm":"sha256","ts":"2026-09-29T06:53:32.125Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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/8/note","value":"history atomicity regression"},{"op":"replace","path":"/metadata/files/8/path","value":"tests/unit/core/history/workspace-history.spec.ts"},{"op":"replace","path":"/metadata/files/7/note","value":"instrumented operation census timeout"},{"op":"replace","path":"/metadata/files/7/path","value":"tests/unit/core/history/operation-source-contract.spec.ts"},{"op":"replace","path":"/metadata/files/6/note","value":"copied-extension acceptance"},{"op":"replace","path":"/metadata/files/6/path","value":"tests/integration/extension-host-contracts.integration.spec.ts"},{"op":"replace","path":"/metadata/files/5/note","value":"caller attribution"},{"op":"replace","path":"/metadata/files/5/path","value":"src/sdk/runtime.ts"},{"op":"replace","path":"/metadata/files/4/note","value":"host-bound mutation"},{"op":"replace","path":"/metadata/files/4/path","value":"src/sdk/extension-command-context.ts"},{"op":"replace","path":"/metadata/files/3/note","value":"audited preview and replay"},{"op":"replace","path":"/metadata/files/3/path","value":"src/core/history/workspace-history.ts"},{"op":"replace","path":"/metadata/files/2/note","value":"typed host capability"},{"op":"replace","path":"/metadata/files/2/path","value":"src/core/extensions/extension-types.ts"},{"op":"replace","path":"/metadata/files/1/note","value":"versioned public SDK capability"},{"op":"replace","path":"/metadata/files/1/path","value":"sdk/public-surface.json"},{"op":"replace","path":"/metadata/files/0/note","value":"latest pm-changelog package install metadata for reviewed changelog"},{"op":"replace","path":"/metadata/files/0/path","value":".agents/pm/extensions/.managed-extensions.json"},{"op":"add","path":"/metadata/files/9","value":{"path":"tests/unit/sdk/sdk-extension-mutation-platform.spec.ts","scope":"project","note":"source-level settings mutation coverage"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T06:53:32.125Z"}],"before_hash":"0e8b6389a0ab9d60e036ac2ab8a31a20b0c098caf8332e8dc7adb678e8df5981","after_hash":"1f7c130aef1c4d0bd941345d8c4fea48b602da17a99fc92ff64088eca6e465ca","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":"2c3148c2b29bd8d8f4ec2f4a8f1c911ca9c1907d4c6da97e07e32cdc33165914"} +{"hash_algorithm":"sha256","ts":"2026-09-29T07:13:31.552Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-29T07:13:31.552Z"},{"op":"add","path":"/metadata/escape_class","value":"production_defect"},{"op":"add","path":"/metadata/gate_evidence","value":{"disposition":"gate_strengthened","gate_id":"Gates (static)","negative_control":"node scripts/run-tests.mjs test -- tests/unit/sdk/sdk-extension-mutation-platform.spec.ts","local_checks":["node scripts/run-tests.mjs test -- tests/unit/sdk/sdk-extension-mutation-platform.spec.ts","node scripts/run-tests.mjs coverage"],"hosted_checks":["Gates (static)","Gates (typecheck)"],"owner":"pm-wtqltn"}}],"before_hash":"1f7c130aef1c4d0bd941345d8c4fea48b602da17a99fc92ff64088eca6e465ca","after_hash":"6160fb1ece22e321493e36dffd9d5b2d72ebc53f3a0d59e2f316f65f9790ef1a","item_hash_version":3,"message":"Review gate evidence: host settings mutation regression acceptance is linked to executable local and hosted checks.","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":"fadc0d0fc60bc9fe5d6ae2790c79f8b4667e1eaa890ff60b5023f1090bedf441"} +{"hash_algorithm":"sha256","ts":"2026-09-29T07:17:18.091Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-29T07:17:18.090Z","author":"harness:codex","text":"PR #1348 review follow-up: the host SDK now keys settings retries by registered command and operation ID; completed retries do not re-run extension code. Settings mutations use the standard serializer and emit onWrite after changed writes. Focused unit and installed-extension integration tests exercise cross-command IDs, legacy format and collection normalization, and hook behavior. pm-test-audit: each case checks externally observable state or history and would fail against the reviewed implementation; no implementation-only assertions or duplicated happy-path coverage."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T07:17:18.091Z"}],"before_hash":"6160fb1ece22e321493e36dffd9d5b2d72ebc53f3a0d59e2f316f65f9790ef1a","after_hash":"c5012a204e4e117dd84589df38e5556a8afcc7eb322aa79d897f144c3240eb0a","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":"96d960507942377c4a35860d3312e42cf8161bc05867868a7d19c59980433329"} +{"hash_algorithm":"sha256","ts":"2026-09-29T07:19:18.384Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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/9/note","value":"instrumented operation census timeout"},{"op":"replace","path":"/metadata/files/9/path","value":"tests/unit/core/history/operation-source-contract.spec.ts"},{"op":"replace","path":"/metadata/files/8/note","value":"copied-extension acceptance"},{"op":"replace","path":"/metadata/files/8/path","value":"tests/integration/extension-host-contracts.integration.spec.ts"},{"op":"remove","path":"/metadata/files/7/note"},{"op":"replace","path":"/metadata/files/7/path","value":"src/sdk/testing.ts"},{"op":"replace","path":"/metadata/files/6/note","value":"caller attribution"},{"op":"replace","path":"/metadata/files/6/path","value":"src/sdk/runtime.ts"},{"op":"replace","path":"/metadata/files/5/note","value":"host-bound mutation"},{"op":"replace","path":"/metadata/files/5/path","value":"src/sdk/extension-command-context.ts"},{"op":"replace","path":"/metadata/files/4/note","value":"audited preview and replay"},{"op":"replace","path":"/metadata/files/4/path","value":"src/core/history/workspace-history.ts"},{"op":"replace","path":"/metadata/files/3/note","value":"typed host capability"},{"op":"replace","path":"/metadata/files/3/path","value":"src/core/extensions/extension-types.ts"},{"op":"remove","path":"/metadata/files/2/note"},{"op":"replace","path":"/metadata/files/2/path","value":"src/cli/main.ts"},{"op":"add","path":"/metadata/files/10","value":{"path":"tests/unit/core/history/workspace-history.spec.ts","scope":"project","note":"history atomicity regression"}},{"op":"add","path":"/metadata/files/11","value":{"path":"tests/unit/sdk/sdk-extension-mutation-platform.spec.ts","scope":"project","note":"source-level settings mutation coverage"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T07:19:18.384Z"}],"before_hash":"c5012a204e4e117dd84589df38e5556a8afcc7eb322aa79d897f144c3240eb0a","after_hash":"620a1c6abfca88bac7f89234802357ec04ef8d1d047c3689640dcef84dd47d20","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":"ce7d9ca008919ccca0019906b81a2cfc332fdd023b5f6bfb7a9b3175bc2d7bea"} +{"hash_algorithm":"sha256","ts":"2026-09-29T07:30:33.241Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-29T07:30:33.241Z","author":"harness:codex","text":"SDK surface review: the replay receipt now exposes an explicit replay variant with no callback-derived result, and createExtensionCommandSdk accepts optional command context. sdk/public-surface.json records both signatures with a breaking-change acknowledgement because the snapshot classifier treats signature changes conservatively; existing three-argument callers remain executable."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T07:30:33.241Z"}],"before_hash":"620a1c6abfca88bac7f89234802357ec04ef8d1d047c3689640dcef84dd47d20","after_hash":"a30469f71377512efa3425ddbe1bf51715ecbd233e015d71864dbfad1949b5ec","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":"8cb61a834c37cf1d6581e4e9e8a60142224baddf445c8d9de5b9545daa057d2b"} +{"hash_algorithm":"sha256","ts":"2026-09-29T08:08:49.032Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"test_run_track","patch":[{"op":"add","path":"/metadata/test_runs/1","value":{"run_id":"test-local-mumea933-x5koq5","kind":"test","status":"passed","started_at":"2026-09-29T08:08:21.943Z","finished_at":"2026-09-29T08:08:49.023Z","recorded_at":"2026-09-29T08:08:49.023Z","passed":2,"failed":0,"skipped":0,"executions":[{"command":"node scripts/run-tests.mjs test -- tests/integration/extension-host-contracts.integration.spec.ts tests/unit/core/history/workspace-history.spec.ts","requested_pm_context_mode":"schema","pm_context_mode":"schema","workspace_context_mode":"source","trust_reason":"local_source_ref"},{"command":"node scripts/run-tests.mjs test -- tests/unit/sdk/sdk-extension-mutation-platform.spec.ts","requested_pm_context_mode":"schema","pm_context_mode":"schema","workspace_context_mode":"source","trust_reason":"local_source_ref"}]}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T08:08:49.032Z"}],"before_hash":"a30469f71377512efa3425ddbe1bf51715ecbd233e015d71864dbfad1949b5ec","after_hash":"29c6524280deb0b6fc3580127c9cbcef0430e50be7ee75c430264d6f1b7e5d5d","item_hash_version":3,"message":"Track test run summary (test-local-mumea933-x5koq5)","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":"05494f9f9e01e7ebdadf6fb553a25b3428204ea3867f2589c396a4bd302106e5"} +{"hash_algorithm":"sha256","ts":"2026-09-29T08:09:24.196Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"4035266b6ea871edfae828b3","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-29T08:09:24.196Z","author":"harness:codex","text":"Final PR #1348 review verification: all 9414 tests passed, exact V8 coverage is 100/100/100/100 with zero uncovered counts, pnpm quality:static passed including exported docstrings and 97.88% mutation score, pnpm security:scan passed, and both linked test commands passed in schema-sandboxed pm test. A fresh npm-packed archive installed in /tmp/pm-1348-packed-aE4P4Y, where npx and bunx reported 2026.9.29 and strict health returned zero findings. Installed-extension acceptance verifies independent same-ID commands, inert replay, serializer normalization, and changed-write hook delivery."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T08:09:24.196Z"}],"before_hash":"29c6524280deb0b6fc3580127c9cbcef0430e50be7ee75c430264d6f1b7e5d5d","after_hash":"6bac5a1feca29227d89400dfd1d33f26fa1ddc82cbd91b9a6d749b6b171c1b7b","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":"d0551b50dbb70b9bb61c8576bf2540a22c6c9a0910b7d072d440ace52be21011"} diff --git a/.agents/pm/issues/pm-2589e6.toon b/.agents/pm/issues/pm-2589e6.toon index 613fa67a46..bca2149db2 100644 --- a/.agents/pm/issues/pm-2589e6.toon +++ b/.agents/pm/issues/pm-2589e6.toon @@ -6,7 +6,7 @@ status: closed priority: 2 tags: [] created_at: "2026-09-29T00:19:58.543Z" -updated_at: "2026-09-29T06:53:29.820Z" +updated_at: "2026-09-29T08:09:24.273Z" closed_at: "2026-09-29T06:50:53.619Z" completed_at: "2026-09-29T06:50:53.619Z" author: "harness:codex" @@ -24,21 +24,50 @@ component: CLI structured mutations dependencies[2]{id,kind,created_at,author,source_kind,author_source}: pm-a2h3,related,"2026-09-29T00:19:58.543Z","harness:codex","cli:create:dep",detected pm-kipd,discovered_from,"2026-09-29T00:19:58.543Z","harness:codex","cli:create:dep",detected -comments[3]{created_at,author,text}: +comments[6]{created_at,author,text}: "2026-09-29T00:19:58.543Z","harness:codex","Duplicate check: searched all-status PM items for stdin-json and notes; closed pm-kipd delivered the original round-trip feature and pm-a2h3 delivered direct note editing, but neither tracks this new silent no-op defect. GitHub issue #1343 has no prior PM link." "2026-09-29T04:56:30.520Z","harness:codex","Test-audit contract: a changed persisted note in get->modify->update --stdin-json must never return a successful no-op. Existing structured round-trip test only appends a new note and does not detect edits to a persisted annotation. Primary proof will use a disposable tracker and history verification; no test-only production seam." "2026-09-29T06:50:36.685Z","harness:codex","Acceptance evidence: full-item get to update --stdin-json now preserves persisted comments, notes, and learnings as immutable snapshots and rejects edited or removed entries under the item lock with stdin_json_persisted_annotation_changed. The structured-mutation integration test proves the formerly silent edited-note no-op fails without changing item or history, while legitimate new annotations still append. Linked test passed; full suite 9414 passed; exact V8 coverage 100/100/100/100; pnpm quality:static passed. Manual correction remains pm notes/comments/learnings --edit with audit history." + "2026-09-29T07:17:18.050Z","harness:codex","PR #1348 review follow-up: malformed full-item annotation arrays now return a PM usage error instead of an unhandled TypeError. Focused parser tests cover all three annotation fields with user-visible diagnostics; these exercise an independent invalid-input boundary rather than mirroring the implementation." + "2026-09-29T07:42:04.396Z","harness:codex","Correction to prior review note: the existing appendFacetOptions -> serializeObjectArray path already refuses null comments/notes/learnings with a field-specific PmCliError before the persisted-snapshot filter. The suggested extra guard was unreachable and failed the exact 100% coverage gate, so it was removed. New regression assertions verify the existing usage code and field guidance on all three inputs; no production behavior change was needed for this bot finding." + "2026-09-29T08:09:24.273Z","harness:codex","Final PR #1348 review verification: all 9414 tests passed, exact V8 coverage is 100/100/100/100, pnpm quality:static and pnpm security:scan passed, and both linked structured-mutation integration and parser unit tests passed through pm test in an isolated schema tracker. Null annotation arrays produce the existing typed usage refusal; the extra guard suggested in review was removed as unreachable." learnings[1]{created_at,author,text}: "2026-09-29T06:50:37.375Z","harness:codex","A full-item JSON round trip must distinguish new annotation entries from persisted audit entries and verify the persisted snapshot inside the locked mutation, including annotation-only updates." -files[5]{path,scope,note}: - src/sdk/generated/generated-error-code-catalog-part-2.ts,project,stable annotation refusal catalog - src/sdk/lifecycle/update.ts,project,locked annotation integrity - src/sdk/structured-mutations.ts,project,full-item annotation snapshot - tests/fixtures/contracts/full.json,project,generated command contract snapshot - tests/integration/structured-mutation-io.integration.spec.ts,project,regression acceptance -tests[1]{command,scope,timeout_seconds,provenance{author,created_at,source_kind,source_ref}}: - node scripts/run-tests.mjs test -- tests/integration/structured-mutation-io.integration.spec.ts,project,600,"harness:codex","2026-09-29T05:13:55.996Z",local_mutation,fix/audited-workspace-mutations-bun-merge -test_runs[1]: +files[6]: + - path: src/sdk/generated/generated-error-code-catalog-part-2.ts + scope: project + note: stable annotation refusal catalog + - path: src/sdk/lifecycle/update.ts + scope: project + note: locked annotation integrity + - path: src/sdk/structured-mutations.ts + scope: project + note: full-item annotation snapshot + - path: tests/fixtures/contracts/full.json + scope: project + note: generated command contract snapshot + - path: tests/integration/structured-mutation-io.integration.spec.ts + scope: project + note: regression acceptance + - path: tests/unit/sdk/structured-mutations.spec.ts + scope: project +tests[2]: + - command: node scripts/run-tests.mjs test -- tests/integration/structured-mutation-io.integration.spec.ts + scope: project + timeout_seconds: 600 + provenance: + author: "harness:codex" + created_at: "2026-09-29T05:13:55.996Z" + source_kind: local_mutation + source_ref: fix/audited-workspace-mutations-bun-merge + - command: node scripts/run-tests.mjs test -- tests/unit/sdk/structured-mutations.spec.ts + scope: project + provenance: + author: "harness:codex" + created_at: "2026-09-29T07:17:48.246Z" + source_kind: local_mutation + source_ref: fix/audited-workspace-mutations-bun-merge +test_runs[2]: - run_id: test-local-mumbe394-q4gosn kind: test status: passed @@ -50,10 +79,30 @@ test_runs[1]: 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/structured-mutation-io.integration.spec.ts,schema,schema,source,local_source_ref + - run_id: test-local-mume762d-n9s2b9 + kind: test + status: passed + started_at: "2026-09-29T08:05:55.987Z" + finished_at: "2026-09-29T08:06:25.141Z" + recorded_at: "2026-09-29T08:06:25.141Z" + passed: 2 + failed: 0 + skipped: 0 + executions[2]{command,requested_pm_context_mode,pm_context_mode,workspace_context_mode,trust_reason}: + node scripts/run-tests.mjs test -- tests/integration/structured-mutation-io.integration.spec.ts,schema,schema,source,local_source_ref + node scripts/run-tests.mjs test -- tests/unit/sdk/structured-mutations.spec.ts,schema,schema,source,local_source_ref docs[4]{path,scope,note}: CHANGELOG.md,project,generated release entry docs/generated/REFUSAL_CLOSURE_CENSUS.md,project,generated refusal recovery contract docs/README.md,project,documentation index docs/SDK_AUDITED_MUTATIONS.md,project,full-item JSON correction contract close_reason: Reject persisted annotation edits in full-item JSON updates +escape_class: production_defect +gate_evidence: + disposition: gate_strengthened + gate_id: Gates (static) + negative_control: node scripts/run-tests.mjs test -- tests/unit/sdk/structured-mutations.spec.ts + local_checks[2]: node scripts/run-tests.mjs test -- tests/unit/sdk/structured-mutations.spec.ts,node scripts/run-tests.mjs coverage + hosted_checks[2]: Gates (static),Gates (typecheck) + owner: pm-2589e6 body: "GitHub: https://github.com/unbraind/pm-cli/issues/1343\n\nReproduce with a disposable tracker: create an item with a note, get the full JSON item, edit notes[0].text, and pipe it to update --stdin-json. Require either an atomic audited note correction or a nonzero field-specific rejection with pm notes --edit guidance." diff --git a/.agents/pm/issues/pm-vks66s.toon b/.agents/pm/issues/pm-vks66s.toon index 6132f4524a..80566f41b6 100644 --- a/.agents/pm/issues/pm-vks66s.toon +++ b/.agents/pm/issues/pm-vks66s.toon @@ -6,7 +6,7 @@ status: closed priority: 2 tags: [] created_at: "2026-09-29T00:20:04.660Z" -updated_at: "2026-09-29T06:58:37.384Z" +updated_at: "2026-09-29T08:09:24.178Z" closed_at: "2026-09-29T06:50:57.064Z" completed_at: "2026-09-29T06:50:57.064Z" author: "harness:codex" @@ -23,11 +23,13 @@ affected_version: 2026.9.28 component: Bun merge driver dependencies[1]{id,kind,created_at,author,source_kind,author_source}: pm-rnl3sa,related,"2026-09-29T00:20:04.660Z","harness:codex","cli:create:dep",detected -comments[4]{created_at,author,text}: +comments[6]{created_at,author,text}: "2026-09-29T00:20:04.660Z","harness:codex","Duplicate check: all-status PM search for Bun merge-driver drift found the closed package-runner dispatch issue pm-rnl3sa and other merge work, but no owner for this Bun-specific install/health mismatch." "2026-09-29T04:56:31.027Z","harness:codex","Test-audit contract: Bun-installed merge drivers must pass strict health, remain idempotent, and execute a real merge. Existing merge safety tests cover Node driver syntax and worktree identity, not Bun's executable name or bunx runtime path. Primary proof will use a disposable Git/Bun workspace." "2026-09-29T06:50:40.724Z","harness:codex","Acceptance evidence: actual Bun 1.4.2 in a disposable npm-packed consumer installed an absolute /usr/bin/bun merge driver; bunx --bun init and repeated merge install both passed strict health with zero findings. A real Git branch merge invoked item and history drivers, retained the left note and right description, and pm history --verify returned three valid entries. npx --no-install pm and bunx --no-install pm both returned the packed CLI version. Linked regression test passed; full suite 9414 passed; exact V8 coverage 100/100/100/100; pnpm quality:static passed. Repository source npm audit found zero advisories and GitHub open Dependabot, code, and secret scanning alerts were empty." "2026-09-29T06:58:37.384Z","harness:codex","Post-rebase packed acceptance on v2026.9.29: fresh tarball installed with Bun in a disposable Git project, bunx --bun pm init succeeded, bunx --bun pm health --strict-exit reported ok with zero findings, and both npx --no-install pm --version and bunx --no-install pm --version returned 2026.9.29. The earlier disposable project also completed a real semantic item/history Git merge with a verified chain." + "2026-09-29T07:17:18.123Z","harness:codex","PR #1348 static gate follow-up: added structured production-defect escape and executable Bun merge-launcher regression evidence; corrected the linked gate path to the committed spec. Repository defect-evidence gate passes with zero findings across 262 governed terminal defects." + "2026-09-29T08:09:24.177Z","harness:codex","Final PR #1348 review verification: full suite 9414 passed, exact V8 coverage 100/100/100/100, static and security gates passed. Fresh npm-packed CLI installed under /tmp/pm-1348-packed-aE4P4Y; npx and bunx both reported 2026.9.29, and Bun strict health returned zero findings. Earlier temporary Git merge acceptance proved the Bun item/history drivers preserve both sides and verify history." learnings[1]{created_at,author,text}: "2026-09-29T06:50:41.415Z","harness:codex",Bun can report a versioned executable path while Git later needs a durable absolute launcher; resolve and validate bun from absolute PATH entries at install time and audit both Node and Bun driver identities. files[2]{path,scope,note}: @@ -51,4 +53,12 @@ docs[2]{path,scope,note}: CHANGELOG.md,project,generated release entry docs/MERGE_SAFETY.md,project,Bun merge-driver contract close_reason: Repair Bun merge-driver launcher identity +escape_class: production_defect +gate_evidence: + disposition: gate_strengthened + gate_id: Gates (static) + negative_control: node scripts/run-tests.mjs test -- tests/unit/sdk/merge/runtime-launcher.spec.ts + local_checks[2]: node scripts/run-tests.mjs test -- tests/unit/sdk/merge/runtime-launcher.spec.ts,node scripts/run-tests.mjs coverage + hosted_checks[2]: Gates (static),Gates (typecheck) + owner: pm-vks66s body: "GitHub: https://github.com/unbraind/pm-cli/issues/1346\n\nReproduce in a disposable Bun Git project; compare installed Git driver commands against strict health expectation, prove idempotent reinstall, and execute a real two-branch item merge using the registered driver." diff --git a/.agents/pm/issues/pm-wtqltn.toon b/.agents/pm/issues/pm-wtqltn.toon index f93f003957..6d72ea3608 100644 --- a/.agents/pm/issues/pm-wtqltn.toon +++ b/.agents/pm/issues/pm-wtqltn.toon @@ -6,7 +6,7 @@ status: closed priority: 2 tags: [] created_at: "2026-09-29T00:20:00.805Z" -updated_at: "2026-09-29T06:53:32.125Z" +updated_at: "2026-09-29T08:09:24.196Z" closed_at: "2026-09-29T06:50:55.143Z" completed_at: "2026-09-29T06:50:55.143Z" author: "harness:codex" @@ -24,27 +24,54 @@ component: SDK extension command context dependencies[2]{id,kind,created_at,author,source_kind,author_source}: pm-klo8,discovered_from,"2026-09-29T00:20:00.805Z","harness:codex","cli:create:dep",detected pm-lvea,related,"2026-09-29T00:20:00.805Z","harness:codex","cli:create:dep",detected -comments[3]{created_at,author,text}: +comments[6]{created_at,author,text}: "2026-09-29T00:20:00.805Z","harness:codex","Duplicate check: all-status PM search for audited extension settings mutation found related closed workspace-audit and SDK audits but no current owner of this missing host-bound contract. GitHub #1345 cites pm-presets pm-1vx6 in that package's tracker; that is a separate consumer defect." "2026-09-29T04:56:31.683Z","harness:codex","Test-audit contract: a copied extension with no locally resolvable pm SDK can atomically mutate governed settings through its injected host SDK while retaining strict workspace history. Existing direct SDK singleton tests do not prove extension portability or failure compensation. Primary proof will use a disposable packed consumer." "2026-09-29T06:50:38.926Z","harness:codex","Acceptance evidence: extension command SDK now exposes mutateWorkspaceSettings with a fixed host tracker path, lock-scoped read/validate/write, workspace history, author attribution, dry-run, and replay-safe operation IDs. Copied extension integration runs without a local SDK and passes strict health/history verification. Source-level tests cover preview, replay, no-op, invalid current/result, drift, and history failure compensation. Linked tests passed; full suite 9414 passed; exact V8 coverage 100/100/100/100; pnpm quality:static passed. Public SDK surface snapshot acknowledges the added host capability and optional controls." + "2026-09-29T07:17:18.090Z","harness:codex","PR #1348 review follow-up: the host SDK now keys settings retries by registered command and operation ID; completed retries do not re-run extension code. Settings mutations use the standard serializer and emit onWrite after changed writes. Focused unit and installed-extension integration tests exercise cross-command IDs, legacy format and collection normalization, and hook behavior. pm-test-audit: each case checks externally observable state or history and would fail against the reviewed implementation; no implementation-only assertions or duplicated happy-path coverage." + "2026-09-29T07:30:33.241Z","harness:codex","SDK surface review: the replay receipt now exposes an explicit replay variant with no callback-derived result, and createExtensionCommandSdk accepts optional command context. sdk/public-surface.json records both signatures with a breaking-change acknowledgement because the snapshot classifier treats signature changes conservatively; existing three-argument callers remain executable." + "2026-09-29T08:09:24.196Z","harness:codex","Final PR #1348 review verification: all 9414 tests passed, exact V8 coverage is 100/100/100/100 with zero uncovered counts, pnpm quality:static passed including exported docstrings and 97.88% mutation score, pnpm security:scan passed, and both linked test commands passed in schema-sandboxed pm test. A fresh npm-packed archive installed in /tmp/pm-1348-packed-aE4P4Y, where npx and bunx reported 2026.9.29 and strict health returned zero findings. Installed-extension acceptance verifies independent same-ID commands, inert replay, serializer normalization, and changed-write hook delivery." learnings[1]{created_at,author,text}: "2026-09-29T06:50:39.821Z","harness:codex",Copied extensions must receive governed workspace mutations through host-injected SDK services; resolving local SDK packages or writing settings.json directly breaks portability and audit attribution. -files[10]{path,scope,note}: - .agents/pm/extensions/.managed-extensions.json,project,latest pm-changelog package install metadata for reviewed changelog - sdk/public-surface.json,project,versioned public SDK capability - src/core/extensions/extension-types.ts,project,typed host capability - src/core/history/workspace-history.ts,project,audited preview and replay - src/sdk/extension-command-context.ts,project,host-bound mutation - src/sdk/runtime.ts,project,caller attribution - tests/integration/extension-host-contracts.integration.spec.ts,project,copied-extension acceptance - tests/unit/core/history/operation-source-contract.spec.ts,project,instrumented operation census timeout - tests/unit/core/history/workspace-history.spec.ts,project,history atomicity regression - tests/unit/sdk/sdk-extension-mutation-platform.spec.ts,project,source-level settings mutation coverage +files[12]: + - path: .agents/pm/extensions/.managed-extensions.json + scope: project + note: latest pm-changelog package install metadata for reviewed changelog + - path: sdk/public-surface.json + scope: project + note: versioned public SDK capability + - path: src/cli/main.ts + scope: project + - path: src/core/extensions/extension-types.ts + scope: project + note: typed host capability + - path: src/core/history/workspace-history.ts + scope: project + note: audited preview and replay + - path: src/sdk/extension-command-context.ts + scope: project + note: host-bound mutation + - path: src/sdk/runtime.ts + scope: project + note: caller attribution + - path: src/sdk/testing.ts + scope: project + - path: tests/integration/extension-host-contracts.integration.spec.ts + scope: project + note: copied-extension acceptance + - path: tests/unit/core/history/operation-source-contract.spec.ts + scope: project + note: instrumented operation census timeout + - path: tests/unit/core/history/workspace-history.spec.ts + scope: project + note: history atomicity regression + - path: tests/unit/sdk/sdk-extension-mutation-platform.spec.ts + scope: project + note: source-level settings mutation coverage tests[2]{command,scope,timeout_seconds,provenance{author,created_at,source_kind,source_ref}}: node scripts/run-tests.mjs test -- tests/integration/extension-host-contracts.integration.spec.ts tests/unit/core/history/workspace-history.spec.ts,project,600,"harness:codex","2026-09-29T05:13:59.218Z",local_mutation,fix/audited-workspace-mutations-bun-merge node scripts/run-tests.mjs test -- tests/unit/sdk/sdk-extension-mutation-platform.spec.ts,project,600,"harness:codex","2026-09-29T06:15:41.923Z",local_mutation,fix/audited-workspace-mutations-bun-merge -test_runs[1]: +test_runs[2]: - run_id: test-local-mumbeylv-rahy0q kind: test status: passed @@ -57,9 +84,29 @@ test_runs[1]: executions[2]{command,requested_pm_context_mode,pm_context_mode,workspace_context_mode,trust_reason}: node scripts/run-tests.mjs test -- tests/integration/extension-host-contracts.integration.spec.ts tests/unit/core/history/workspace-history.spec.ts,schema,schema,source,local_source_ref node scripts/run-tests.mjs test -- tests/unit/sdk/sdk-extension-mutation-platform.spec.ts,schema,schema,source,local_source_ref + - run_id: test-local-mumea933-x5koq5 + kind: test + status: passed + started_at: "2026-09-29T08:08:21.943Z" + finished_at: "2026-09-29T08:08:49.023Z" + recorded_at: "2026-09-29T08:08:49.023Z" + passed: 2 + failed: 0 + skipped: 0 + executions[2]{command,requested_pm_context_mode,pm_context_mode,workspace_context_mode,trust_reason}: + node scripts/run-tests.mjs test -- tests/integration/extension-host-contracts.integration.spec.ts tests/unit/core/history/workspace-history.spec.ts,schema,schema,source,local_source_ref + node scripts/run-tests.mjs test -- tests/unit/sdk/sdk-extension-mutation-platform.spec.ts,schema,schema,source,local_source_ref docs[3]{path,scope,note}: CHANGELOG.md,project,generated release entry docs/README.md,project,documentation index docs/SDK_AUDITED_MUTATIONS.md,project,extension settings API close_reason: Deliver audited host-bound extension settings mutations +escape_class: production_defect +gate_evidence: + disposition: gate_strengthened + gate_id: Gates (static) + negative_control: node scripts/run-tests.mjs test -- tests/unit/sdk/sdk-extension-mutation-platform.spec.ts + local_checks[2]: node scripts/run-tests.mjs test -- tests/unit/sdk/sdk-extension-mutation-platform.spec.ts,node scripts/run-tests.mjs coverage + hosted_checks[2]: Gates (static),Gates (typecheck) + owner: pm-wtqltn body: "GitHub: https://github.com/unbraind/pm-cli/issues/1345\n\nExpose a typed extension-command SDK operation for lock-scoped audited singleton mutation with invocation identity, atomic write and history, compensation, idempotence, and dry-run. Prove a packed extension without a local host SDK dependency can apply settings and pass strict health." diff --git a/docs/SDK_AUDITED_MUTATIONS.md b/docs/SDK_AUDITED_MUTATIONS.md index 9ae62e7ad0..b343ff6e32 100644 --- a/docs/SDK_AUDITED_MUTATIONS.md +++ b/docs/SDK_AUDITED_MUTATIONS.md @@ -27,13 +27,18 @@ api.registerCommand({ }); ``` -Supply a stable `operationId` for a logical operation. Retrying an already -recorded operation returns `replayed: true` without appending another history -event. `dryRun: true` calculates `changed` without changing settings or history; +Supply a stable `operationId` for a logical operation within its registered +command. The host scopes the ID to that command, so another command can use +the same ID independently. Retrying an already recorded operation returns +`replayed: true` without invoking `mutate`, writing settings, or appending +another history event. `dryRun: true` calculates `changed` without changing settings or history; the same operation ID can then be used for the real mutation. A normal return also reports `changed` and `dry_run`. Invalid results fail before writing, and a history-append failure restores the previous settings bytes. The callback must derive its complete next settings object from the locked `current` value. +The host applies the standard settings serializer, including legacy format +coercion and collection-name sanitization, and runs active `onWrite` hooks +after a changed write. ## Full-item JSON updates diff --git a/sdk/public-surface.json b/sdk/public-surface.json index b13b1dbe6a..c77db5e4db 100644 --- a/sdk/public-surface.json +++ b/sdk/public-surface.json @@ -2126,6 +2126,14 @@ ], "package_version": "2026.9.28", "reason": "Add audited host-bound settings mutation and full-item annotation integrity; signatures add optional controls and result fields, while extension command SDK gains one required host-provided capability." + }, + { + "changes": [ + "signature ./sdk:createExtensionCommandSdk", + "signature ./sdk:mutateWorkspaceJsonWithHistory" + ], + "package_version": "2026.9.29", + "reason": "Command-scoped host settings retries and explicit replay receipt prevent cross-command collisions and callback reexecution; the optional command context is backward compatible at runtime." } ], "entrypoints": { @@ -5478,7 +5486,7 @@ "classification": "advanced_export", "kind": "function", "name": "createExtensionCommandSdk", - "signature": "(pmRoot: string, client: PmClient, invocationAuthor?: string) => ExtensionCommandSdk | calls: (pmRoot: string, client: PmClient, invocationAuthor?: string) => ExtensionCommandSdk" + "signature": "(pmRoot: string, client: PmClient, invocationAuthor?: string, invocationCommand?: string) => ExtensionCommandSdk | calls: (pmRoot: string, client: PmClient, invocationAuthor?: string, invocationCommand?: string) => ExtensionCommandSdk" }, { "classification": "supported", @@ -10800,7 +10808,7 @@ "classification": "advanced_export", "kind": "function", "name": "mutateWorkspaceJsonWithHistory", - "signature": "(params: WorkspaceJsonMutationOptions) => Promise<{ changed: boolean; result: Result; replayed?: boolean; }> | calls: (params: WorkspaceJsonMutationOptions) => Promise<{ changed: boolean; result: Result; replayed?: boolean; }>" + "signature": "{ (params: WorkspaceJsonMutationOptions & { idempotencyKey?: undefined; }): Promise<{ changed: boolean; result: Result; replayed?: false; }>; (params: WorkspaceJsonMutationOptions): Promise<{ changed: boolean; result: Result; replayed?: false; } | { changed: false; result: undefined; replayed: true; }>; } | calls: (params: WorkspaceJsonMutationOptions & { idempotencyKey?: undefined; }) => Promise<{ changed: boolean; result: Result; replayed?: false; }> ; (params: WorkspaceJsonMutationOptions) => Promise<{ changed: boolean; result: Result; replayed?: false; } | { changed: false; result: undefined; replayed: true; }>" }, { "classification": "advanced_export", diff --git a/src/cli/main.ts b/src/cli/main.ts index f5c43c4841..72861ceb78 100644 --- a/src/cli/main.ts +++ b/src/cli/main.ts @@ -1010,9 +1010,9 @@ async function executeRegisteredRuntimeMigrations(migrations: RegisteredExtensio /* c8 ignore start */ /** Build one host-bound extension SDK using the command's resolved author. */ -function buildExtensionCommandSdk(pmRoot: string, global: GlobalOptions) { +function buildExtensionCommandSdk(pmRoot: string, global: GlobalOptions, commandPath: string) { const author = typeof global.author === "string" && global.author.trim() ? global.author.trim() : "pm-extension"; - return createExtensionCommandSdk(pmRoot, PmClient.forActiveExtensionHost({ pmRoot, author })); + return createExtensionCommandSdk(pmRoot, PmClient.forActiveExtensionHost({ pmRoot, author }), author, normalizeExtensionCommandPath(commandPath)); } async function runRequiredExtensionCommand( @@ -1059,7 +1059,7 @@ async function runRequiredExtensionCommand( options: commandOptions, global: resolvedGlobalOptions, pm_root: pmRoot, - sdk: buildExtensionCommandSdk(pmRoot, resolvedGlobalOptions), + sdk: buildExtensionCommandSdk(pmRoot, resolvedGlobalOptions, commandPath), }); if (resolvedGlobalOptions.profile && extensionCommandResult.warnings.length > 0) { printError(`profile:extensions command_handler_warnings=${formatHookWarnings(extensionCommandResult.warnings)}`); @@ -1197,7 +1197,7 @@ function wrapProgramActionsForExtensionHandlers(rootProgram: Command): void { options: commandOptions, global: globalOptions, pm_root: pmRoot, - sdk: buildExtensionCommandSdk(pmRoot, globalOptions), + sdk: buildExtensionCommandSdk(pmRoot, globalOptions, commandPath), }); maybePrintExtensionProfileWarnings(globalOptions.profile, "command_handler_warnings", extensionCommandResult.warnings); if (extensionCommandResult.handled) { diff --git a/src/core/history/workspace-history.ts b/src/core/history/workspace-history.ts index a7d9684699..ee706b86e8 100644 --- a/src/core/history/workspace-history.ts +++ b/src/core/history/workspace-history.ts @@ -502,9 +502,23 @@ async function writeWorkspaceJsonWithHistoryLocked( * Derive and persist an audited singleton mutation from one lock-protected * before-state, preventing read-modify-write callers from losing updates. */ +export function mutateWorkspaceJsonWithHistory( + params: WorkspaceJsonMutationOptions & { idempotencyKey?: undefined }, +): Promise<{ changed: boolean; result: Result; replayed?: false }>; +/** Mutate an audited singleton while reporting a completed operation replay. */ +export function mutateWorkspaceJsonWithHistory( + params: WorkspaceJsonMutationOptions, +): Promise< + | { changed: boolean; result: Result; replayed?: false } + | { changed: false; result: undefined; replayed: true } +>; +/** Apply the lock-scoped write once, returning an inert receipt for retries. */ export async function mutateWorkspaceJsonWithHistory( params: WorkspaceJsonMutationOptions, -): Promise<{ changed: boolean; result: Result; replayed?: boolean }> { +): Promise< + | { changed: boolean; result: Result; replayed?: false } + | { changed: false; result: undefined; replayed: true } +> { const { documentPath } = resolveGovernedDocumentPath(params.pmRoot, params.filePath); const release = await acquireLock( params.pmRoot, @@ -539,7 +553,7 @@ export async function mutateWorkspaceJsonWithHistory( if (params.idempotencyKey !== undefined && entries.some( (entry) => entry.op === `${params.op}:${params.idempotencyKey}`, )) { - return { changed: false, result: (await params.mutate(beforeRaw)).result, replayed: true }; + return { changed: false, result: undefined, replayed: true }; } } const mutation = await params.mutate(beforeRaw); diff --git a/src/sdk/extension-command-context.ts b/src/sdk/extension-command-context.ts index 010bee53e1..c2d4695825 100644 --- a/src/sdk/extension-command-context.ts +++ b/src/sdk/extension-command-context.ts @@ -4,7 +4,9 @@ * Builds the host-owned SDK services injected into extension commands so * package runtimes never need private imports or runtime package resolution. */ +import { createHash } from "node:crypto"; import type { ExtensionCommandSdk } from "../core/extensions/extension-types.js"; +import { runActiveOnWriteHooks } from "../core/extensions/index.js"; import type { PmSettings } from "../types/index.js"; import { mutateWorkspaceJsonWithHistory } from "../core/history/workspace-history.js"; import { resolveAuthor } from "../core/shared/author.js"; @@ -12,7 +14,7 @@ import { EXIT_CODE } from "../core/shared/constants.js"; import { PmCliError } from "../core/shared/errors.js"; import { stableValueEquals } from "../core/shared/serialization.js"; import { getSettingsPath } from "../core/store/paths.js"; -import { readSettings } from "../core/store/settings.js"; +import { readSettings, serializeSettings } from "../core/store/settings.js"; import { clearSettingsReadCache } from "../core/store/settings-read-cache.js"; import { validateSettings } from "../core/store/settings-validator.js"; import { isPmCliExpectedError } from "./errors.js"; @@ -47,6 +49,7 @@ export function createExtensionCommandSdk( pmRoot: string, client: PmClient, invocationAuthor?: string, + invocationCommand?: string, ): ExtensionCommandSdk { return { client, @@ -94,7 +97,9 @@ export function createExtensionCommandSdk( pmRoot, filePath: getSettingsPath(pmRoot), op: "extension:pm:settings", - idempotencyKey: options.operationId, + idempotencyKey: invocationCommand === undefined + ? options.operationId + : `${createHash("sha256").update(invocationCommand).digest("hex").slice(0, 16)}:${options.operationId}`, author: invocationAuthor ?? resolveAuthor(undefined, settings.author_default), lockTtlSeconds: settings.locks.ttl_seconds, lockWaitMs: settings.locks.wait_ms, @@ -118,11 +123,18 @@ export function createExtensionCommandSdk( return { raw: stableValueEquals(current, next) ? beforeRaw! - : `${JSON.stringify(next, null, 2)}\n`, + : serializeSettings(next), result: undefined, }; }, }); + if (mutation.changed && options.dryRun !== true) { + await runActiveOnWriteHooks({ + path: getSettingsPath(pmRoot), + scope: "project", + op: "extension:pm:settings", + }); + } return { changed: mutation.changed, dry_run: options.dryRun === true, diff --git a/src/sdk/runtime.ts b/src/sdk/runtime.ts index c7a6980fa1..202aa7c002 100644 --- a/src/sdk/runtime.ts +++ b/src/sdk/runtime.ts @@ -2776,6 +2776,7 @@ async function dispatchActiveExtensionAction( author: extensionAuthor, }), extensionAuthor, + normalizeCommandPath(command), ), }); if (!handlerResult.handled) { diff --git a/src/sdk/testing.ts b/src/sdk/testing.ts index 6409c12be2..20b58af2e8 100644 --- a/src/sdk/testing.ts +++ b/src/sdk/testing.ts @@ -999,7 +999,7 @@ export async function runRegisteredCommandForTest( options.sdk ?? options.sdkFactory?.({ pmRoot, global }) ?? (pmRoot.length > 0 - ? createExtensionCommandSdk(pmRoot, new PmClient({ pmRoot })) + ? createExtensionCommandSdk(pmRoot, new PmClient({ pmRoot }), undefined, command) : undefined); return runCommandHandler(commands, { command, diff --git a/tests/integration/extension-host-contracts.integration.spec.ts b/tests/integration/extension-host-contracts.integration.spec.ts index afd4777a77..a120e68059 100644 --- a/tests/integration/extension-host-contracts.integration.spec.ts +++ b/tests/integration/extension-host-contracts.integration.spec.ts @@ -38,7 +38,9 @@ describe("extension host contracts", () => { await installHostContractExtension( context.pmPath, [ + "import { appendFileSync } from 'node:fs';", "export default { activate(api) {", + " api.hooks.onWrite((context) => appendFileSync(new URL('./write-hooks.log', import.meta.url), `${context.op}\\n`));", " api.registerCommand({", " name: 'host settings',", " flags: [{ long: '--operation-id', value_name: 'id', value_type: 'string' }, { long: '--enabled', value_name: 'value', value_type: 'string' }, { long: '--preview', value_type: 'boolean' }],", @@ -47,16 +49,18 @@ describe("extension host contracts", () => { " mutate: (current) => options.enabled === 'invalid' ? { broken: true } : ({ ...current, ux: { ...current.ux, deprecation_hints: options.enabled === 'true' } }),", " }),", " });", + " api.registerCommand({ name: 'host other', run: ({ sdk }) => sdk.mutateWorkspaceSettings({ operationId: 'apply-1', mutate: (current) => ({ ...current, ux: { ...current.ux, deprecation_hints: true } }) }) });", "} };", ].join("\n"), - ["commands", "schema"], - ["host settings"], + ["commands", "schema", "hooks"], + ["host settings", "host other"], ); const manifestPath = path.join(context.pmPath, "extensions", "host-contract-test", "manifest.json"); const manifest = JSON.parse(await readFile(manifestPath, "utf8")) as Record; await writeFile(manifestPath, `${JSON.stringify({ ...manifest, pm_min_version: "2026.9.28" }, null, 2)}\n`); expect(context.runCli(["extension", "adopt-all", "--project", "--json"]).code).toBe(0); const settingsPath = path.join(context.pmPath, "settings.json"); + const hookLogPath = path.join(context.pmPath, "extensions", "host-contract-test", "write-hooks.log"); const before = await readFile(settingsPath, "utf8"); const preview = context.runCli( ["host", "settings", "--operation-id", "preview-1", "--enabled", "false", "--preview", "--json"], @@ -72,18 +76,26 @@ describe("extension host contracts", () => { expect(applied).toMatchObject({ code: 0, json: { changed: true, dry_run: false, replayed: false } }); const persisted = await readFile(settingsPath, "utf8"); expect(JSON.parse(persisted)).toMatchObject({ ux: { deprecation_hints: false } }); + const appliedHooks = await readFile(hookLogPath, "utf8"); + expect(appliedHooks).toContain("extension:pm:settings"); const replay = context.runCli( ["host", "settings", "--operation-id", "apply-1", "--enabled", "true", "--json"], { expectJson: true }, ); expect(replay).toMatchObject({ code: 0, json: { changed: false, replayed: true } }); expect(await readFile(settingsPath, "utf8")).toBe(persisted); + expect((await readFile(hookLogPath, "utf8")).split("extension:pm:settings").length) + .toBe(appliedHooks.split("extension:pm:settings").length); const invalid = context.runCli( ["host", "settings", "--operation-id", "invalid-1", "--enabled", "invalid", "--json"], ); expect(invalid.code).toBe(2); expect(await readFile(settingsPath, "utf8")).toBe(persisted); + const other = context.runCli(["host", "other", "--json"], { expectJson: true }); + expect(other).toMatchObject({ code: 0, json: { changed: true, replayed: false } }); + expect(JSON.parse(await readFile(settingsPath, "utf8"))).toMatchObject({ ux: { deprecation_hints: true } }); + expect((await readFile(hookLogPath, "utf8")).split("extension:pm:settings")).toHaveLength(3); expect(context.runCli(["history", "_workspace", "--verify", "--json"], { expectJson: true }).code).toBe(0); const health = context.runCli(["health", "--strict-exit", "--json"], { expectJson: true }); expect(health.code, JSON.stringify(health.json)).toBe(0); diff --git a/tests/unit/core/history/workspace-history.spec.ts b/tests/unit/core/history/workspace-history.spec.ts index bbc7bbae87..9ce06c427c 100644 --- a/tests/unit/core/history/workspace-history.spec.ts +++ b/tests/unit/core/history/workspace-history.spec.ts @@ -743,8 +743,8 @@ describe("workspace history", () => { })).resolves.toMatchObject({ changed: true, result: true }); const appliedHistory = await readFile(getWorkspaceHistoryPath(context.pmPath), "utf8"); await expect(mutateWorkspaceJsonWithHistory({ - ...common, idempotencyKey: "apply-1", mutate: () => change(false), - })).resolves.toMatchObject({ changed: false, replayed: true }); + ...common, idempotencyKey: "apply-1", mutate: () => { throw new Error("replay reran mutation"); }, + })).resolves.toEqual({ changed: false, result: undefined, replayed: true }); expect(await readFile(filePath, "utf8")).toBe('{"enabled":true}\n'); expect(await readFile(getWorkspaceHistoryPath(context.pmPath), "utf8")).toBe(appliedHistory); diff --git a/tests/unit/sdk/sdk-extension-mutation-platform.spec.ts b/tests/unit/sdk/sdk-extension-mutation-platform.spec.ts index 2cec3d4506..5940acc413 100644 --- a/tests/unit/sdk/sdk-extension-mutation-platform.spec.ts +++ b/tests/unit/sdk/sdk-extension-mutation-platform.spec.ts @@ -75,11 +75,27 @@ describe("extension mutation platform", () => { author: "extension-test-actor", }); + const otherCommand = createExtensionCommandSdk( + pmPath, + new PmClient({ pmRoot: pmPath, noExtensions: true }), + "extension-test-actor", + "other settings command", + ); + await expect(otherCommand.mutateWorkspaceSettings({ + operationId: "author-v1", + mutate: (current) => ({ ...current, author_default: "other-command-author" }), + })).resolves.toMatchObject({ changed: true, replayed: false }); + expect((await readHistoryEntries(historyPath, WORKSPACE_HISTORY_ID))[1]?.op) + .toMatch(/^extension:pm:settings:[a-f0-9]{16}:author-v1$/u); + expect(JSON.parse(await readFile(settingsPath, "utf8"))).toMatchObject({ + author_default: "other-command-author", + }); + await expect(sdk.mutateWorkspaceSettings({ operationId: "invalid-next", mutate: () => ({ broken: true }) as unknown as PmSettings, })).rejects.toThrow(/invalid settings/); - expect(await readHistoryEntries(historyPath, WORKSPACE_HISTORY_ID)).toHaveLength(1); + expect(await readHistoryEntries(historyPath, WORKSPACE_HISTORY_ID)).toHaveLength(2); const defaultActorSdk = createExtensionCommandSdk( pmPath, @@ -89,10 +105,23 @@ describe("extension mutation platform", () => { operationId: "author-v2", mutate: (current) => ({ ...current, author_default: "next-default" }), })).resolves.toMatchObject({ changed: true, replayed: false }); - expect((await readHistoryEntries(historyPath, WORKSPACE_HISTORY_ID))[1]).toMatchObject({ + expect((await readHistoryEntries(historyPath, WORKSPACE_HISTORY_ID))[2]).toMatchObject({ author: "test-author", }); + await expect(sdk.mutateWorkspaceSettings({ + operationId: "normalize-settings", + mutate: (current) => ({ + ...current, + item_format: "json_markdown" as PmSettings["item_format"], + vector_store: { ...current.vector_store, collection_name: "bad/name" }, + }), + })).resolves.toMatchObject({ changed: true }); + expect(JSON.parse(await readFile(settingsPath, "utf8"))).toMatchObject({ + item_format: "toon", + vector_store: { collection_name: "bad_name" }, + }); + await writeFile(settingsPath, "{}\n"); await expect(sdk.mutateWorkspaceSettings({ operationId: "invalid-current", diff --git a/tests/unit/sdk/structured-mutations.spec.ts b/tests/unit/sdk/structured-mutations.spec.ts index 3ab3545ed4..a900944007 100644 --- a/tests/unit/sdk/structured-mutations.spec.ts +++ b/tests/unit/sdk/structured-mutations.spec.ts @@ -1,4 +1,5 @@ import { describe, expect, it, vi } from "vitest"; +import { PmCliError } from "../../../src/core/shared/errors.js"; import { itemDocumentToMutationOptions, parseAtomicMutationControls, @@ -556,6 +557,18 @@ describe("structured mutation input", () => { expect(() => itemDocumentToMutationOptions('{"item":[]}', "update"), ).toThrow("item must be an object"); + for (const key of ["comments", "notes", "learnings"]) { + try { + itemDocumentToMutationOptions(JSON.stringify({ [key]: null }), "update"); + throw new Error("Expected malformed annotation array to be refused."); + } catch (error) { + expect(error).toBeInstanceOf(PmCliError); + expect(error).toMatchObject({ + exitCode: 2, + message: expect.stringContaining(`${key} must be an array`), + }); + } + } expect(() => itemDocumentToMutationOptions("[]", "create")).toThrow( "must be a JSON object", );