From 1504cc4662075ccfcd56d1c6e20e1206eeed9fa7 Mon Sep 17 00:00:00 2001 From: Stefan Preu Date: Sun, 27 Sep 2026 04:07:09 +0200 Subject: [PATCH 1/5] fix: isolate SDK linked tests and preserve multi-tracker merge fences Add an opt-in linked-test context mode that omits PM_PATH only for disposable workspaces, while keeping trust checks and PM_GLOBAL_PATH isolation. This lets package acceptance tests create independent SDK tracker roots. Reconcile the active tracker merge block without deleting sibling mappings and make strict health detect a missing or drifted fence. Cover mixed legacy fences, truncated blocks, root-mounted trackers, and nested installs. Close the two tracked issues with test evidence, add the newly reported package scan defect to PM, and regenerate the changelog with pm-changelog. --- .../pm/extensions/.managed-extensions.json | 2 +- .agents/pm/history/pm-erogk1.jsonl | 1 + .agents/pm/history/pm-kynkl8.jsonl | 21 +++ .agents/pm/history/pm-t05d8d.jsonl | 15 ++ .agents/pm/issues/pm-erogk1.toon | 16 ++ .agents/pm/issues/pm-kynkl8.toon | 81 ++++++++++ .agents/pm/issues/pm-t05d8d.toon | 58 ++++++- CHANGELOG.md | 2 + docs/TESTING.md | 13 +- docs/generated/REFUSAL_CLOSURE_CENSUS.md | 8 +- scripts/agent-token-surface-baseline.json | 46 +++--- sdk/public-surface.json | 17 +- src/cli/register-operations.ts | 2 +- src/core/diagnostics/remediation.ts | 10 ++ src/core/item/item-format.ts | 1 + .../commander-mutation-options.ts | 2 +- .../cli-contracts/tool-parameter-tables.ts | 6 +- src/sdk/completion/fish.ts | 4 +- src/sdk/completion/zsh.ts | 4 +- .../generated-error-code-catalog-part-1.ts | 39 +++-- .../generated-error-code-catalog-part-2.ts | 13 ++ src/sdk/governance/health-merge-evidence.ts | 63 ++++++++ src/sdk/governance/health.ts | 33 ++-- src/sdk/merge/install.ts | 141 ++++++++++++----- src/sdk/test/execution.ts | 86 ++++++++--- src/sdk/test/parsers.ts | 1 + src/types.ts | 6 +- tests/fixtures/contracts/full.json | 58 +++++-- ...ked-test-context-trust.integration.spec.ts | 64 ++++++++ ...rge-fence-version-skew.integration.spec.ts | 146 +++++++++++++++++- .../commands/test/linked-test-parsers.spec.ts | 1 + .../execution-diagnostics-primitives.spec.ts | 1 + .../health-verdict-authority.spec.ts | 4 +- 33 files changed, 816 insertions(+), 149 deletions(-) create mode 100644 .agents/pm/history/pm-erogk1.jsonl create mode 100644 .agents/pm/history/pm-kynkl8.jsonl create mode 100644 .agents/pm/issues/pm-erogk1.toon create mode 100644 .agents/pm/issues/pm-kynkl8.toon create mode 100644 src/sdk/governance/health-merge-evidence.ts diff --git a/.agents/pm/extensions/.managed-extensions.json b/.agents/pm/extensions/.managed-extensions.json index 87a26482f..b5104c18b 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-26T22:20:41.276Z", + "updated_at": "2026-09-27T01:50:28.300Z", "entries": [ { "name": "pm-changelog", diff --git a/.agents/pm/history/pm-erogk1.jsonl b/.agents/pm/history/pm-erogk1.jsonl new file mode 100644 index 000000000..50a292ef7 --- /dev/null +++ b/.agents/pm/history/pm-erogk1.jsonl @@ -0,0 +1 @@ +{"hash_algorithm":"sha256","ts":"2026-09-27T00:37:15.358Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"create","patch":[{"op":"add","path":"/metadata/id","value":"pm-erogk1"},{"op":"add","path":"/metadata/title","value":"Refuse local package installation when source scan stops at entry limit"},{"op":"add","path":"/metadata/description","value":"GitHub #1321: local package source inspection reaches max_entries=10000, reports copy.complete=false and stop_reason=entry_limit, yet dry-run and real install return ok=true, warnings=[] and exit 0; real path may activate a partial package. Fail closed before write/activation, give a packed-package recovery hint, and align dry-run readiness."},{"op":"add","path":"/metadata/type","value":"Issue"},{"op":"add","path":"/metadata/status","value":"open"},{"op":"add","path":"/metadata/priority","value":1},{"op":"add","path":"/metadata/tags","value":[]},{"op":"add","path":"/metadata/created_at","value":"2026-09-27T00:37:15.358Z"},{"op":"add","path":"/metadata/updated_at","value":"2026-09-27T00:37:15.358Z"},{"op":"add","path":"/metadata/author","value":"harness:codex"},{"op":"add","path":"/metadata/parent","value":"pm-x6jf"},{"op":"add","path":"/metadata/dependencies","value":[{"id":"pm-x6jf","kind":"discovered_from","created_at":"2026-09-27T00:37:15.358Z","author":"harness:codex","source_kind":"cli:create:dep","author_source":"detected"}]},{"op":"add","path":"/metadata/comments","value":[{"created_at":"2026-09-27T00:37:15.358Z","author":"harness:codex","text":"Duplicate check across all PM statuses: searched GH #1321, entry_limit, incomplete source scan, partial package copy, and package install. Earlier lifecycle items cover different completed fixes; no existing item owns this limit-truncation defect. Source: https://github.com/unbraind/pm-cli/issues/1321. Intake only; item remains open and unclaimed."}]}],"before_hash":"3cc22dff72be7b14824654a7a64ea62b04799939b2fee54c1b5f52ca60bf6df0","after_hash":"7d0efe9810572cf2effbaaf56994e8fee9f0a849b034f9b039a6137804e49865","item_hash_version":3,"message":"Track GitHub #1321 partial package install defect without claiming work","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":"af88212bdba8fe77db3868942ac531e3cdc616f1463a94e13e95246876468498"} diff --git a/.agents/pm/history/pm-kynkl8.jsonl b/.agents/pm/history/pm-kynkl8.jsonl new file mode 100644 index 000000000..398098fb0 --- /dev/null +++ b/.agents/pm/history/pm-kynkl8.jsonl @@ -0,0 +1,21 @@ +{"hash_algorithm":"sha256","ts":"2026-09-26T23:39:49.274Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"create","patch":[{"op":"add","path":"/metadata/id","value":"pm-kynkl8"},{"op":"add","path":"/metadata/title","value":"Preserve every tracker merge fence during nested init and detect lost active mappings"},{"op":"add","path":"/metadata/description","value":"Nested pm init replaces the root tracker merge-driver attributes, and strict health misses the lost mappings. Reconcile distinct tracker fences in a shared Git repository without erasing other trackers, and make active-tracker strict health fail when the merge fence is missing or stale. GitHub #1320."},{"op":"add","path":"/metadata/type","value":"Issue"},{"op":"add","path":"/metadata/status","value":"open"},{"op":"add","path":"/metadata/priority","value":1},{"op":"add","path":"/metadata/tags","value":["area:init","area:merge","gh-1320","multi-tracker"]},{"op":"add","path":"/metadata/created_at","value":"2026-09-26T23:39:49.274Z"},{"op":"add","path":"/metadata/updated_at","value":"2026-09-26T23:39:49.274Z"},{"op":"add","path":"/metadata/author","value":"harness:codex"},{"op":"add","path":"/metadata/acceptance_criteria","value":"Nested init and subsequent merge install preserve both root and nested merge mappings; Strict health detects lost active-tracker mappings; Focused regression, full coverage, and temporary packed CLI acceptance pass"},{"op":"add","path":"/metadata/parent","value":"pm-dj98"},{"op":"add","path":"/metadata/comments","value":[{"created_at":"2026-09-26T23:39:49.274Z","author":"harness:codex","text":"created_at=now,text=Duplicate check 2026-09-27: all-status searches for GH-1320, nested init merge attributes, merge fence ownership, and tracker fence drift found closed pm-1w3ljt, pm-i4fx, pm-l56d0o, and pm-g5sx. They delivered initial fence installation, coverage, and version-skew handling but not preservation across nested trackers. Source report https://github.com/unbraind/pm-cli/issues/1320."}]}],"before_hash":"3cc22dff72be7b14824654a7a64ea62b04799939b2fee54c1b5f52ca60bf6df0","after_hash":"484aae87667329fa5bda578ea467b0c437e39756141a9763691d41988f8d6ded","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":"29c7c46384d575b91aeafa9e805189d7de26853b66b9b6c62c4522be50fc16f9"} +{"hash_algorithm":"sha256","ts":"2026-09-26T23:40:06.161Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-26T23:40:06.161Z"},{"op":"add","path":"/metadata/assignee","value":"harness:codex"},{"op":"add","path":"/metadata/claim_principal","value":"harness:codex#f9d60a3a144e8d9ec3f9b203"}],"before_hash":"484aae87667329fa5bda578ea467b0c437e39756141a9763691d41988f8d6ded","after_hash":"9970774ae81230581efd1ded317166f831d9c01a37146d053de9b218a7fa1021","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":"ccbd301c4cc5c72e5e31859e0a2c7cecaa2e6e7dc99d314f73f63d1211909bbb"} +{"hash_algorithm":"sha256","ts":"2026-09-26T23:40:06.314Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","lineage:pm-kynkl8","lineage:pm-dj98","lineage:pm-doxj"]},"topic":{"value":"pm-kynkl8","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","lineage:pm-kynkl8","lineage:pm-dj98","lineage:pm-doxj"]}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T23:40:06.314Z"},{"op":"replace","path":"/metadata/status","value":"in_progress"}],"before_hash":"9970774ae81230581efd1ded317166f831d9c01a37146d053de9b218a7fa1021","after_hash":"44ddd2087b79265de2d5b93d64b3267054a4bf2c3ca032383ec844d4a2f0cb75","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":"74ca769b8562adb64de4af89c68567b7613792873cedd3ce3d8c4c44a5908d91"} +{"hash_algorithm":"sha256","ts":"2026-09-27T00:30:21.853Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T00:30:21.853Z"},{"op":"add","path":"/metadata/dependencies","value":[{"id":"pm-1w3ljt","kind":"verifies","created_at":"2026-09-27T00:30:21.598Z","author":"harness:codex","source_kind":"cli:update:dep","author_source":"detected"},{"id":"pm-i4fx","kind":"verifies","created_at":"2026-09-27T00:30:21.598Z","author":"harness:codex","source_kind":"cli:update:dep","author_source":"detected"},{"id":"pm-t05d8d","kind":"discovered_from","created_at":"2026-09-27T00:30:21.598Z","author":"harness:codex","source_kind":"cli:update:dep","author_source":"detected"}]}],"before_hash":"44ddd2087b79265de2d5b93d64b3267054a4bf2c3ca032383ec844d4a2f0cb75","after_hash":"67e9ec8590e68a87aa8227514c1debd38c32e0bca23e5ee7867b3716b390162d","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":"0033dd4a9eafca1037f0209bb122ec753f3505679033e3c62a8c506f1c92a222"} +{"hash_algorithm":"sha256","ts":"2026-09-27T00:30:22.626Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T00:30:22.626Z"},{"op":"add","path":"/metadata/files","value":[{"path":"src/core/diagnostics/remediation.ts","scope":"project"},{"path":"src/sdk/generated/generated-error-code-catalog-part-1.ts","scope":"project"},{"path":"src/sdk/generated/generated-error-code-catalog-part-2.ts","scope":"project"},{"path":"src/sdk/governance/health-merge-evidence.ts","scope":"project"},{"path":"src/sdk/governance/health.ts","scope":"project"},{"path":"src/sdk/merge/install.ts","scope":"project"},{"path":"tests/fixtures/contracts/full.json","scope":"project"},{"path":"tests/integration/merge-fence-version-skew.integration.spec.ts","scope":"project"}]}],"before_hash":"67e9ec8590e68a87aa8227514c1debd38c32e0bca23e5ee7867b3716b390162d","after_hash":"77d41a2aea9b8d69438eb2e896ac9528a5ccaa09691132c68d131ecf6db495e8","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":"c69199908f84f69d69dcfe82959abc2df7a7d183bc5f9a33b6261be942bdf084"} +{"hash_algorithm":"sha256","ts":"2026-09-27T00:30:23.402Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"docs_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T00:30:23.402Z"},{"op":"add","path":"/metadata/docs","value":[{"path":"docs/REFUSAL_CLOSURE_CENSUS.md","scope":"project"},{"path":"docs/TESTING.md","scope":"project"}]}],"before_hash":"77d41a2aea9b8d69438eb2e896ac9528a5ccaa09691132c68d131ecf6db495e8","after_hash":"7db472a6eff1e0d2ef73f4deda8b4079b2077dcbdf19b637798d71a292f2c170","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":"dd3c9df242ef5418ed2e82371e9c06321072bfcb2c45ef785ac3c912b973611b"} +{"hash_algorithm":"sha256","ts":"2026-09-27T00:31:39.540Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"docs_add","patch":[{"op":"replace","path":"/metadata/docs/0/path","value":"docs/generated/REFUSAL_CLOSURE_CENSUS.md"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T00:31:39.540Z"}],"before_hash":"7db472a6eff1e0d2ef73f4deda8b4079b2077dcbdf19b637798d71a292f2c170","after_hash":"3978ee859b476fb46606564404d41b9cfa8324edc3b078981948652fc0abf1a7","item_hash_version":3,"message":"Correct generated census path after capability snapshot update","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":"9903235e38f0632e2884d0ced9501ffeba61c2c559dad0ff525a55d690a9e646"} +{"hash_algorithm":"sha256","ts":"2026-09-27T00:31:51.425Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"tests_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T00:31:51.425Z"},{"op":"add","path":"/metadata/tests","value":[{"command":"node scripts/run-tests.mjs test -- tests/integration/merge-fence-version-skew.integration.spec.ts","scope":"project","timeout_seconds":2400,"provenance":{"author":"harness:codex","created_at":"2026-09-27T00:31:51.396Z","source_kind":"local_mutation","source_ref":"fix/isolated-project-context-and-merge-fences"}}]}],"before_hash":"3978ee859b476fb46606564404d41b9cfa8324edc3b078981948652fc0abf1a7","after_hash":"2fc25d264a7a9da758530623ed3c189a8d99df5aeeb337206f94931be976d663","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":"48a42ea3d36035e8d0403e2434544b6ebb31af662d936656c6465cf37f80456f"} +{"hash_algorithm":"sha256","ts":"2026-09-27T00:32:09.999Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"test_run_track","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T00:32:09.999Z"},{"op":"add","path":"/metadata/test_runs","value":[{"run_id":"test-local-muj33ain-jamd0w","kind":"test","status":"passed","started_at":"2026-09-27T00:31:59.684Z","finished_at":"2026-09-27T00:32:09.983Z","recorded_at":"2026-09-27T00:32:09.983Z","passed":1,"failed":0,"skipped":0,"executions":[{"command":"node scripts/run-tests.mjs test -- tests/integration/merge-fence-version-skew.integration.spec.ts","requested_pm_context_mode":"schema","pm_context_mode":"schema","workspace_context_mode":"source","trust_reason":"local_source_ref"}]}]}],"before_hash":"2fc25d264a7a9da758530623ed3c189a8d99df5aeeb337206f94931be976d663","after_hash":"15baeb50ab98aee026c8e71c50b03309e48ebeeb3cebe93182aa7bc62467e97d","item_hash_version":3,"message":"Track test run summary (test-local-muj33ain-jamd0w)","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":"8fb490d2a76685b04857bc847f170ddc5cc9523c9d3dbf3d19c69f0ded12a83b"} +{"hash_algorithm":"sha256","ts":"2026-09-27T00:32:56.877Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/1","value":{"created_at":"2026-09-27T00:32:56.876Z","author":"harness:codex","text":"Root cause: shared .gitattributes reconciliation removed the first managed block without checking tracker ownership, so a nested tracker install could erase the root tracker fence (and vice versa). The fix preserves sibling blocks, replaces only the active tracker block, collapses duplicate owned blocks, and makes strict health report missing/drifted active fences. Focused integration tests passed (2/2), including git check-attr and reinstall for both roots. GitHub #1320 carries the canonical PM link; tracker validation now reports zero orphaned paths."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T00:32:56.877Z"}],"before_hash":"15baeb50ab98aee026c8e71c50b03309e48ebeeb3cebe93182aa7bc62467e97d","after_hash":"3520bcc714a1a4983c70958b2725cd8ed1e23b595f1f6914bfbb65d9854c6135","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":"8f804309dcb4a4728d1b1ecb88abf6d56df27d4df8e1211bbc4d7a6fb81b07e7"} +{"hash_algorithm":"sha256","ts":"2026-09-27T00:40:54.856Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"learning_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T00:40:54.856Z"},{"op":"add","path":"/metadata/learnings","value":[{"created_at":"2026-09-27T00:40:54.856Z","author":"harness:codex","text":"A repository may host multiple PM roots sharing one .gitattributes. Merge-fence maintenance must identify ownership by tracker-relative pattern prefix, preserve sibling blocks, and audit the active tracker separately from clone-local Git driver configuration."}]}],"before_hash":"3520bcc714a1a4983c70958b2725cd8ed1e23b595f1f6914bfbb65d9854c6135","after_hash":"27f9931d3c8d436ab225880bb2aa66fb8d0bc16e813abc4c85a22736a918f25e","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":"3c4243a5491c382ab299f7ec2330bda1b9fc2ed7fe9deaae6c25bc7541bf24e8"} +{"hash_algorithm":"sha256","ts":"2026-09-27T00:54:54.141Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"files_add","patch":[{"op":"add","path":"/metadata/files/8","value":{"path":"tests/unit/sdk/governance/health-verdict-authority.spec.ts","scope":"project"}},{"op":"add","path":"/metadata/files/9","value":{"path":"tests/unit/sdk/merge-safety.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T00:54:54.141Z"}],"before_hash":"27f9931d3c8d436ab225880bb2aa66fb8d0bc16e813abc4c85a22736a918f25e","after_hash":"a0285594360c12e75f1529d17540970c763f8ef7ae403e5aae45ea09474e7b3e","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":"82fa8c59fcab5d11726574b026e0570ee68a28741e5682ba49c80705ad076322"} +{"hash_algorithm":"sha256","ts":"2026-09-27T00:56:03.425Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/2","value":{"created_at":"2026-09-27T00:56:03.425Z","author":"harness:codex","text":"Full coverage run surfaced a real truncated-fence audit edge case: an unterminated managed block still contained expected patterns, so the audit marked drift but reported zero missing patterns. The parser now records whether each block closed, excludes unclosed content from committed mappings, and retains a drift verdict. Updated strict-health expectation; the focused merge and health suite now passes 25 tests."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T00:56:03.425Z"}],"before_hash":"a0285594360c12e75f1529d17540970c763f8ef7ae403e5aae45ea09474e7b3e","after_hash":"dbe5ba769d58fbb65d0df468485a34e00c373eca6ee0a5d308ddfc9927ecabe6","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":"d19d8b9451690b154f218d83ad1082273744e76bfe230c229740e38885ab7437"} +{"hash_algorithm":"sha256","ts":"2026-09-27T01:37:02.675Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"test_run_track","patch":[{"op":"add","path":"/metadata/test_runs/1","value":{"run_id":"test-local-muj5eq4p-ult72v","kind":"test","status":"passed","started_at":"2026-09-27T01:36:51.877Z","finished_at":"2026-09-27T01:37:02.665Z","recorded_at":"2026-09-27T01:37:02.665Z","passed":1,"failed":0,"skipped":0,"executions":[{"command":"node scripts/run-tests.mjs test -- tests/integration/merge-fence-version-skew.integration.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-27T01:37:02.675Z"}],"before_hash":"dbe5ba769d58fbb65d0df468485a34e00c373eca6ee0a5d308ddfc9927ecabe6","after_hash":"2b437a21c5e30e54b49b17d5d8f7585921edda351455c9738c2693ca9ca5963b","item_hash_version":3,"message":"Track test run summary (test-local-muj5eq4p-ult72v)","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":"e24d9bd21ca840518e63e27af4c7fcbc48dd0eec1829a1ec1253743b6dabf7c1"} +{"hash_algorithm":"sha256","ts":"2026-09-27T01:37:13.523Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/3","value":{"created_at":"2026-09-27T01:37:13.523Z","author":"harness:codex","text":"Final verification: pnpm test:coverage passed 748 test files / 9,371 tests with exact 100/100/100/100 coverage (65,708 statements; 50,236 branches; 13,602 functions; 62,756 lines), 2 Windows-only tests skipped on Linux. pm test pm-kynkl8 --run passed 4 integration cases after the final edits. The new cases cover mixed legacy/versioned fences, root-mounted trackers with siblings, ownerless legacy migration, and lone nested-block preservation. pnpm build, pnpm typecheck, ESLint, docstring/static scanner, docs-skills gate, and npm audit high passed. Packaged CLI smoke in a disposable Git project passed, including strict health and npx/bunx version commands."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T01:37:13.523Z"}],"before_hash":"2b437a21c5e30e54b49b17d5d8f7585921edda351455c9738c2693ca9ca5963b","after_hash":"2a8646d3348fe186707d4789d4d53b49e41685cc420df7038bea57976cda431c","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":"c88e1bbb850708f1f22db8af57138faa0dd325dc41efd42cc41c732f7a4608d0"} +{"hash_algorithm":"sha256","ts":"2026-09-27T01:47:43.018Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"update","patch":[{"op":"add","path":"/metadata/dependencies/3","value":{"id":"pm-g5sx","kind":"implements","created_at":"2026-09-27T01:47:42.715Z","author":"harness:codex","source_kind":"cli:update:dep","author_source":"detected"}},{"op":"add","path":"/metadata/dependencies/4","value":{"id":"pm-l56d0o","kind":"verifies","created_at":"2026-09-27T01:47:42.715Z","author":"harness:codex","source_kind":"cli:update:dep","author_source":"detected"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T01:47:43.018Z"}],"before_hash":"2a8646d3348fe186707d4789d4d53b49e41685cc420df7038bea57976cda431c","after_hash":"2172eb4c49f3673872f013d07d1c8278d6117e25af936f338b88f279702616d6","item_hash_version":3,"message":"Type the two historical merge-lineage references found by graph composition","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":"4b8d7449841c6b658942e483246d2477cf77025daeedf2014c814751c896263f"} +{"hash_algorithm":"sha256","ts":"2026-09-27T01:48:55.299Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"docs_add","patch":[{"op":"replace","path":"/metadata/docs/1/path","value":"docs/generated/REFUSAL_CLOSURE_CENSUS.md"},{"op":"replace","path":"/metadata/docs/0/path","value":"CHANGELOG.md"},{"op":"add","path":"/metadata/docs/2","value":{"path":"docs/TESTING.md","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T01:48:55.299Z"}],"before_hash":"2172eb4c49f3673872f013d07d1c8278d6117e25af936f338b88f279702616d6","after_hash":"6b48dd21e681b6364e0c676f3509abb97d3f7f0f42850d1b9cc2b636b7eb3dda","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":"6a7f5ede71ec18b983ec30687b351a2de2fd4f5995427a0c595de63683ee39c6"} +{"hash_algorithm":"sha256","ts":"2026-09-27T01:49:01.054Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/4","value":{"created_at":"2026-09-27T01:49:01.054Z","author":"harness:codex","text":"Graph-composition gate identified two prose-only references in this item’s duplicate-check history. Added typed verifies link to closed version-skew issue pm-l56d0o and implements link to closed merge-semantics feature pm-g5sx; graph-composition now passes with prose-edge gaps at its 1236 ceiling. Record-integrity assurance and the mandatory mutation gate also passed (323/330 mutants killed; seven classified equivalent)."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T01:49:01.054Z"}],"before_hash":"6b48dd21e681b6364e0c676f3509abb97d3f7f0f42850d1b9cc2b636b7eb3dda","after_hash":"0072adafb4f585185facaedfc43f4ec4702924012e6c87e799c4e8d60f54c75e","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":"141bee185a081bedc526ef73c842b6bd7211e7c5912917b1ef0e94c9ba19e8ce"} +{"hash_algorithm":"sha256","ts":"2026-09-27T01:49:16.847Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"close","patch":[{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T01:49:16.847Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/closed_at","value":"2026-09-27T01:49:16.819Z"},{"op":"add","path":"/metadata/completed_at","value":"2026-09-27T01:49:16.819Z"},{"op":"add","path":"/metadata/resolution","value":"Reconciled only the active tracker managed .gitattributes block, preserved sibling and mixed legacy blocks, repaired truncated block audit, and added strict-health remediation for missing or drifted fences."},{"op":"add","path":"/metadata/expected_result","value":"Root, nested, and root-mounted trackers retain field-aware Git attributes across init and reinstall; strict health rejects a lost active fence."},{"op":"add","path":"/metadata/actual_result","value":"Four focused merge integration cases and linked PM test passed; 9,371 repository tests passed with exact 100/100/100/100 coverage; packed disposable-project health passed; graph, record-integrity, docstring, lint, and mutation checks passed."},{"op":"add","path":"/metadata/close_reason","value":"Nested tracker merge fences are preserved and strict health detects missing active mappings."}],"before_hash":"0072adafb4f585185facaedfc43f4ec4702924012e6c87e799c4e8d60f54c75e","after_hash":"eda1cfda93326e064da95910d56527fa4f4e552c2850342d006d50613dc16cc2","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":"9b9d19449d92e99e290313d0cb9a2b295bd8de622e7adcaf9288abee5fd8a42f"} +{"hash_algorithm":"sha256","ts":"2026-09-27T01:49:17.737Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T01:49:17.737Z"}],"before_hash":"eda1cfda93326e064da95910d56527fa4f4e552c2850342d006d50613dc16cc2","after_hash":"662e25dae87f02a0fe9aa5b697d4b8cb2a75190a356b7bdd706ec6bd5d5a18fe","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":"923655bc466ca009a37cd61e07e713d660292eb155c64e9166bde23aa7274050"} +{"hash_algorithm":"sha256","ts":"2026-09-27T01:54:54.961Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-27T01:54:54.961Z"},{"op":"add","path":"/metadata/escape_class","value":"production_defect"},{"op":"add","path":"/metadata/gate_evidence","value":{"disposition":"gate_strengthened","owner":"pm-kynkl8","gate_id":"merge-fence-active-tracker-ownership","negative_control":"node scripts/run-tests.mjs test -- tests/integration/merge-fence-version-skew.integration.spec.ts against the pre-fix merge installer: nested init loses root merge=pm-item-toon and strict health passes","local_checks":["node scripts/run-tests.mjs test -- tests/integration/merge-fence-version-skew.integration.spec.ts","node scripts/run-tests.mjs coverage","pnpm quality:static"],"hosted_checks":["CI / Gates (static)","CI / Gates (coverage)"]}}],"before_hash":"662e25dae87f02a0fe9aa5b697d4b8cb2a75190a356b7bdd706ec6bd5d5a18fe","after_hash":"6c2deecea55f6034dc3214b8f7040e1679b17ddcfec3082008ef07bb6969355c","item_hash_version":3,"message":"Classify shipped nested-tracker defect and record gate-strengthening evidence","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"3517da240c9e34d734bff4a60394e7a1da32ab7e801e7367513dd696835bfb8f"} diff --git a/.agents/pm/history/pm-t05d8d.jsonl b/.agents/pm/history/pm-t05d8d.jsonl index 7186b5d07..d5926a206 100644 --- a/.agents/pm/history/pm-t05d8d.jsonl +++ b/.agents/pm/history/pm-t05d8d.jsonl @@ -6,3 +6,18 @@ {"hash_algorithm":"sha256","ts":"2026-09-26T20:39:34.483Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"release-operator","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]},"topic":{"value":"pm-lhhnx9","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T20:39:34.483Z"},{"op":"replace","path":"/metadata/status","value":"open"}],"before_hash":"d8eeaab575ece66f2dae09999dbf3596448cb41cf3a6268c5d3101bc0c72bc12","after_hash":"f722b292e2b1ebb98beb9096fd7917512ada80124b068e0499590d81cbd34d06","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"fc7c39e2a19410b15a936ae08df8720e9d7235bda24e3e46a2f74e3c4b7cf567"} {"hash_algorithm":"sha256","ts":"2026-09-26T20:39:34.589Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"release-operator","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]},"topic":{"value":"pm-lhhnx9","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-lhhnx9","release:pm-t05d8d"]}},"op":"release","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T20:39:34.589Z"}],"before_hash":"f722b292e2b1ebb98beb9096fd7917512ada80124b068e0499590d81cbd34d06","after_hash":"9aa0e42e1a39322ef1231943c6116df4509d9f32e6744eadb57c99ae13bcaf91","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"26a328f7d82603c54a571a9f7b76e4a7d400110e566504fc028c7f547a7d78f8"} {"hash_algorithm":"sha256","ts":"2026-09-26T22:18:49.678Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"1988a47670c5b401cd591f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"update","patch":[{"op":"add","path":"/metadata/dependencies/3","value":{"id":"pm-6pij","kind":"verifies","created_at":"2026-09-26T22:18:49.388Z","author":"harness:codex","source_kind":"cli:update:dep","author_source":"detected"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T22:18:49.678Z"}],"before_hash":"9aa0e42e1a39322ef1231943c6116df4509d9f32e6744eadb57c99ae13bcaf91","after_hash":"10862d51e188543f721c1ad49fe4ddc5c9f5f7368a2931447e3a9f88df024a5c","item_hash_version":3,"message":"Link historical linked-test dataset parity guardrail cited by duplicate check","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"14a1ceaa05d3209122af3705e20920fe2afc1e6b25ac2b537e7a5ba05d448db4"} +{"hash_algorithm":"sha256","ts":"2026-09-26T23:40:06.986Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","lineage:pm-kynkl8","lineage:pm-dj98","lineage:pm-doxj"]},"topic":{"value":"pm-kynkl8","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","lineage:pm-kynkl8","lineage:pm-dj98","lineage:pm-doxj"]}},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T23:40:06.986Z"},{"op":"add","path":"/metadata/assignee","value":"harness:codex"},{"op":"add","path":"/metadata/claim_principal","value":"harness:codex#f9d60a3a144e8d9ec3f9b203"}],"before_hash":"10862d51e188543f721c1ad49fe4ddc5c9f5f7368a2931447e3a9f88df024a5c","after_hash":"5851f8232bc71a8ebac1a3454a7433e56e01ce69cf31844301ee3095024fd0f0","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":"803d84ce56721e72d176a39ac71c13374ab19be14613ce7453aef714bff92c53"} +{"hash_algorithm":"sha256","ts":"2026-09-26T23:40:07.229Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-26T23:40:07.229Z"},{"op":"replace","path":"/metadata/status","value":"in_progress"}],"before_hash":"5851f8232bc71a8ebac1a3454a7433e56e01ce69cf31844301ee3095024fd0f0","after_hash":"c47c48056535d32f7cb9071b98adbb67a37b5166303375954d4b039537d99f65","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":"8df7b46dce4cfdc17f69981d8d55892b4d548e5ffdcb8c63d49802cd62ddeef3"} +{"hash_algorithm":"sha256","ts":"2026-09-27T00:31:40.243Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T00:31:40.243Z"},{"op":"add","path":"/metadata/files","value":[{"path":"scripts/agent-token-surface-baseline.json","scope":"project"},{"path":"sdk/public-surface.json","scope":"project"},{"path":"src/cli/register-operations.ts","scope":"project"},{"path":"src/core/item/item-format.ts","scope":"project"},{"path":"src/sdk/cli-contracts/commander-mutation-options.ts","scope":"project"},{"path":"src/sdk/cli-contracts/tool-parameter-tables.ts","scope":"project"},{"path":"src/sdk/completion/fish.ts","scope":"project"},{"path":"src/sdk/completion/zsh.ts","scope":"project"},{"path":"src/sdk/test/execution.ts","scope":"project"},{"path":"src/sdk/test/parsers.ts","scope":"project"},{"path":"src/types.ts","scope":"project"},{"path":"tests/fixtures/contracts/full.json","scope":"project"},{"path":"tests/integration/linked-test-context-trust.integration.spec.ts","scope":"project"}]}],"before_hash":"c47c48056535d32f7cb9071b98adbb67a37b5166303375954d4b039537d99f65","after_hash":"c24f4489273687a47b05f4131b799b5f508b92ee2545c1cd78509677070455c0","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":"60dcad404284fbfcc32bdc3cba606b6db07f65163c02209ae9e773d46b6a8728"} +{"hash_algorithm":"sha256","ts":"2026-09-27T00:31:41.004Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"docs_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T00:31:41.004Z"},{"op":"add","path":"/metadata/docs","value":[{"path":"docs/TESTING.md","scope":"project"}]}],"before_hash":"c24f4489273687a47b05f4131b799b5f508b92ee2545c1cd78509677070455c0","after_hash":"c58a8ae0009962946924cce33dd6fb97ad1c7588f35864e26ead358874b8ec33","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":"fdc1f73a04ec1ce145d2e51369f71212c5787c9351c4834bd665e17543b30808"} +{"hash_algorithm":"sha256","ts":"2026-09-27T00:31:52.089Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"tests_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T00:31:52.089Z"},{"op":"add","path":"/metadata/tests","value":[{"command":"node scripts/run-tests.mjs test -- tests/integration/linked-test-context-trust.integration.spec.ts","scope":"project","timeout_seconds":2400,"provenance":{"author":"harness:codex","created_at":"2026-09-27T00:31:52.044Z","source_kind":"local_mutation","source_ref":"fix/isolated-project-context-and-merge-fences"}}]}],"before_hash":"c58a8ae0009962946924cce33dd6fb97ad1c7588f35864e26ead358874b8ec33","after_hash":"49577f312e9715f2ace451dbd621b3590ff57b18a91657eebb0a7cf85bbd351a","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":"9755613d852a286c1b1c48da263f97ca5bbb39b24dbdc8ef1a6cc7f413517469"} +{"hash_algorithm":"sha256","ts":"2026-09-27T00:32:26.619Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"test_run_track","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T00:32:26.619Z"},{"op":"add","path":"/metadata/test_runs","value":[{"run_id":"test-local-muj33nc5-n2dni3","kind":"test","status":"passed","started_at":"2026-09-27T00:32:13.902Z","finished_at":"2026-09-27T00:32:26.597Z","recorded_at":"2026-09-27T00:32:26.597Z","passed":1,"failed":0,"skipped":0,"executions":[{"command":"node scripts/run-tests.mjs test -- tests/integration/linked-test-context-trust.integration.spec.ts","requested_pm_context_mode":"schema","pm_context_mode":"schema","workspace_context_mode":"source","trust_reason":"local_source_ref"}]}]}],"before_hash":"49577f312e9715f2ace451dbd621b3590ff57b18a91657eebb0a7cf85bbd351a","after_hash":"1e04880b324ba49a83184645d8957656a20eb49f7c4ae04c1e0b752bcaa39646","item_hash_version":3,"message":"Track test run summary (test-local-muj33nc5-n2dni3)","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":"3262fda4d541e99d56ea32f776eb765a502b6155ae26b7769ec21d4cf19dc4be"} +{"hash_algorithm":"sha256","ts":"2026-09-27T00:32:57.827Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/1","value":{"created_at":"2026-09-27T00:32:57.827Z","author":"harness:codex","text":"Added opt-in pm_context_mode=none for isolated/snapshot linked tests that create their own SDK tracker roots. The preflight rejects source-workspace or direct PM commands, and execution clears inherited PM_PATH while retaining sandboxed PM_GLOBAL_PATH. Focused integration tests passed (3/3). A temporary Git project outside this repo ran a snapshot linked test importing the built PmClient SDK, created/read two independent tracker roots, and left the source project untouched. SDK public-surface snapshot was explicitly updated because extending the union changes public TypeScript signatures; existing modes keep their prior behavior. Full repo gates are running."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T00:32:57.827Z"}],"before_hash":"1e04880b324ba49a83184645d8957656a20eb49f7c4ae04c1e0b752bcaa39646","after_hash":"f80aac74e91f702d2aa14ef6524a816af0173d86ee4610befd82cef5960da845","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":"c1777a057b36c19edbcb2babc8c2b4a1bf97e2f81f4d2a47c4c2738b6cba7b3f"} +{"hash_algorithm":"sha256","ts":"2026-09-27T00:40:55.731Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"learning_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T00:40:55.731Z"},{"op":"add","path":"/metadata/learnings","value":[{"created_at":"2026-09-27T00:40:55.731Z","author":"harness:codex","text":"A linked test that initializes its own SDK workspaces needs a third safe context choice: omit PM_PATH only when the command executes in a disposable isolated or snapshot workspace, while PM_GLOBAL_PATH remains sandboxed and source/trust preflights still apply."}]}],"before_hash":"f80aac74e91f702d2aa14ef6524a816af0173d86ee4610befd82cef5960da845","after_hash":"d3ed7df53c5358c0fc6802354a8f7c4d7f3be0d881ce8856283129c42aacc030","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":"5c30fa8086dc17f4802da4c2d2e83e3303b348358e77b40af996a5e8ad224ce7"} +{"hash_algorithm":"sha256","ts":"2026-09-27T00:54:53.332Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"files_add","patch":[{"op":"add","path":"/metadata/files/13","value":{"path":"tests/unit/commands/test/linked-test-parsers.spec.ts","scope":"project"}},{"op":"add","path":"/metadata/files/14","value":{"path":"tests/unit/sdk/execution-diagnostics-primitives.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T00:54:53.332Z"}],"before_hash":"d3ed7df53c5358c0fc6802354a8f7c4d7f3be0d881ce8856283129c42aacc030","after_hash":"0d0a4ea1a4f0c26be0201be04662e88cf99abeb071b42764d29184f664267e01","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":"28d444d56b6c22c2973b23f3913859956efcc3e45d97e6e070ba39afdc759328"} +{"hash_algorithm":"sha256","ts":"2026-09-27T00:56:04.231Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/2","value":{"created_at":"2026-09-27T00:56:04.231Z","author":"harness:codex","text":"First full coverage pass found two existing contract assertions listing only schema, tracker, and auto. Updated them to include the new none mode; focused parser and public SDK barrel tests pass. Full 100-percent coverage is rerunning."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T00:56:04.231Z"}],"before_hash":"0d0a4ea1a4f0c26be0201be04662e88cf99abeb071b42764d29184f664267e01","after_hash":"a0a34f77c7b4423c402e0a2e7527d494880aed5ed2b64e7a4eb24c25ab8a481e","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":"4fadd20dc2d1df9d6c8fa06b00bb23c6566546cc309e1673670121dcd0e56a62"} +{"hash_algorithm":"sha256","ts":"2026-09-27T01:37:14.269Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/3","value":{"created_at":"2026-09-27T01:37:14.269Z","author":"harness:codex","text":"Final verification: pnpm test:coverage passed 748 test files / 9,371 tests with exact 100/100/100/100 coverage; pm test pm-t05d8d --run passed 3 focused integration cases. Packed this checkout, installed its tarball into a disposable project, linked a snapshot test with pm_context_mode=none, and verified the packaged SDK created/read two independent PM roots while source project stayed clean; strict project health returned ok=true, warning_count=0. npx and bunx both resolved the packed CLI at 2026.9.26. Full static gate is the remaining delivery check."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T01:37:14.269Z"}],"before_hash":"a0a34f77c7b4423c402e0a2e7527d494880aed5ed2b64e7a4eb24c25ab8a481e","after_hash":"927b31a8a1f357cd9886e6a2eb23574c63565337570348d94b217f81c3fdcdec","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":"086c543c779d559bac043693cd5642d56de2ec5e43abe56d0ee52ede06746b0a"} +{"hash_algorithm":"sha256","ts":"2026-09-27T01:48:56.000Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"docs_add","patch":[{"op":"replace","path":"/metadata/docs/0/path","value":"CHANGELOG.md"},{"op":"add","path":"/metadata/docs/1","value":{"path":"docs/TESTING.md","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T01:48:56.000Z"}],"before_hash":"927b31a8a1f357cd9886e6a2eb23574c63565337570348d94b217f81c3fdcdec","after_hash":"2078be93d8c50b9d00b705e9e9513e428b7d4f9b4f57fc310dc49915f6c42bb9","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":"e9e7ae105e894d0e61c56d591b3d3d20d1f5e136a7a46084926f06980375da32"} +{"hash_algorithm":"sha256","ts":"2026-09-27T01:49:18.624Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-t05d8d","release:pm-kynkl8"]},"topic":{"value":"pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-t05d8d","release:pm-kynkl8"]}},"op":"close","patch":[{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T01:49:18.624Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/closed_at","value":"2026-09-27T01:49:18.597Z"},{"op":"add","path":"/metadata/completed_at","value":"2026-09-27T01:49:18.597Z"},{"op":"add","path":"/metadata/resolution","value":"Added explicit pm_context_mode=none across parser, SDK types, CLI/MCP contracts, help, completion, execution preflight, and docs; non-PM commands run only in isolated or snapshot workspaces, with PM_GLOBAL_PATH still sandboxed."},{"op":"add","path":"/metadata/expected_result","value":"Linked package acceptance creates independent SDK tracker roots without touching source data or weakening default context and trust rules."},{"op":"add","path":"/metadata/actual_result","value":"Three focused linked-context integration cases and linked PM test passed; packed-package snapshot acceptance created and read two independent roots while source remained clean; 9,371 repository tests passed with exact 100/100/100/100 coverage; npx and bunx resolved the packed CLI."},{"op":"add","path":"/metadata/close_reason","value":"Self-isolating SDK package tests run through linked PM tests without inherited PM_PATH."}],"before_hash":"2078be93d8c50b9d00b705e9e9513e428b7d4f9b4f57fc310dc49915f6c42bb9","after_hash":"e2464b46f22be27ab110a7b7414913889393b88c8b55f507aaf4414e7a59121d","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":"a571d92645c8fad6c7d0b6a2b076849d997f885bc2d926e104f502c7278ad1e8"} +{"hash_algorithm":"sha256","ts":"2026-09-27T01:49:19.415Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-t05d8d","release:pm-kynkl8"]},"topic":{"value":"pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-t05d8d","release:pm-kynkl8"]}},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T01:49:19.415Z"}],"before_hash":"e2464b46f22be27ab110a7b7414913889393b88c8b55f507aaf4414e7a59121d","after_hash":"e7e637535e47742021206925e88f8be2e7a44eae773506a6c2d505209eb2d484","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":"c73f472674545475dbecff651b8020e4985d4aa839abffc9c77f52235f23b29e"} +{"hash_algorithm":"sha256","ts":"2026-09-27T01:54:55.658Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-27T01:54:55.658Z"},{"op":"add","path":"/metadata/escape_class","value":"production_defect"},{"op":"add","path":"/metadata/gate_evidence","value":{"disposition":"gate_strengthened","owner":"pm-t05d8d","gate_id":"linked-test-independent-sdk-workspaces","negative_control":"node scripts/run-tests.mjs test -- tests/integration/linked-test-context-trust.integration.spec.ts against the pre-fix runner: isolated SDK workspace test inherits PM_PATH and rejects a second fresh init","local_checks":["node scripts/run-tests.mjs test -- tests/integration/linked-test-context-trust.integration.spec.ts","node scripts/run-tests.mjs coverage","pnpm quality:static"],"hosted_checks":["CI / Gates (static)","CI / Gates (coverage)"]}}],"before_hash":"e7e637535e47742021206925e88f8be2e7a44eae773506a6c2d505209eb2d484","after_hash":"1207850cec1fe795d68fc93dbbd41abf320f4450ddee7a938c41cbca090d9408","item_hash_version":3,"message":"Classify shipped linked-test context defect and record gate-strengthening evidence","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"bfbdb53a59fa1aa073bcba73728eacf42d8308b188a842b47f8034847917d30d"} diff --git a/.agents/pm/issues/pm-erogk1.toon b/.agents/pm/issues/pm-erogk1.toon new file mode 100644 index 000000000..7250ef045 --- /dev/null +++ b/.agents/pm/issues/pm-erogk1.toon @@ -0,0 +1,16 @@ +id: pm-erogk1 +title: Refuse local package installation when source scan stops at entry limit +description: "GitHub #1321: local package source inspection reaches max_entries=10000, reports copy.complete=false and stop_reason=entry_limit, yet dry-run and real install return ok=true, warnings=[] and exit 0; real path may activate a partial package. Fail closed before write/activation, give a packed-package recovery hint, and align dry-run readiness." +type: Issue +status: open +priority: 1 +tags: [] +created_at: "2026-09-27T00:37:15.358Z" +updated_at: "2026-09-27T00:37:15.358Z" +author: "harness:codex" +parent: pm-x6jf +dependencies[1]{id,kind,created_at,author,source_kind,author_source}: + pm-x6jf,discovered_from,"2026-09-27T00:37:15.358Z","harness:codex","cli:create:dep",detected +comments[1]{created_at,author,text}: + "2026-09-27T00:37:15.358Z","harness:codex","Duplicate check across all PM statuses: searched GH #1321, entry_limit, incomplete source scan, partial package copy, and package install. Earlier lifecycle items cover different completed fixes; no existing item owns this limit-truncation defect. Source: https://github.com/unbraind/pm-cli/issues/1321. Intake only; item remains open and unclaimed." +body: "" diff --git a/.agents/pm/issues/pm-kynkl8.toon b/.agents/pm/issues/pm-kynkl8.toon new file mode 100644 index 000000000..6477c7fc9 --- /dev/null +++ b/.agents/pm/issues/pm-kynkl8.toon @@ -0,0 +1,81 @@ +id: pm-kynkl8 +title: Preserve every tracker merge fence during nested init and detect lost active mappings +description: "Nested pm init replaces the root tracker merge-driver attributes, and strict health misses the lost mappings. Reconcile distinct tracker fences in a shared Git repository without erasing other trackers, and make active-tracker strict health fail when the merge fence is missing or stale. GitHub #1320." +type: Issue +status: closed +priority: 1 +tags[4]: "area:init","area:merge",gh-1320,multi-tracker +created_at: "2026-09-26T23:39:49.274Z" +updated_at: "2026-09-27T01:54:54.961Z" +closed_at: "2026-09-27T01:49:16.819Z" +completed_at: "2026-09-27T01:49:16.819Z" +author: "harness:codex" +acceptance_criteria: "Nested init and subsequent merge install preserve both root and nested merge mappings; Strict health detects lost active-tracker mappings; Focused regression, full coverage, and temporary packed CLI acceptance pass" +parent: pm-dj98 +resolution: "Reconciled only the active tracker managed .gitattributes block, preserved sibling and mixed legacy blocks, repaired truncated block audit, and added strict-health remediation for missing or drifted fences." +expected_result: "Root, nested, and root-mounted trackers retain field-aware Git attributes across init and reinstall; strict health rejects a lost active fence." +actual_result: "Four focused merge integration cases and linked PM test passed; 9,371 repository tests passed with exact 100/100/100/100 coverage; packed disposable-project health passed; graph, record-integrity, docstring, lint, and mutation checks passed." +dependencies[5]{id,kind,created_at,author,source_kind,author_source}: + pm-1w3ljt,verifies,"2026-09-27T00:30:21.598Z","harness:codex","cli:update:dep",detected + pm-i4fx,verifies,"2026-09-27T00:30:21.598Z","harness:codex","cli:update:dep",detected + pm-t05d8d,discovered_from,"2026-09-27T00:30:21.598Z","harness:codex","cli:update:dep",detected + pm-g5sx,implements,"2026-09-27T01:47:42.715Z","harness:codex","cli:update:dep",detected + pm-l56d0o,verifies,"2026-09-27T01:47:42.715Z","harness:codex","cli:update:dep",detected +comments[5]{created_at,author,text}: + "2026-09-26T23:39:49.274Z","harness:codex","created_at=now,text=Duplicate check 2026-09-27: all-status searches for GH-1320, nested init merge attributes, merge fence ownership, and tracker fence drift found closed pm-1w3ljt, pm-i4fx, pm-l56d0o, and pm-g5sx. They delivered initial fence installation, coverage, and version-skew handling but not preservation across nested trackers. Source report https://github.com/unbraind/pm-cli/issues/1320." + "2026-09-27T00:32:56.876Z","harness:codex","Root cause: shared .gitattributes reconciliation removed the first managed block without checking tracker ownership, so a nested tracker install could erase the root tracker fence (and vice versa). The fix preserves sibling blocks, replaces only the active tracker block, collapses duplicate owned blocks, and makes strict health report missing/drifted active fences. Focused integration tests passed (2/2), including git check-attr and reinstall for both roots. GitHub #1320 carries the canonical PM link; tracker validation now reports zero orphaned paths." + "2026-09-27T00:56:03.425Z","harness:codex","Full coverage run surfaced a real truncated-fence audit edge case: an unterminated managed block still contained expected patterns, so the audit marked drift but reported zero missing patterns. The parser now records whether each block closed, excludes unclosed content from committed mappings, and retains a drift verdict. Updated strict-health expectation; the focused merge and health suite now passes 25 tests." + "2026-09-27T01:37:13.523Z","harness:codex","Final verification: pnpm test:coverage passed 748 test files / 9,371 tests with exact 100/100/100/100 coverage (65,708 statements; 50,236 branches; 13,602 functions; 62,756 lines), 2 Windows-only tests skipped on Linux. pm test pm-kynkl8 --run passed 4 integration cases after the final edits. The new cases cover mixed legacy/versioned fences, root-mounted trackers with siblings, ownerless legacy migration, and lone nested-block preservation. pnpm build, pnpm typecheck, ESLint, docstring/static scanner, docs-skills gate, and npm audit high passed. Packaged CLI smoke in a disposable Git project passed, including strict health and npx/bunx version commands." + "2026-09-27T01:49:01.054Z","harness:codex",Graph-composition gate identified two prose-only references in this item’s duplicate-check history. Added typed verifies link to closed version-skew issue pm-l56d0o and implements link to closed merge-semantics feature pm-g5sx; graph-composition now passes with prose-edge gaps at its 1236 ceiling. Record-integrity assurance and the mandatory mutation gate also passed (323/330 mutants killed; seven classified equivalent). +learnings[1]{created_at,author,text}: + "2026-09-27T00:40:54.856Z","harness:codex","A repository may host multiple PM roots sharing one .gitattributes. Merge-fence maintenance must identify ownership by tracker-relative pattern prefix, preserve sibling blocks, and audit the active tracker separately from clone-local Git driver configuration." +files[10]{path,scope}: + src/core/diagnostics/remediation.ts,project + src/sdk/generated/generated-error-code-catalog-part-1.ts,project + src/sdk/generated/generated-error-code-catalog-part-2.ts,project + src/sdk/governance/health-merge-evidence.ts,project + src/sdk/governance/health.ts,project + src/sdk/merge/install.ts,project + tests/fixtures/contracts/full.json,project + tests/integration/merge-fence-version-skew.integration.spec.ts,project + tests/unit/sdk/governance/health-verdict-authority.spec.ts,project + tests/unit/sdk/merge-safety.spec.ts,project +tests[1]{command,scope,timeout_seconds,provenance{author,created_at,source_kind,source_ref}}: + node scripts/run-tests.mjs test -- tests/integration/merge-fence-version-skew.integration.spec.ts,project,2400,"harness:codex","2026-09-27T00:31:51.396Z",local_mutation,fix/isolated-project-context-and-merge-fences +test_runs[2]: + - run_id: test-local-muj33ain-jamd0w + kind: test + status: passed + started_at: "2026-09-27T00:31:59.684Z" + finished_at: "2026-09-27T00:32:09.983Z" + recorded_at: "2026-09-27T00:32:09.983Z" + 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/merge-fence-version-skew.integration.spec.ts,schema,schema,source,local_source_ref + - run_id: test-local-muj5eq4p-ult72v + kind: test + status: passed + started_at: "2026-09-27T01:36:51.877Z" + finished_at: "2026-09-27T01:37:02.665Z" + recorded_at: "2026-09-27T01:37:02.665Z" + 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/merge-fence-version-skew.integration.spec.ts,schema,schema,source,local_source_ref +docs[3]{path,scope}: + CHANGELOG.md,project + docs/generated/REFUSAL_CLOSURE_CENSUS.md,project + docs/TESTING.md,project +close_reason: Nested tracker merge fences are preserved and strict health detects missing active mappings. +escape_class: production_defect +gate_evidence: + disposition: gate_strengthened + owner: pm-kynkl8 + gate_id: merge-fence-active-tracker-ownership + negative_control: "node scripts/run-tests.mjs test -- tests/integration/merge-fence-version-skew.integration.spec.ts against the pre-fix merge installer: nested init loses root merge=pm-item-toon and strict health passes" + local_checks[3]: node scripts/run-tests.mjs test -- tests/integration/merge-fence-version-skew.integration.spec.ts,node scripts/run-tests.mjs coverage,"pnpm quality:static" + hosted_checks[2]: CI / Gates (static),CI / Gates (coverage) +body: "" diff --git a/.agents/pm/issues/pm-t05d8d.toon b/.agents/pm/issues/pm-t05d8d.toon index 00c1b6fc2..36a4cede9 100644 --- a/.agents/pm/issues/pm-t05d8d.toon +++ b/.agents/pm/issues/pm-t05d8d.toon @@ -2,21 +2,73 @@ id: pm-t05d8d title: Allow self-isolating linked package tests without inherited PM_PATH description: "A non-PM linked package test that initializes disposable SDK workspaces inherits the runner sandbox PM_PATH, so explicit cwd still resolves to a shared tracker and later fixtures fail. Provide a safe explicit context mode that omits inherited PM_PATH for self-isolating commands while keeping source workspace trust and default sandbox policy." type: Issue -status: open +status: closed priority: 1 tags[5]: "area:sdk","area:test",context-management,gh-1318,packages created_at: "2026-09-26T20:34:16.625Z" -updated_at: "2026-09-26T22:18:49.678Z" +updated_at: "2026-09-27T01:54:55.658Z" +closed_at: "2026-09-27T01:49:18.597Z" +completed_at: "2026-09-27T01:49:18.597Z" author: "harness:codex" acceptance_criteria: "An explicit linked-test context mode lets non-PM package commands create independent disposable SDK workspaces; default linked tests retain isolated PM_PATH; PM commands cannot bypass tracker isolation; source workspace trust remains enforced; CLI/SDK metadata and execution receipts expose requested and effective mode; focused, full coverage, and temporary package acceptance pass." parent: pm-ugqx +resolution: "Added explicit pm_context_mode=none across parser, SDK types, CLI/MCP contracts, help, completion, execution preflight, and docs; non-PM commands run only in isolated or snapshot workspaces, with PM_GLOBAL_PATH still sandboxed." +expected_result: Linked package acceptance creates independent SDK tracker roots without touching source data or weakening default context and trust rules. +actual_result: "Three focused linked-context integration cases and linked PM test passed; packed-package snapshot acceptance created and read two independent roots while source remained clean; 9,371 repository tests passed with exact 100/100/100/100 coverage; npx and bunx resolved the packed CLI." dependencies[4]{id,kind,created_at,author,source_kind,author_source}: pm-954h0o,verifies,"2026-09-26T20:34:16.625Z","harness:codex","cli:create:dep",detected pm-alhqbz,verifies,"2026-09-26T20:34:16.625Z","harness:codex","cli:create:dep",detected pm-e97jyf,discovered_from,"2026-09-26T20:34:16.625Z","harness:codex","cli:create:dep",detected pm-6pij,verifies,"2026-09-26T22:18:49.388Z","harness:codex","cli:update:dep",detected -comments[1]{created_at,author,text}: +comments[4]{created_at,author,text}: "2026-09-26T20:34:16.625Z","harness:codex","Duplicate check 2026-09-26: all-status PM searches for GH-1318, inherited PM_PATH, self-isolating linked tests, and context mode found closed pm-e97jyf, pm-alhqbz, pm-954h0o, and pm-6pij. Those delivered source/tracker parity and nested-write protection but do not provide a no-inherited-PM_PATH mode. GitHub report: https://github.com/unbraind/pm-cli/issues/1318." + "2026-09-27T00:32:57.827Z","harness:codex","Added opt-in pm_context_mode=none for isolated/snapshot linked tests that create their own SDK tracker roots. The preflight rejects source-workspace or direct PM commands, and execution clears inherited PM_PATH while retaining sandboxed PM_GLOBAL_PATH. Focused integration tests passed (3/3). A temporary Git project outside this repo ran a snapshot linked test importing the built PmClient SDK, created/read two independent tracker roots, and left the source project untouched. SDK public-surface snapshot was explicitly updated because extending the union changes public TypeScript signatures; existing modes keep their prior behavior. Full repo gates are running." + "2026-09-27T00:56:04.231Z","harness:codex","First full coverage pass found two existing contract assertions listing only schema, tracker, and auto. Updated them to include the new none mode; focused parser and public SDK barrel tests pass. Full 100-percent coverage is rerunning." + "2026-09-27T01:37:14.269Z","harness:codex","Final verification: pnpm test:coverage passed 748 test files / 9,371 tests with exact 100/100/100/100 coverage; pm test pm-t05d8d --run passed 3 focused integration cases. Packed this checkout, installed its tarball into a disposable project, linked a snapshot test with pm_context_mode=none, and verified the packaged SDK created/read two independent PM roots while source project stayed clean; strict project health returned ok=true, warning_count=0. npx and bunx both resolved the packed CLI at 2026.9.26. Full static gate is the remaining delivery check." notes[1]{created_at,author,text}: "2026-09-26T20:39:23.664Z","harness:codex","Investigation 2026-09-26: linked-test runner currently injects sandbox PM_PATH for every child; parser, item serialization, import normalization, SDK/CLI help, and execution receipts all model schema|tracker|auto. A no-inherited-PM_PATH mode must refuse source working-directory execution: nested pm commands would otherwise rediscover the real source tracker. Safer design is an explicit mode paired with a disposable snapshot or isolated working directory; snapshot retains package files and binds .agents/pm to a sandbox tracker while explicit SDK init cwd can select a new temp tracker. Preserve protected env keys and clone trust checks. No source implementation or acceptance proof yet." +learnings[1]{created_at,author,text}: + "2026-09-27T00:40:55.731Z","harness:codex","A linked test that initializes its own SDK workspaces needs a third safe context choice: omit PM_PATH only when the command executes in a disposable isolated or snapshot workspace, while PM_GLOBAL_PATH remains sandboxed and source/trust preflights still apply." +files[15]{path,scope}: + scripts/agent-token-surface-baseline.json,project + sdk/public-surface.json,project + src/cli/register-operations.ts,project + src/core/item/item-format.ts,project + src/sdk/cli-contracts/commander-mutation-options.ts,project + src/sdk/cli-contracts/tool-parameter-tables.ts,project + src/sdk/completion/fish.ts,project + src/sdk/completion/zsh.ts,project + src/sdk/test/execution.ts,project + src/sdk/test/parsers.ts,project + src/types.ts,project + tests/fixtures/contracts/full.json,project + tests/integration/linked-test-context-trust.integration.spec.ts,project + tests/unit/commands/test/linked-test-parsers.spec.ts,project + tests/unit/sdk/execution-diagnostics-primitives.spec.ts,project +tests[1]{command,scope,timeout_seconds,provenance{author,created_at,source_kind,source_ref}}: + node scripts/run-tests.mjs test -- tests/integration/linked-test-context-trust.integration.spec.ts,project,2400,"harness:codex","2026-09-27T00:31:52.044Z",local_mutation,fix/isolated-project-context-and-merge-fences +test_runs[1]: + - run_id: test-local-muj33nc5-n2dni3 + kind: test + status: passed + started_at: "2026-09-27T00:32:13.902Z" + finished_at: "2026-09-27T00:32:26.597Z" + recorded_at: "2026-09-27T00:32:26.597Z" + 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/linked-test-context-trust.integration.spec.ts,schema,schema,source,local_source_ref +docs[2]{path,scope}: + CHANGELOG.md,project + docs/TESTING.md,project +close_reason: Self-isolating SDK package tests run through linked PM tests without inherited PM_PATH. +escape_class: production_defect +gate_evidence: + disposition: gate_strengthened + owner: pm-t05d8d + gate_id: linked-test-independent-sdk-workspaces + negative_control: "node scripts/run-tests.mjs test -- tests/integration/linked-test-context-trust.integration.spec.ts against the pre-fix runner: isolated SDK workspace test inherits PM_PATH and rejects a second fresh init" + local_checks[3]: node scripts/run-tests.mjs test -- tests/integration/linked-test-context-trust.integration.spec.ts,node scripts/run-tests.mjs coverage,"pnpm quality:static" + hosted_checks[2]: CI / Gates (static),CI / Gates (coverage) body: "" diff --git a/CHANGELOG.md b/CHANGELOG.md index 8702a2155..316d5382f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Fixed +- Allow self-isolating linked package tests without inherited PM_PATH ([pm-t05d8d](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/issues/pm-t05d8d.toon)) +- Preserve every tracker merge fence during nested init and detect lost active mappings ([pm-kynkl8](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/issues/pm-kynkl8.toon)) - SDK complete-list receipt falsely reports legacy aliases for canonical helper ([pm-vf9iaf](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/issues/pm-vf9iaf.toon)) ### Security diff --git a/docs/TESTING.md b/docs/TESTING.md index e286b98b6..f598f4a16 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -2,14 +2,17 @@ This page describes safe local tests, linked tests, coverage, and release-readiness checks. +Current linked-test and merge-fence work: [pm-t05d8d](../.agents/pm/issues/pm-t05d8d.toon), [pm-kynkl8](../.agents/pm/issues/pm-kynkl8.toon). + Tracked implementation updates: [pm-52eh](../.agents/pm/features/pm-52eh.toon), [pm-mcxr](../.agents/pm/issues/pm-mcxr.toon), [pm-u42x](../.agents/pm/issues/pm-u42x.toon), [pm-atfm](../.agents/pm/features/pm-atfm.toon), [pm-xmp5](../.agents/pm/tasks/pm-xmp5.toon), [pm-39cqqx](../.agents/pm/tasks/pm-39cqqx.toon), [pm-5cgm2z](../.agents/pm/chores/pm-5cgm2z.toon), [pm-avv3wx](../.agents/pm/issues/pm-avv3wx.toon), [pm-rizqb6](../.agents/pm/issues/pm-rizqb6.toon), [pm-95h7pg](../.agents/pm/issues/pm-95h7pg.toon), [pm-giks4s](../.agents/pm/issues/pm-giks4s.toon), [pm-xa3t0o](../.agents/pm/issues/pm-xa3t0o.toon), [pm-e97jyf](../.agents/pm/issues/pm-e97jyf.toon), [pm-efkvdy](../.agents/pm/issues/pm-efkvdy.toon), [pm-ed28wi](../.agents/pm/issues/pm-ed28wi.toon), and [pm-5ug5xq](../.agents/pm/issues/pm-5ug5xq.toon). ## Agent Quick Context - Unit and integration tests must not read or write real `.agents/pm` data. - Prefer `node scripts/run-tests.mjs ...` because it creates sandboxed `PM_PATH` and `PM_GLOBAL_PATH`. -- Linked-test execution injects isolated `PM_PATH` and `PM_GLOBAL_PATH` for - every command and applies stored-command provenance checks before process +- Linked-test execution injects isolated `PM_GLOBAL_PATH` and normally + `PM_PATH`; `pm_context_mode=none` omits `PM_PATH` for self-isolating SDK tests + in disposable workspaces. It applies stored-command provenance checks before process creation. Direct runners are accepted; prefer `node scripts/run-tests.mjs ...` when the repository provides it because the wrapper also reproduces build, coverage, and cleanup policy. @@ -510,6 +513,11 @@ only their schema roots; a non-PM command explicitly configured with data are materialized only when at least one selected command requires tracker context. This preserves source isolation without copying an unrelated tracker into constrained temporary storage. +For SDK tests that create independent temporary PM roots, use +`pm_context_mode: "none"` with `workspace_context_mode: "isolated"` or +`"snapshot"`. The runner omits `PM_PATH` so each SDK client can resolve its +explicit workspace independently; `PM_GLOBAL_PATH` remains sandboxed. Source +workspace execution and direct PM commands refuse this mode before execution. Capacity, permission, and resource failures while seeding a required tracker surface as typed, path-redacted host-environment refusals with recovery steps. When a legacy source tracker has settings but no `_workspace` history, tracker @@ -593,6 +601,7 @@ Use explicit modes when needed: pm test --run --pm-context schema pm test --run --pm-context tracker pm test --run --pm-context auto --check-context --auto-pm-context +pm test --add-json '{"command":"node scripts/run-tests.mjs test -- tests/integration/independent-sdk-workspaces.spec.ts","pm_context_mode":"none","workspace_context_mode":"snapshot"}' ``` For complex linked-test commands, prefer JSON input so shell syntax survives unchanged: diff --git a/docs/generated/REFUSAL_CLOSURE_CENSUS.md b/docs/generated/REFUSAL_CLOSURE_CENSUS.md index 6635d9299..8ca28955f 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: 385 +- Catalog error codes: 387 - 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: 366 -- Coverage fraction: 0.049351 +- Uncovered error codes: 368 +- Coverage fraction: 0.049096 - Closed-domain probes: 19 - Grammar probes: 117 @@ -203,6 +203,8 @@ Every catalog code is listed. An `uncovered` row is an explicit closure obligati | `merge_decisions_unreviewed` | `merge_decisions_unreviewed` | uncovered | none | 0 | | `merge_driver_configuration_drift` | `merge_driver_configuration_drift` | uncovered | none | 0 | | `merge_driver_configuration_missing` | `merge_driver_configuration_missing` | uncovered | none | 0 | +| `merge_fence_drift` | `merge_fence_drift` | uncovered | none | 0 | +| `merge_fence_missing` | `merge_fence_missing` | uncovered | none | 0 | | `merge_git_config_unwritable` | `merge_git_config_unwritable` | uncovered | none | 0 | | `merge_receipt_evidence_invalid` | `merge_receipt_evidence_invalid` | uncovered | none | 0 | | `merge_receipt_history_reference_missing` | `merge_receipt_history_reference_missing` | uncovered | none | 0 | diff --git a/scripts/agent-token-surface-baseline.json b/scripts/agent-token-surface-baseline.json index 068932def..7161f042e 100644 --- a/scripts/agent-token-surface-baseline.json +++ b/scripts/agent-token-surface-baseline.json @@ -3,17 +3,17 @@ "metric": "utf8_bytes", "headroom": 1.1, "surfaces": { - "root_help": 3692, - "per_command_total": 247565, - "full_help_surface": 251257, + "root_help": 3507, + "per_command_total": 252652, + "full_help_surface": 256159, "contracts": { - "summary_toon": 15630, - "summary_json": 19432, - "json": 19293, - "full": 1332593, - "bounded_full": 614 + "summary_toon": 13000, + "summary_json": 19800, + "json": 19798, + "full": 1470352, + "bounded_full": 613 }, - "mcp_tools_list": 48783, + "mcp_tools_list": 50863, "required_commands": [ "assurance apply", "assurance derive", @@ -83,16 +83,20 @@ "plan decision": 10181, "plan approve": 10179, "plan resume": 10177, - "update": 9981, + "update": 10135, "create": 9394, - "list": 9204, - "search": 8261, - "ops validate": 4870, - "context": 3441, + "list": 9125, + "search": 8258, + "ops validate": 4645, + "context": 3780, "get": 3049, - "context next": 2165, - "close": 2077, - "claim": 2020, + "context next": 2324, + "close": 2237, + "workspace init": 2226, + "claim": 1975, + "workspace": 1611, + "ops": 1591, + "assurance lineages": 1229, "assurance risk": 1152, "assurance run": 1150, "assurance promote": 1140, @@ -109,12 +113,8 @@ "workspace snapshot inspect": 890, "workspace snapshot list": 888, "workspace snapshot delete": 888, - "release": 537, - "workspace position": 136, - "assurance lineages": 1229, - "ops": 1591, - "workspace init": 2204, - "workspace": 1611 + "release": 512, + "workspace position": 136 } } } diff --git a/sdk/public-surface.json b/sdk/public-surface.json index c6986130f..ef122aea3 100644 --- a/sdk/public-surface.json +++ b/sdk/public-surface.json @@ -2088,6 +2088,15 @@ ], "package_version": "2026.9.25", "reason": "Additive optional per-query floor violations and embedding execution receipts; query-set contract adds optional minimum metadata. Existing callers and query sets retain behavior." + }, + { + "changes": [ + "signature ./sdk:LINKED_TEST_PM_CONTEXT_MODE_VALUES", + "signature ./sdk:LinkedTest", + "signature ./sdk/runtime:LinkedTest" + ], + "package_version": "2026.9.26", + "reason": "Add opt-in none linked-test PM context for self-isolating SDK workspaces; existing modes retain their behavior" } ], "entrypoints": { @@ -9922,7 +9931,7 @@ "classification": "advanced_export", "kind": "value", "name": "LINKED_TEST_PM_CONTEXT_MODE_VALUES", - "signature": "readonly [\"schema\", \"tracker\", \"auto\"]" + "signature": "readonly [\"schema\", \"tracker\", \"auto\", \"none\"]" }, { "classification": "advanced_export", @@ -10000,7 +10009,7 @@ "classification": "advanced_export", "kind": "interface", "name": "LinkedTest", - "signature": "export interface LinkedTest { command?: string; path?: string; scope: LinkScope; timeout_seconds?: number; pm_context_mode?: \"schema\" | \"tracker\" | \"auto\"; workspace_context_mode?: \"source\" | \"isolated\" | \"snapshot\"; provenance?: LinkedTestProvenance; provenance_invalid?: true; env_set?: Record; env_clear?: string[]; shared_host_safe?: boolean; assert_stdout_contains?: string[]; assert_stdout_regex?: string[]; assert_stderr_contains?: string[]; assert_stderr_regex?: string[]; assert_stdout_min_lines?: number; assert_json_field_equals?: Record; assert_json_field_gte?: Record; note?: string; }" + "signature": "export interface LinkedTest { command?: string; path?: string; scope: LinkScope; timeout_seconds?: number; pm_context_mode?: \"schema\" | \"tracker\" | \"auto\" | \"none\"; workspace_context_mode?: \"source\" | \"isolated\" | \"snapshot\"; provenance?: LinkedTestProvenance; provenance_invalid?: true; env_set?: Record; env_clear?: string[]; shared_host_safe?: boolean; assert_stdout_contains?: string[]; assert_stdout_regex?: string[]; assert_stderr_contains?: string[]; assert_stderr_regex?: string[]; assert_stdout_min_lines?: number; assert_json_field_equals?: Record; assert_json_field_gte?: Record; note?: string; }" }, { "classification": "advanced_export", @@ -32560,7 +32569,7 @@ "classification": "advanced_export", "kind": "interface", "name": "LinkedTest", - "signature": "export interface LinkedTest { command?: string; path?: string; scope: LinkScope; timeout_seconds?: number; pm_context_mode?: \"schema\" | \"tracker\" | \"auto\"; workspace_context_mode?: \"source\" | \"isolated\" | \"snapshot\"; provenance?: LinkedTestProvenance; provenance_invalid?: true; env_set?: Record; env_clear?: string[]; shared_host_safe?: boolean; assert_stdout_contains?: string[]; assert_stdout_regex?: string[]; assert_stderr_contains?: string[]; assert_stderr_regex?: string[]; assert_stdout_min_lines?: number; assert_json_field_equals?: Record; assert_json_field_gte?: Record; note?: string; }" + "signature": "export interface LinkedTest { command?: string; path?: string; scope: LinkScope; timeout_seconds?: number; pm_context_mode?: \"schema\" | \"tracker\" | \"auto\" | \"none\"; workspace_context_mode?: \"source\" | \"isolated\" | \"snapshot\"; provenance?: LinkedTestProvenance; provenance_invalid?: true; env_set?: Record; env_clear?: string[]; shared_host_safe?: boolean; assert_stdout_contains?: string[]; assert_stdout_regex?: string[]; assert_stderr_contains?: string[]; assert_stderr_regex?: string[]; assert_stdout_min_lines?: number; assert_json_field_equals?: Record; assert_json_field_gte?: Record; note?: string; }" }, { "classification": "supported", @@ -35347,6 +35356,8 @@ "merge_decisions_unreviewed", "merge_driver_configuration_drift", "merge_driver_configuration_missing", + "merge_fence_drift", + "merge_fence_missing", "merge_git_config_unwritable", "merge_receipt_evidence_invalid", "merge_receipt_history_reference_missing", diff --git a/src/cli/register-operations.ts b/src/cli/register-operations.ts index ae6c35658..2348e0f2c 100644 --- a/src/cli/register-operations.ts +++ b/src/cli/register-operations.ts @@ -885,7 +885,7 @@ function addLinkedTestExecutionOptions(command: Command): Command { ) .option( "--pm-context ", - "PM linked-test context mode: schema|tracker|auto (default: schema)", + "PM test context: schema|tracker|auto|none (default: schema)", ) .option( "--override-linked-pm-context", diff --git a/src/core/diagnostics/remediation.ts b/src/core/diagnostics/remediation.ts index 246f39b35..61af11cc2 100644 --- a/src/core/diagnostics/remediation.ts +++ b/src/core/diagnostics/remediation.ts @@ -135,6 +135,16 @@ export const REMEDIATION_REGISTRY: readonly RemediationEntry[] = Object.freeze([ command: "pm merge install", summary: "Reinstall clone-local Git drivers after runtime or package paths change.", }, + { + code: "merge_fence_missing", + command: "pm merge install", + summary: "Install the active tracker's committed Git merge attributes.", + }, + { + code: "merge_fence_drift", + command: "pm merge install", + summary: "Restore the active tracker's merge mappings while retaining sibling tracker fences.", + }, { code: "validate_merge_driver_configuration", command: "pm merge install", diff --git a/src/core/item/item-format.ts b/src/core/item/item-format.ts index 007511bb5..b9641f558 100644 --- a/src/core/item/item-format.ts +++ b/src/core/item/item-format.ts @@ -63,6 +63,7 @@ const LINKED_TEST_PM_CONTEXT_MODE_VALUES = new Set([ "schema", "tracker", "auto", + "none", ]); const LINKED_TEST_WORKSPACE_CONTEXT_MODE_VALUES = new Set([ "source", diff --git a/src/sdk/cli-contracts/commander-mutation-options.ts b/src/sdk/cli-contracts/commander-mutation-options.ts index 5ce468172..64b822382 100644 --- a/src/sdk/cli-contracts/commander-mutation-options.ts +++ b/src/sdk/cli-contracts/commander-mutation-options.ts @@ -1076,7 +1076,7 @@ export const UPDATE_COMMANDER_OPTION_REGISTRATION_CONTRACTS: CommanderOptionRegi keys: ["test", "linkedTest"], option: "--test ", description: - "Append linked test command=,path=,scope=,timeout_seconds=,pm_context_mode= (also accepts markdown pairs and - for stdin; repeatable)", + "Append test command=,path=,scope=,timeout_seconds=,pm_context_mode= (also accepts markdown pairs and - for stdin; repeatable)", repeatable: true, aliasOptions: [ { diff --git a/src/sdk/cli-contracts/tool-parameter-tables.ts b/src/sdk/cli-contracts/tool-parameter-tables.ts index 8d5e9317b..b3f04102d 100644 --- a/src/sdk/cli-contracts/tool-parameter-tables.ts +++ b/src/sdk/cli-contracts/tool-parameter-tables.ts @@ -402,7 +402,7 @@ export const PM_TOOL_PARAMETER_PROPERTIES: Record = { fixManagedState: { type: "boolean" }, isolated: { type: "boolean" }, ignoreGlobal: { type: "boolean" }, - pmContext: { type: "string", enum: ["schema", "tracker", "auto"] }, + pmContext: { type: "string", enum: ["schema", "tracker", "auto", "none"] }, overrideLinkedPmContext: { type: "boolean" }, workspaceContext: { type: "string", @@ -1556,8 +1556,8 @@ export const PM_TOOL_PARAMETER_METADATA: Record< }, pmContext: { description: - "PM linked-test context mode (schema keeps isolated tracker data; tracker seeds source tracker data; auto uses tracker for PM tracker-read linked commands).", - examples: ["schema", "tracker", "auto"], + "PM linked-test context mode (schema keeps isolated tracker data; tracker seeds source tracker data; auto uses tracker for PM tracker-read commands; none omits PM_PATH for self-isolating SDK tests in isolated or snapshot workspaces).", + examples: ["schema", "tracker", "auto", "none"], }, overrideLinkedPmContext: { description: diff --git a/src/sdk/completion/fish.ts b/src/sdk/completion/fish.ts index 44cf4abd1..812adb2d2 100644 --- a/src/sdk/completion/fish.ts +++ b/src/sdk/completion/fish.ts @@ -901,7 +901,7 @@ complete -c pm -n '__fish_seen_subcommand_from test' -l progress -d 'Emit linked complete -c pm -n '__fish_seen_subcommand_from test' -l env-set -d 'Set linked-test runtime environment values' -r complete -c pm -n '__fish_seen_subcommand_from test' -l env-clear -d 'Clear linked-test runtime environment values' -r complete -c pm -n '__fish_seen_subcommand_from test' -l shared-host-safe -d 'Apply shared-host-safe runtime defaults' -complete -c pm -n '__fish_seen_subcommand_from test' -l pm-context -d 'PM linked-test context mode' -r -a 'schema tracker auto' +complete -c pm -n '__fish_seen_subcommand_from test' -l pm-context -d 'PM linked-test context mode' -r -a 'schema tracker auto none' complete -c pm -n '__fish_seen_subcommand_from test' -l override-linked-pm-context -d 'Force run-level --pm-context over per-linked-test metadata' complete -c pm -n '__fish_seen_subcommand_from test' -l fail-on-context-mismatch -d 'Fail when context item counts mismatch' complete -c pm -n '__fish_seen_subcommand_from test' -l fail-on-skipped -d 'Treat skipped linked tests as dependency failures' @@ -923,7 +923,7 @@ complete -c pm -n '__pm_history_operation test-all' -l progress -d 'Emit linked- complete -c pm -n '__pm_history_operation test-all' -l env-set -d 'Set linked-test runtime environment values' -r complete -c pm -n '__pm_history_operation test-all' -l env-clear -d 'Clear linked-test runtime environment values' -r complete -c pm -n '__pm_history_operation test-all' -l shared-host-safe -d 'Apply shared-host-safe runtime defaults' -complete -c pm -n '__pm_history_operation test-all' -l pm-context -d 'PM linked-test context mode' -r -a 'schema tracker auto' +complete -c pm -n '__pm_history_operation test-all' -l pm-context -d 'PM linked-test context mode' -r -a 'schema tracker auto none' complete -c pm -n '__pm_history_operation test-all' -l override-linked-pm-context -d 'Force run-level --pm-context over per-linked-test metadata' complete -c pm -n '__pm_history_operation test-all' -l fail-on-context-mismatch -d 'Fail when context item counts mismatch' complete -c pm -n '__pm_history_operation test-all' -l fail-on-skipped -d 'Treat skipped linked tests as dependency failures' diff --git a/src/sdk/completion/zsh.ts b/src/sdk/completion/zsh.ts index 48bc43e1b..af803413b 100644 --- a/src/sdk/completion/zsh.ts +++ b/src/sdk/completion/zsh.ts @@ -1077,7 +1077,7 @@ ${renderZshArgumentSpecs(ATTEST_INVOCATIONS.map((flag) => `'${flag.flag}[${flag. '--env-set[Set linked-test runtime environment values]:entry' \\ '--env-clear[Clear linked-test runtime environment values]:name' \\ '--shared-host-safe[Apply shared-host-safe runtime defaults]' \\ - '--pm-context[PM linked-test context mode]:(schema tracker auto)' \\ + '--pm-context[PM linked-test context mode]:(schema tracker auto none)' \\ '--override-linked-pm-context[Force run-level --pm-context over per-linked-test pm_context_mode metadata]' \\ '--fail-on-context-mismatch[Fail when context item counts mismatch]' \\ '--fail-on-skipped[Treat skipped linked tests as dependency failures]' \\ @@ -1102,7 +1102,7 @@ ${renderZshArgumentSpecs(ATTEST_INVOCATIONS.map((flag) => `'${flag.flag}[${flag. '--env-set[Set linked-test runtime environment values]:entry' \\ '--env-clear[Clear linked-test runtime environment values]:name' \\ '--shared-host-safe[Apply shared-host-safe runtime defaults]' \\ - '--pm-context[PM linked-test context mode]:(schema tracker auto)' \\ + '--pm-context[PM linked-test context mode]:(schema tracker auto none)' \\ '--override-linked-pm-context[Force run-level --pm-context over per-linked-test pm_context_mode metadata]' \\ '--fail-on-context-mismatch[Fail when context item counts mismatch]' \\ '--fail-on-skipped[Treat skipped linked tests as dependency failures]' \\ diff --git a/src/sdk/generated/generated-error-code-catalog-part-1.ts b/src/sdk/generated/generated-error-code-catalog-part-1.ts index 1aff75fb2..67f68e9cb 100644 --- a/src/sdk/generated/generated-error-code-catalog-part-1.ts +++ b/src/sdk/generated/generated-error-code-catalog-part-1.ts @@ -2474,6 +2474,32 @@ export const PM_ERROR_CODE_CATALOG_PART_1: PmErrorCodeContract[] = [ canonical_code: "merge_driver_configuration_missing", aliases: [], }, + { + code: "merge_fence_drift", + meaning: "Merge fence drift condition.", + stability: "provisional", + exit_code: 1, + class: "generic_failure", + recovery: + "Inspect the structured error guidance and retry the suggested command.", + sources: ["core/diagnostics/remediation.ts"], + emitting_commands: ["*"], + canonical_code: "merge_fence_drift", + aliases: [], + }, + { + code: "merge_fence_missing", + meaning: "Merge fence missing condition.", + stability: "provisional", + exit_code: 1, + class: "generic_failure", + recovery: + "Inspect the structured error guidance and retry the suggested command.", + sources: ["core/diagnostics/remediation.ts"], + emitting_commands: ["*"], + canonical_code: "merge_fence_missing", + aliases: [], + }, { code: "merge_git_config_unwritable", meaning: "Merge git config unwritable condition.", @@ -2526,17 +2552,4 @@ export const PM_ERROR_CODE_CATALOG_PART_1: PmErrorCodeContract[] = [ canonical_code: "merge_receipts_pending", aliases: [], }, - { - code: "merge_reconcile_receipt_evidence_untrusted", - meaning: "Merge reconcile receipt evidence untrusted condition.", - stability: "provisional", - exit_code: 4, - class: "conflict", - recovery: - "Inspect the structured error guidance and retry the suggested command.", - sources: ["sdk/history-repair.ts"], - emitting_commands: ["*"], - canonical_code: "merge_reconcile_receipt_evidence_untrusted", - aliases: [], - }, ]; diff --git a/src/sdk/generated/generated-error-code-catalog-part-2.ts b/src/sdk/generated/generated-error-code-catalog-part-2.ts index 5ea492049..a8ad54594 100644 --- a/src/sdk/generated/generated-error-code-catalog-part-2.ts +++ b/src/sdk/generated/generated-error-code-catalog-part-2.ts @@ -7,6 +7,19 @@ import type { PmErrorCodeContract } from "../error-code-catalog.js"; /** Generated partition 2 of the exhaustive error-code catalog. */ export const PM_ERROR_CODE_CATALOG_PART_2: PmErrorCodeContract[] = [ + { + code: "merge_reconcile_receipt_evidence_untrusted", + meaning: "Merge reconcile receipt evidence untrusted condition.", + stability: "provisional", + exit_code: 4, + class: "conflict", + recovery: + "Inspect the structured error guidance and retry the suggested command.", + sources: ["sdk/history-repair.ts"], + emitting_commands: ["*"], + canonical_code: "merge_reconcile_receipt_evidence_untrusted", + aliases: [], + }, { code: "merge_root_not_found", meaning: "Merge root not found condition.", diff --git a/src/sdk/governance/health-merge-evidence.ts b/src/sdk/governance/health-merge-evidence.ts new file mode 100644 index 000000000..302af2f02 --- /dev/null +++ b/src/sdk/governance/health-merge-evidence.ts @@ -0,0 +1,63 @@ +/** + * @module sdk/governance/health-merge-evidence + * + * Reports shared Git attribute and merge receipt evidence for tracker health. + */ +import type { PmSettings } from "../../types/index.js"; +import { + auditMergeAttributeFence, + resolveProjectMergeTypeFolders, +} from "../merge/install.js"; + +/** Audit the active tracker's shared attribute fence and report strict merge protection findings. */ +export async function resolveMergeFenceHealthEvidence( + pmRoot: string, + gitWorkspaceRoot: string | null, + settings: PmSettings, + required: boolean, +): Promise<{ + audit: Awaited> | null; + count: number; + warnings: string[]; +}> { + if (gitWorkspaceRoot === null) { + return { audit: null, count: 0, warnings: [] }; + } + const audit = await auditMergeAttributeFence( + pmRoot, + resolveProjectMergeTypeFolders(settings), + ); + if (audit.status === "ok") { + return { audit, count: 0, warnings: [] }; + } + const count = Math.max( + 1, + audit.missing_patterns.length + audit.stale_patterns.length, + ); + return { + audit, + count, + warnings: required + ? [`merge_fence_${audit.status === "not_installed" ? "missing" : "drift"}:${count}`] + : [], + }; +} + +/** Report unresolved durable merge receipt evidence in compact warning form. */ +export function buildMergeReceiptEvidenceWarnings(params: { + invalidEvidenceCount: number; + missingHistoryReferenceCount: number; +}): string[] { + const warnings: string[] = []; + if (params.invalidEvidenceCount > 0) { + warnings.push( + `merge_receipt_evidence_invalid:${params.invalidEvidenceCount}`, + ); + } + if (params.missingHistoryReferenceCount > 0) { + warnings.push( + `merge_receipt_history_reference_missing:${params.missingHistoryReferenceCount}`, + ); + } + return warnings; +} diff --git a/src/sdk/governance/health.ts b/src/sdk/governance/health.ts index b007d84e9..fdd8f08db 100644 --- a/src/sdk/governance/health.ts +++ b/src/sdk/governance/health.ts @@ -110,6 +110,10 @@ import { type StaleInProgressScan, } from "./stale-work.js"; import { scanTrackedRuntimeCache } from "./tracked-runtime-cache.js"; +import { + buildMergeReceiptEvidenceWarnings, + resolveMergeFenceHealthEvidence, +} from "./health-merge-evidence.js"; import { auditMergeDriverConfiguration, findGitWorkspaceRoot, @@ -737,28 +741,11 @@ function buildHierarchyIntegrityWarnings( ]; } -function buildMergeReceiptEvidenceWarnings(params: { - invalidEvidenceCount: number; - missingHistoryReferenceCount: number; -}): string[] { - const warnings: string[] = []; - if (params.invalidEvidenceCount > 0) { - warnings.push( - `merge_receipt_evidence_invalid:${params.invalidEvidenceCount}`, - ); - } - if (params.missingHistoryReferenceCount > 0) { - warnings.push( - `merge_receipt_history_reference_missing:${params.missingHistoryReferenceCount}`, - ); - } - return warnings; -} - async function buildIntegrityCheck( pmRoot: string, typeToFolder: Record, schema: PmSettings["schema"], + settings: PmSettings, requireMergeDrivers: boolean, items: Array, ): Promise< @@ -778,6 +765,12 @@ async function buildIntegrityCheck( ); const trackedRuntimeCache = await scanTrackedRuntimeCache(pmRoot); const gitWorkspaceRoot = await findGitWorkspaceRoot(pmRoot); + const mergeFence = await resolveMergeFenceHealthEvidence( + pmRoot, + gitWorkspaceRoot, + settings, + requireMergeDrivers, + ); const mergeDriverAudit = gitWorkspaceRoot === null ? null @@ -847,6 +840,7 @@ async function buildIntegrityCheck( `merge_driver_configuration_${mergeDriverInstallationMissing ? "missing" : "drift"}:${mergeDriverAudit.missing_keys.length + mergeDriverAudit.drifted_keys.length}`, ] : []), + ...mergeFence.warnings, ...(pendingMergeDecisions.length > 0 ? [`merge_decisions_unreviewed:${pendingMergeDecisions.length}`] : []), @@ -892,6 +886,7 @@ async function buildIntegrityCheck( ? 0 : mergeDriverAudit.missing_keys.length + mergeDriverAudit.drifted_keys.length, + merge_fence: mergeFence.count, pending_merge_decisions: pendingMergeDecisions.length, lossless_merge_receipts: losslessMergeReceipts.length, invalid_merge_receipt_evidence: @@ -931,6 +926,7 @@ async function buildIntegrityCheck( ...mergeDriverAudit, required: requireMergeDrivers, }, + merge_fence: mergeFence.audit, pending_merge_decision_items: [ ...new Set(pendingMergeDecisions.map((receipt) => receipt.item_id)), ].sort((left, right) => left.localeCompare(right)), @@ -3468,6 +3464,7 @@ export async function runHealth( pmRoot, typeRegistry.type_to_folder, settings.schema, + settings, requireMergeDrivers, items, ); diff --git a/src/sdk/merge/install.ts b/src/sdk/merge/install.ts index 8de9d0220..1cda0d09c 100644 --- a/src/sdk/merge/install.ts +++ b/src/sdk/merge/install.ts @@ -405,20 +405,74 @@ export function buildMergeAttributePatterns( return patterns; } -function removeManagedBlock( - content: string, - startMarker: string, - endMarker: string, -): string { - const start = content.indexOf(startMarker); - const end = content.indexOf(endMarker); - return start >= 0 && end > start - ? `${content.slice(0, start)}${content.slice(end + endMarker.length)}` - : content.replace(startMarker, "").replace(endMarker, ""); +/** Locate every managed fence so one tracker can be updated without erasing its siblings. */ +function managedMergeBlocks(content: string): Array<{ + start: number; + end: number; + lines: string[]; + closed: boolean; +}> { + const blocks: Array<{ + start: number; + end: number; + lines: string[]; + closed: boolean; + }> = []; + let cursor = 0; + while (cursor < content.length) { + const legacyStart = content.indexOf(PM_GITATTRIBUTES_START, cursor); + const versionedStart = content.indexOf(PM_GITATTRIBUTES_V2_START, cursor); + const start = + legacyStart === -1 + ? versionedStart + : versionedStart === -1 + ? legacyStart + : Math.min(legacyStart, versionedStart); + if (start === -1) break; + const versioned = start === versionedStart; + const startMarker = versioned + ? PM_GITATTRIBUTES_V2_START + : PM_GITATTRIBUTES_START; + const endMarker = versioned + ? PM_GITATTRIBUTES_V2_END + : PM_GITATTRIBUTES_END; + const closing = content.indexOf(endMarker, start + startMarker.length); + const end = closing === -1 ? content.length : closing + endMarker.length; + blocks.push({ + start, + end, + closed: closing !== -1, + lines: content + .slice(start + startMarker.length, closing === -1 ? end : closing) + .split("\n") + .map((line) => line.trim()) + .filter(Boolean), + }); + cursor = end; + } + return blocks; +} + +/** Match the tracker's own prefix, including incomplete pre-versioned fences. */ +function blockBelongsToTracker( + lines: string[], + trackerRelativeRoot: string, +): boolean { + if (trackerRelativeRoot === "") { + return lines.some((line) => + line.startsWith(quoteGitAttributePattern("**/*.toon")), + ); + } + const prefix = `${trackerRelativeRoot}/`; + const quotedPrefix = quoteGitAttributePattern(prefix).slice(0, -1); + return lines.some( + (line) => line.startsWith(quotedPrefix) || line.startsWith(prefix), + ); } async function reconcileGitattributesBlock( workspaceRoot: string, + trackerRelativeRoot: string, patterns: string[], dryRun: boolean, ): Promise<{ path: string; changed: boolean }> { @@ -438,17 +492,32 @@ async function reconcileGitattributesBlock( ...patterns, PM_GITATTRIBUTES_V2_END, ].join("\n"); - const withoutManagedBlock = removeManagedBlock( - removeManagedBlock( - current, - PM_GITATTRIBUTES_V2_START, - PM_GITATTRIBUTES_V2_END, - ), - PM_GITATTRIBUTES_START, - PM_GITATTRIBUTES_END, + const blocks = managedMergeBlocks(current); + const owned = blocks.filter((candidate) => + blockBelongsToTracker(candidate.lines, trackerRelativeRoot), ); - const prefix = withoutManagedBlock.trimEnd(); - const next = `${prefix.length > 0 ? `${prefix}\n\n` : ""}${block}\n`; + // A marker-only legacy fence carries no owner. The default tracker can + // migrate it safely when it is the sole managed block in the repository. + if ( + owned.length === 0 && + trackerRelativeRoot === ".agents/pm" && + blocks.length === 1 && + blocks[0].lines.every((line) => !line.includes(".agents/pm/")) + ) { + owned.push(blocks[0]); + } + let next: string; + if (owned.length > 0) { + const first = owned[0]; + next = current; + for (const duplicate of owned.slice(1).reverse()) { + next = `${next.slice(0, duplicate.start)}${next.slice(duplicate.end)}`; + } + next = `${next.slice(0, first.start)}${block}${next.slice(first.end)}`; + } else { + const prefix = current.trimEnd(); + next = `${prefix.length > 0 ? `${prefix}\n\n` : ""}${block}\n`; + } if (next === current) { return { path: gitattributesPath, changed: false }; } @@ -528,23 +597,16 @@ export async function auditMergeAttributeFence( }; } const fencePath = path.join(fenceDirectory, ".gitattributes"); - const usesV2 = fenceContent.includes(PM_GITATTRIBUTES_V2_START); - const startMarker = usesV2 - ? PM_GITATTRIBUTES_V2_START - : PM_GITATTRIBUTES_START; - const endMarker = usesV2 ? PM_GITATTRIBUTES_V2_END : PM_GITATTRIBUTES_END; - const start = fenceContent.indexOf(startMarker); - const end = fenceContent.indexOf(endMarker); - const committed = - end > start - ? fenceContent - .slice(start + startMarker.length, end) - .split("\n") - .map((line) => line.trim()) - .filter((line) => line.length > 0) - : []; + const relativeRoot = toPosixRelative(fenceDirectory, resolvedRoot); + const blocks = managedMergeBlocks(fenceContent); + const owned = blocks.filter((block) => + blockBelongsToTracker(block.lines, relativeRoot), + ); + const committed = owned.flatMap((block) => + block.closed ? block.lines : [], + ); const expected = buildMergeAttributePatterns( - toPosixRelative(fenceDirectory, resolvedRoot), + relativeRoot, typeFolders, ); const committedSet = new Set(committed); @@ -552,7 +614,10 @@ export async function auditMergeAttributeFence( const missing = expected.filter((line) => !committedSet.has(line)); const stale = committed.filter((line) => !expectedSet.has(line)); return { - status: missing.length > 0 || stale.length > 0 ? "drift" : "ok", + status: + owned.length !== 1 || !owned[0].closed || missing.length > 0 || stale.length > 0 + ? "drift" + : "ok", path: fencePath, missing_patterns: missing, stale_patterns: stale, @@ -672,6 +737,7 @@ export async function refreshMergeAttributeFenceIfInstalled( const typeFolders = resolveProjectMergeTypeFolders(settings); const outcome = await reconcileGitattributesBlock( workspaceRoot, + trackerRelativeRoot, buildMergeAttributePatterns(trackerRelativeRoot, typeFolders), false, ); @@ -814,6 +880,7 @@ export async function installMergeFence(options: { // activated fence pointing at drivers that this clone has not installed. const gitattributes = await reconcileGitattributesBlock( canonicalWorkspaceRoot, + trackerRelativeRoot, patterns, dryRun, ); diff --git a/src/sdk/test/execution.ts b/src/sdk/test/execution.ts index beaad8deb..89472a1d2 100644 --- a/src/sdk/test/execution.ts +++ b/src/sdk/test/execution.ts @@ -2622,17 +2622,37 @@ function resolveLinkedTestSafetyPreflightResult(params: { : null; } -function resolveLinkedTestPreflightResult(params: { +/** Refuse unbound PM context when a linked command could reach the source tracker. */ +function resolveUnboundPmContextFailure(params: { + linkedTest: LinkedTest; + executionContext: NonNullable; +}): TestRunResult | null { + if (params.executionContext.pm_context_mode !== "none") return null; + if (params.executionContext.workspace_context_mode === "source") { + return buildLinkedTestAssertionFailureResult( + params.linkedTest, + params.executionContext, + "pm_context_mode=none requires workspace_context_mode=isolated or snapshot so implicit tracker discovery cannot reach the source workspace.", + ); + } + if (params.executionContext.is_pm_command) { + return buildLinkedTestAssertionFailureResult( + params.linkedTest, + params.executionContext, + "pm_context_mode=none is for self-isolating SDK tests; direct PM commands require schema, tracker, or auto context.", + ); + } + return null; +} + +/** Explain a seeded-tracker mismatch before a linked PM command runs. */ +function resolvePmContextMismatchFailure(params: { linkedTest: LinkedTest; executionContext: NonNullable; runLevelPmContextMode: LinkedTestPmContextMode; linkedOverridePmContextMode: LinkedTestPmContextMode | undefined; options: RunLinkedTestsOptions | undefined; }): TestRunResult | null { - const safetyFailure = resolveLinkedTestSafetyPreflightResult(params); - if (safetyFailure) return safetyFailure; - const trustFailure = resolveLinkedTestTrustPreflightResult(params); - if (trustFailure) return trustFailure; const failOnMismatchByDefault = params.executionContext.pm_context_mode === "schema" && params.executionContext.is_pm_tracker_read_command && @@ -2641,23 +2661,39 @@ function resolveLinkedTestPreflightResult(params: { params.options?.failOnContextMismatch === true && params.executionContext.is_pm_command && params.executionContext.mismatch_detected; - if (failOnMismatchByDefault || failOnMismatchByFlag) { - const mismatchHint = buildPmContextMismatchHint({ - executionContext: params.executionContext, - runLevelPmContextMode: params.runLevelPmContextMode, - linkedOverridePmContextMode: params.linkedOverridePmContextMode, - }); - const mismatchPrefix = - params.options?.checkContext === true - ? "Linked test preflight PM context mismatch detected" - : "Linked test PM context mismatch detected"; - return buildLinkedTestAssertionFailureResult( - params.linkedTest, - params.executionContext, - `${mismatchPrefix} (source_project_items=${params.executionContext.source_project_item_count}, ` + - `sandbox_project_items=${params.executionContext.sandbox_project_item_count}).${mismatchHint}`, - ); - } + if (!failOnMismatchByDefault && !failOnMismatchByFlag) return null; + const mismatchHint = buildPmContextMismatchHint({ + executionContext: params.executionContext, + runLevelPmContextMode: params.runLevelPmContextMode, + linkedOverridePmContextMode: params.linkedOverridePmContextMode, + }); + const mismatchPrefix = + params.options?.checkContext === true + ? "Linked test preflight PM context mismatch detected" + : "Linked test PM context mismatch detected"; + return buildLinkedTestAssertionFailureResult( + params.linkedTest, + params.executionContext, + `${mismatchPrefix} (source_project_items=${params.executionContext.source_project_item_count}, ` + + `sandbox_project_items=${params.executionContext.sandbox_project_item_count}).${mismatchHint}`, + ); +} + +function resolveLinkedTestPreflightResult(params: { + linkedTest: LinkedTest; + executionContext: NonNullable; + runLevelPmContextMode: LinkedTestPmContextMode; + linkedOverridePmContextMode: LinkedTestPmContextMode | undefined; + options: RunLinkedTestsOptions | undefined; +}): TestRunResult | null { + const safetyFailure = resolveLinkedTestSafetyPreflightResult(params); + if (safetyFailure) return safetyFailure; + const trustFailure = resolveLinkedTestTrustPreflightResult(params); + if (trustFailure) return trustFailure; + const unboundContextFailure = resolveUnboundPmContextFailure(params); + if (unboundContextFailure) return unboundContextFailure; + const mismatchFailure = resolvePmContextMismatchFailure(params); + if (mismatchFailure) return mismatchFailure; if ( params.options?.requireAssertionsForPm === true && params.executionContext.is_pm_command && @@ -2694,7 +2730,11 @@ function buildLinkedTestExecutionEnv(params: { applySharedHostSafeDefaults(executionEnv); } executionEnv.FORCE_COLOR = "0"; - executionEnv.PM_PATH = params.executionContext.sandbox_project_pm_path; + if (params.executionContext.pm_context_mode === "none") { + delete executionEnv.PM_PATH; + } else { + executionEnv.PM_PATH = params.executionContext.sandbox_project_pm_path; + } executionEnv.PM_GLOBAL_PATH = params.executionContext.sandbox_global_pm_path; if (params.executionContext.workspace_context_mode === "isolated") { delete executionEnv.PM_SOURCE_WORKSPACE_ROOT; diff --git a/src/sdk/test/parsers.ts b/src/sdk/test/parsers.ts index 4d740e308..a4875571e 100644 --- a/src/sdk/test/parsers.ts +++ b/src/sdk/test/parsers.ts @@ -46,6 +46,7 @@ export const LINKED_TEST_PM_CONTEXT_MODE_VALUES = [ "schema", "tracker", "auto", + "none", ] as const; /** Restricts linked test pm context mode values accepted by command, SDK, and storage contracts. */ export type LinkedTestPmContextMode = diff --git a/src/types.ts b/src/types.ts index 34f8acb44..f1ba2f18a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -351,7 +351,7 @@ export interface LinkedTest { /** Value that configures or reports timeout seconds for this contract. */ timeout_seconds?: number; /** Strategy used to control pm context behavior. */ - pm_context_mode?: "schema" | "tracker" | "auto"; + pm_context_mode?: "schema" | "tracker" | "auto" | "none"; /** Strategy used for source-workspace visibility and command working directory. */ workspace_context_mode?: "source" | "isolated" | "snapshot"; /** Immutable provenance captured when the linked command enters tracker data. */ @@ -804,9 +804,9 @@ export interface ItemTestRunExecution { /** Exact linked command selected for execution. */ command: string; /** Context mode requested before linked-test overrides or auto-remediation. */ - requested_pm_context_mode?: "schema" | "tracker" | "auto"; + requested_pm_context_mode?: "schema" | "tracker" | "auto" | "none"; /** Effective context mode used by the linked command. */ - pm_context_mode?: "schema" | "tracker" | "auto"; + pm_context_mode?: "schema" | "tracker" | "auto" | "none"; /** Effective source-workspace mode used by the linked command. */ workspace_context_mode?: "source" | "isolated" | "snapshot"; /** Trust classification applied before execution. */ diff --git a/tests/fixtures/contracts/full.json b/tests/fixtures/contracts/full.json index 6eecae034..74e1d2bf0 100644 --- a/tests/fixtures/contracts/full.json +++ b/tests/fixtures/contracts/full.json @@ -23167,6 +23167,40 @@ ], "stability": "provisional" }, + { + "aliases": [], + "canonical_code": "merge_fence_drift", + "class": "generic_failure", + "code": "merge_fence_drift", + "emitting_commands": [ + "*" + ], + "exit_code": 1, + "meaning": "Merge fence drift condition.", + "owned_states": [], + "recovery": "Inspect the structured error guidance and retry the suggested command.", + "sources": [ + "core/diagnostics/remediation.ts" + ], + "stability": "provisional" + }, + { + "aliases": [], + "canonical_code": "merge_fence_missing", + "class": "generic_failure", + "code": "merge_fence_missing", + "emitting_commands": [ + "*" + ], + "exit_code": 1, + "meaning": "Merge fence missing condition.", + "owned_states": [], + "recovery": "Inspect the structured error guidance and retry the suggested command.", + "sources": [ + "core/diagnostics/remediation.ts" + ], + "stability": "provisional" + }, { "aliases": [], "canonical_code": "merge_git_config_unwritable", @@ -40075,16 +40109,18 @@ "type": "string" }, "pmContext": { - "description": "PM linked-test context mode (schema keeps isolated tracker data; tracker seeds source tracker data; auto uses tracker for PM tracker-read linked commands).", + "description": "PM linked-test context mode (schema keeps isolated tracker data; tracker seeds source tracker data; auto uses tracker for PM tracker-read commands; none omits PM_PATH for self-isolating SDK tests in isolated or snapshot workspaces).", "enum": [ "schema", "tracker", - "auto" + "auto", + "none" ], "examples": [ "schema", "tracker", - "auto" + "auto", + "none" ], "type": "string" }, @@ -58747,16 +58783,18 @@ "type": "string" }, "pmContext": { - "description": "PM linked-test context mode (schema keeps isolated tracker data; tracker seeds source tracker data; auto uses tracker for PM tracker-read linked commands).", + "description": "PM linked-test context mode (schema keeps isolated tracker data; tracker seeds source tracker data; auto uses tracker for PM tracker-read commands; none omits PM_PATH for self-isolating SDK tests in isolated or snapshot workspaces).", "enum": [ "schema", "tracker", - "auto" + "auto", + "none" ], "examples": [ "schema", "tracker", - "auto" + "auto", + "none" ], "type": "string" }, @@ -58986,16 +59024,18 @@ "type": "string" }, "pmContext": { - "description": "PM linked-test context mode (schema keeps isolated tracker data; tracker seeds source tracker data; auto uses tracker for PM tracker-read linked commands).", + "description": "PM linked-test context mode (schema keeps isolated tracker data; tracker seeds source tracker data; auto uses tracker for PM tracker-read commands; none omits PM_PATH for self-isolating SDK tests in isolated or snapshot workspaces).", "enum": [ "schema", "tracker", - "auto" + "auto", + "none" ], "examples": [ "schema", "tracker", - "auto" + "auto", + "none" ], "type": "string" }, diff --git a/tests/integration/linked-test-context-trust.integration.spec.ts b/tests/integration/linked-test-context-trust.integration.spec.ts index 043b83a59..d40b758a5 100644 --- a/tests/integration/linked-test-context-trust.integration.spec.ts +++ b/tests/integration/linked-test-context-trust.integration.spec.ts @@ -266,4 +266,68 @@ describe("linked-test workspace and trust contracts", () => { }); }); }); + + it("allows SDK-style tests to manage their own PM roots only in isolated workspaces", async () => { + await withTempPmPath(async (context) => { + const id = createTestItemId(context, { + title: "self-isolating linked test", + createMode: "progressive", + }); + const command = + "node -e \"process.stdout.write(String(process.env.PM_PATH)+':'+String(Boolean(process.env.PM_GLOBAL_PATH)))\""; + const added = context.runCli( + [ + "test", + id, + "--add", + `command=${command},pm_context_mode=none,workspace_context_mode=isolated`, + "--json", + ], + { expectJson: true }, + ); + expect(added.code).toBe(0); + expect((added.json as TestEnvelope).tests[0]).toMatchObject({ + pm_context_mode: "none", + workspace_context_mode: "isolated", + }); + const isolated = context.runCli(["test", id, "--run", "--json"], { + expectJson: true, + }); + expect(isolated.code).toBe(0); + expect((isolated.json as TestEnvelope).run_results[0]).toMatchObject({ + status: "passed", + stdout: "undefined:true", + }); + + await overwriteTaskTests(context, id, [ + { command: "node --version", pm_context_mode: "none" }, + ]); + const source = context.runCli(["test", id, "--run", "--json"], { + expectJson: true, + }); + expect(source.code).toBe(5); + expect((source.json as TestEnvelope).run_results[0]).toMatchObject({ + status: "failed", + failure_category: "assertion_failure", + error: expect.stringContaining("workspace_context_mode"), + }); + + await overwriteTaskTests(context, id, [ + { + command: "pm --version", + pm_context_mode: "none", + workspace_context_mode: "isolated", + }, + ]); + const directPm = context.runCli(["test", id, "--run", "--json"], { + expectJson: true, + }); + expect(directPm.code).toBe(5); + expect((directPm.json as TestEnvelope).run_results[0]).toMatchObject({ + status: "failed", + failure_category: "assertion_failure", + error: expect.stringContaining("direct PM commands"), + }); + }); + }); }); diff --git a/tests/integration/merge-fence-version-skew.integration.spec.ts b/tests/integration/merge-fence-version-skew.integration.spec.ts index 1557978d6..5be6f7c8c 100644 --- a/tests/integration/merge-fence-version-skew.integration.spec.ts +++ b/tests/integration/merge-fence-version-skew.integration.spec.ts @@ -2,23 +2,107 @@ import { execFileSync } from "node:child_process"; import { readFile, writeFile } from "node:fs/promises"; import path from "node:path"; import { describe, expect, it } from "vitest"; +import { runInit } from "../../src/cli/commands/workspace/init.js"; +import { readSettings } from "../../src/core/store/settings.js"; +import { runHealth } from "../../src/sdk/governance/health.js"; import { + auditMergeAttributeFence, + buildMergeAttributePatterns, installMergeFence, PM_GITATTRIBUTES_END, PM_GITATTRIBUTES_START, PM_GITATTRIBUTES_V2_END, PM_GITATTRIBUTES_V2_START, + resolveProjectMergeTypeFolders, } from "../../src/sdk/merge/install.js"; import { withTempPmPath } from "../helpers/withTempPmPath.js"; describe("merge fence version-skew compatibility", () => { + it("keeps root and nested tracker mappings after either tracker is reinstalled", async () => { + await withTempPmPath(async ({ pmPath, tempRoot }) => { + execFileSync("git", ["init", "-q"], { cwd: tempRoot }); + await installMergeFence({ pmRoot: pmPath, workspaceRoot: tempRoot }); + const nestedPmPath = path.join(tempRoot, "nested", ".agents", "pm"); + await runInit( + undefined, + { path: nestedPmPath }, + { defaults: true, agentGuidance: "skip" }, + ); + expect( + execFileSync( + "git", + ["check-attr", "merge", "--", ".agents/pm/tasks/root.toon"], + { cwd: tempRoot, encoding: "utf8" }, + ), + ).toContain("merge: pm-item-toon"); + for (const tracker of [pmPath, nestedPmPath, pmPath, nestedPmPath]) { + await installMergeFence({ pmRoot: tracker, workspaceRoot: tempRoot }); + const relative = path.relative(tempRoot, tracker); + expect( + execFileSync( + "git", + ["check-attr", "merge", "--", `${relative}/tasks/example.toon`], + { cwd: tempRoot, encoding: "utf8" }, + ), + ).toContain("merge: pm-item-toon"); + } + for (const tracker of [pmPath, nestedPmPath]) { + expect( + ( + await auditMergeAttributeFence( + tracker, + resolveProjectMergeTypeFolders(await readSettings(tracker)), + ) + ).status, + ).toBe("ok"); + } + expect( + execFileSync("git", ["check-attr", "merge", "--", ".agents/pm/tasks/root.toon"], { + cwd: tempRoot, + encoding: "utf8", + }), + ).toContain("merge: pm-item-toon"); + const attributesPath = path.join(tempRoot, ".gitattributes"); + const attributes = await readFile(attributesPath, "utf8"); + const rootBlock = attributes.match( + /# pm-cli:merge-drivers:v2:start[\s\S]*?# pm-cli:merge-drivers:v2:end/, + )?.[0]; + expect(rootBlock).toBeDefined(); + await writeFile(attributesPath, `${attributes}\n${rootBlock}\n`); + await installMergeFence({ pmRoot: pmPath, workspaceRoot: tempRoot }); + expect( + (await readFile(attributesPath, "utf8")).match( + /# pm-cli:merge-drivers:v2:start/g, + ), + ).toHaveLength(2); + await writeFile( + attributesPath, + (await readFile(attributesPath, "utf8")).replace( + /# pm-cli:merge-drivers:v2:start[\s\S]*?# pm-cli:merge-drivers:v2:end\n?/, + "", + ), + ); + const health = await runHealth({ path: pmPath }, { strictExit: true, skipDrift: true, skipVectors: true }); + expect(health.ok).toBe(false); + expect(health.warnings).toContainEqual(expect.stringMatching(/^merge_fence_drift:/)); + }); + }); + it("migrates the legacy fence so older ordinary commands cannot rewrite it", async () => { await withTempPmPath(async ({ pmPath, tempRoot }) => { execFileSync("git", ["init", "-q"], { cwd: tempRoot }); const attributesPath = path.join(tempRoot, ".gitattributes"); await writeFile( attributesPath, - `${PM_GITATTRIBUTES_START}\nlegacy-pattern merge=pm-json\n${PM_GITATTRIBUTES_END}\n`, + [ + PM_GITATTRIBUTES_START, + '".agents/pm/tasks/*.toon" merge=pm-item-toon', + PM_GITATTRIBUTES_END, + PM_GITATTRIBUTES_V2_START, + '"nested/.agents/pm/tasks/*.toon" merge=pm-item-toon', + PM_GITATTRIBUTES_V2_END, + "", + ].join("\n"), ); await installMergeFence({ pmRoot: pmPath, @@ -29,6 +113,7 @@ describe("merge fence version-skew compatibility", () => { expect(current).toContain(PM_GITATTRIBUTES_V2_START); expect(current).toContain(PM_GITATTRIBUTES_V2_END); expect(current).not.toContain(PM_GITATTRIBUTES_START); + expect(current).toContain('"nested/.agents/pm/tasks/*.toon" merge=pm-item-toon'); // Releases through 2026.8.7 only recognize the legacy marker, so their // automatic refresh path now leaves the versioned contract unchanged. @@ -41,4 +126,63 @@ describe("merge fence version-skew compatibility", () => { ); }); }); + + it("audits a tracker mounted at the Git root", async () => { + await withTempPmPath(async ({ tempRoot }) => { + execFileSync("git", ["init", "-q"], { cwd: tempRoot }); + await writeFile( + path.join(tempRoot, ".gitattributes"), + [ + PM_GITATTRIBUTES_V2_START, + ...buildMergeAttributePatterns("", ["tasks"]), + PM_GITATTRIBUTES_V2_END, + PM_GITATTRIBUTES_V2_START, + ...buildMergeAttributePatterns("nested/.agents/pm", ["tasks"]), + PM_GITATTRIBUTES_V2_END, + "", + ].join("\n"), + ); + expect((await auditMergeAttributeFence(tempRoot, ["tasks"])).status).toBe("ok"); + expect( + execFileSync("git", ["check-attr", "merge", "--", "tasks/root.toon"], { + cwd: tempRoot, + encoding: "utf8", + }), + ).toContain("merge: pm-item-toon"); + }); + }); + + it("upgrades an ownerless legacy block but preserves a lone nested block", async () => { + await withTempPmPath(async ({ pmPath, tempRoot }) => { + execFileSync("git", ["init", "-q"], { cwd: tempRoot }); + const attributesPath = path.join(tempRoot, ".gitattributes"); + await writeFile( + attributesPath, + `${PM_GITATTRIBUTES_START}\nlegacy-pattern merge=pm-json\n${PM_GITATTRIBUTES_END}\n`, + ); + await installMergeFence({ pmRoot: pmPath, workspaceRoot: tempRoot }); + expect(await readFile(attributesPath, "utf8")).not.toContain( + PM_GITATTRIBUTES_START, + ); + + const nestedBlock = [ + PM_GITATTRIBUTES_V2_START, + '"nested/.agents/pm/tasks/*.toon" merge=pm-item-toon', + PM_GITATTRIBUTES_V2_END, + "", + ].join("\n"); + await writeFile(attributesPath, nestedBlock); + await installMergeFence({ pmRoot: pmPath, workspaceRoot: tempRoot }); + const current = await readFile(attributesPath, "utf8"); + expect(current).toContain(nestedBlock.trimEnd()); + expect(current.match(/# pm-cli:merge-drivers:v2:start/g)).toHaveLength(2); + expect( + execFileSync( + "git", + ["check-attr", "merge", "--", "nested/.agents/pm/tasks/item.toon"], + { cwd: tempRoot, encoding: "utf8" }, + ), + ).toContain("merge: pm-item-toon"); + }); + }); }); diff --git a/tests/unit/commands/test/linked-test-parsers.spec.ts b/tests/unit/commands/test/linked-test-parsers.spec.ts index 5b0b4624a..cb40ed0e7 100644 --- a/tests/unit/commands/test/linked-test-parsers.spec.ts +++ b/tests/unit/commands/test/linked-test-parsers.spec.ts @@ -28,6 +28,7 @@ describe("linked-test constants", () => { "schema", "tracker", "auto", + "none", ]); }); }); diff --git a/tests/unit/sdk/execution-diagnostics-primitives.spec.ts b/tests/unit/sdk/execution-diagnostics-primitives.spec.ts index 44d79c923..a853c3667 100644 --- a/tests/unit/sdk/execution-diagnostics-primitives.spec.ts +++ b/tests/unit/sdk/execution-diagnostics-primitives.spec.ts @@ -90,6 +90,7 @@ describe("SDK execution and diagnostics ownership", () => { "schema", "tracker", "auto", + "none", ]); expect(TELEMETRY_SUBCOMMANDS).toEqual([ "status", diff --git a/tests/unit/sdk/governance/health-verdict-authority.spec.ts b/tests/unit/sdk/governance/health-verdict-authority.spec.ts index 5a30ac4a7..e1532410a 100644 --- a/tests/unit/sdk/governance/health-verdict-authority.spec.ts +++ b/tests/unit/sdk/governance/health-verdict-authority.spec.ts @@ -83,7 +83,9 @@ describe("health invocation verdict authority", () => { }), ); expect(result.failed_because).toEqual( - strictExit ? ["merge_driver_configuration_missing:5"] : [], + strictExit + ? ["merge_driver_configuration_missing:5", "merge_fence_missing:1"] + : [], ); } } From 5927547c841bb11dc0e0faeeb98ada9d59e609b0 Mon Sep 17 00:00:00 2001 From: Stefan Preu Date: Sun, 27 Sep 2026 05:03:49 +0200 Subject: [PATCH 2/5] Preserve nested tracker merge fences under concurrent updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Match managed blocks to exact tracker patterns while retaining legacy direct type-folder drift diagnostics. Serialize shared .gitattributes reconciliation through a Git-directory lock and publish it atomically so simultaneous tracker refreshes cannot lose each other’s mappings. Cover ancestor and descendant trackers, extension exclusions, and concurrent refreshes. Record the review regression and complete verification in pm-kynkl8. --- .../pm/extensions/.managed-extensions.json | 2 +- .agents/pm/history/pm-kynkl8.jsonl | 4 + .agents/pm/issues/pm-kynkl8.toon | 22 +- src/sdk/merge/install.ts | 240 +++++++++++------- ...rge-fence-version-skew.integration.spec.ts | 123 ++++++++- 5 files changed, 286 insertions(+), 105 deletions(-) diff --git a/.agents/pm/extensions/.managed-extensions.json b/.agents/pm/extensions/.managed-extensions.json index b5104c18b..dd09b27f6 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-27T01:50:28.300Z", + "updated_at": "2026-09-27T03:03:08.367Z", "entries": [ { "name": "pm-changelog", diff --git a/.agents/pm/history/pm-kynkl8.jsonl b/.agents/pm/history/pm-kynkl8.jsonl index 398098fb0..6b258545d 100644 --- a/.agents/pm/history/pm-kynkl8.jsonl +++ b/.agents/pm/history/pm-kynkl8.jsonl @@ -19,3 +19,7 @@ {"hash_algorithm":"sha256","ts":"2026-09-27T01:49:16.847Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"close","patch":[{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T01:49:16.847Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/closed_at","value":"2026-09-27T01:49:16.819Z"},{"op":"add","path":"/metadata/completed_at","value":"2026-09-27T01:49:16.819Z"},{"op":"add","path":"/metadata/resolution","value":"Reconciled only the active tracker managed .gitattributes block, preserved sibling and mixed legacy blocks, repaired truncated block audit, and added strict-health remediation for missing or drifted fences."},{"op":"add","path":"/metadata/expected_result","value":"Root, nested, and root-mounted trackers retain field-aware Git attributes across init and reinstall; strict health rejects a lost active fence."},{"op":"add","path":"/metadata/actual_result","value":"Four focused merge integration cases and linked PM test passed; 9,371 repository tests passed with exact 100/100/100/100 coverage; packed disposable-project health passed; graph, record-integrity, docstring, lint, and mutation checks passed."},{"op":"add","path":"/metadata/close_reason","value":"Nested tracker merge fences are preserved and strict health detects missing active mappings."}],"before_hash":"0072adafb4f585185facaedfc43f4ec4702924012e6c87e799c4e8d60f54c75e","after_hash":"eda1cfda93326e064da95910d56527fa4f4e552c2850342d006d50613dc16cc2","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":"9b9d19449d92e99e290313d0cb9a2b295bd8de622e7adcaf9288abee5fd8a42f"} {"hash_algorithm":"sha256","ts":"2026-09-27T01:49:17.737Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]},"topic":{"value":"workset:pm-kynkl8+pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-kynkl8","claim:pm-t05d8d","lineage:pm-t05d8d","lineage:pm-ugqx","lineage:pm-doxj"]}},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T01:49:17.737Z"}],"before_hash":"eda1cfda93326e064da95910d56527fa4f4e552c2850342d006d50613dc16cc2","after_hash":"662e25dae87f02a0fe9aa5b697d4b8cb2a75190a356b7bdd706ec6bd5d5a18fe","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":"923655bc466ca009a37cd61e07e713d660292eb155c64e9166bde23aa7274050"} {"hash_algorithm":"sha256","ts":"2026-09-27T01:54:54.961Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-27T01:54:54.961Z"},{"op":"add","path":"/metadata/escape_class","value":"production_defect"},{"op":"add","path":"/metadata/gate_evidence","value":{"disposition":"gate_strengthened","owner":"pm-kynkl8","gate_id":"merge-fence-active-tracker-ownership","negative_control":"node scripts/run-tests.mjs test -- tests/integration/merge-fence-version-skew.integration.spec.ts against the pre-fix merge installer: nested init loses root merge=pm-item-toon and strict health passes","local_checks":["node scripts/run-tests.mjs test -- tests/integration/merge-fence-version-skew.integration.spec.ts","node scripts/run-tests.mjs coverage","pnpm quality:static"],"hosted_checks":["CI / Gates (static)","CI / Gates (coverage)"]}}],"before_hash":"662e25dae87f02a0fe9aa5b697d4b8cb2a75190a356b7bdd706ec6bd5d5a18fe","after_hash":"6c2deecea55f6034dc3214b8f7040e1679b17ddcfec3082008ef07bb6969355c","item_hash_version":3,"message":"Classify shipped nested-tracker defect and record gate-strengthening evidence","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"3517da240c9e34d734bff4a60394e7a1da32ab7e801e7367513dd696835bfb8f"} +{"hash_algorithm":"sha256","ts":"2026-09-27T02:40:30.447Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-27T02:40:30.447Z","author":"harness:codex","text":"PR #1322 review follow-up: CodeRabbit identified ancestor/descendant fence ownership and inferred concurrent shared-file write loss. A new integration regression first failed when parent reinstall erased a child tracker fence. Exact generated-pattern matching now preserves that child; legacy direct type-folder rows remain attributable for drift diagnostics. A separate Git-directory merge-attributes lock serializes shared .gitattributes read/modify/write across trackers, and the existing atomic file writer publishes the result. The focused merge/validate suite passed 122 tests, including concurrent refresh and child extension exclusion. The first full coverage attempt exposed two test contract mismatches and one 10.265 s performance ceiling under host load; the two contract failures are fixed and the performance case passed focused at 5.78 s. Full rerun pending."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T02:40:30.447Z"}],"before_hash":"6c2deecea55f6034dc3214b8f7040e1679b17ddcfec3082008ef07bb6969355c","after_hash":"5e73ae7af9c42edd217851d6e67811c7ae09c868d1bbc8ae77c40ad95fff608f","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":"fa2c420b6c569fcbf5470ccc63fc573606b21b2274e43a2be14a927943273f8d"} +{"hash_algorithm":"sha256","ts":"2026-09-27T02:40:47.732Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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/2","value":{"run_id":"test-local-muj7opjs-mdcpnq","kind":"test","status":"passed","started_at":"2026-09-27T02:40:35.690Z","finished_at":"2026-09-27T02:40:47.704Z","recorded_at":"2026-09-27T02:40:47.704Z","passed":1,"failed":0,"skipped":0,"executions":[{"command":"node scripts/run-tests.mjs test -- tests/integration/merge-fence-version-skew.integration.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-27T02:40:47.732Z"}],"before_hash":"5e73ae7af9c42edd217851d6e67811c7ae09c868d1bbc8ae77c40ad95fff608f","after_hash":"c6e8da01440969097c4cc42f456d4b6dd9a1a23befa636c8a4931a25110634d7","item_hash_version":3,"message":"Track test run summary (test-local-muj7opjs-mdcpnq)","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":"95080d98aa37d9b67ee700f2cdc9e4844dbaf228a3baaf930cd6fe73b8b4b376"} +{"hash_algorithm":"sha256","ts":"2026-09-27T03:03:27.480Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/6","value":{"created_at":"2026-09-27T03:03:27.480Z","author":"harness:codex","text":"PR #1322 review-fix verification complete: node scripts/run-tests.mjs test -- tests/unit/sdk/merge-safety.spec.ts tests/unit/commands/governance/validate-command.spec.ts tests/integration/merge-fence-version-skew.integration.spec.ts passed 122/122; linked pm test passed 6/6; pnpm test:coverage passed 748 files and 9,373 tests (2 Windows-only skipped) with exact 100/100/100/100 coverage (65,706 statements, 50,238 branches, 13,601 functions, 62,755 lines); pnpm build, pnpm typecheck, focused ESLint, and full pnpm quality:static passed, including CLI transport, graph composition, record integrity, docstring, and mutation gates (323 killed, 7 equivalent). pm-changelog 2026.9.25 --check reported unchanged. The new concurrent refresh and ancestor-child cases are part of the same merge-fence contract."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T03:03:27.480Z"}],"before_hash":"c6e8da01440969097c4cc42f456d4b6dd9a1a23befa636c8a4931a25110634d7","after_hash":"830ca97bd624b137dc9799f02ceadf3450f12e4b3242a04615318da9129a7630","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":"ed8797ede6555a94b05ad1b925e7038675e6b54192b291120e8e1f9b507de072"} +{"hash_algorithm":"sha256","ts":"2026-09-27T03:03:41.872Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"learning_add","patch":[{"op":"add","path":"/metadata/learnings/1","value":{"created_at":"2026-09-27T03:03:41.872Z","author":"harness:codex","text":"Correction to the earlier prefix-ownership note: a tracker can live inside another tracker root, so a broad prefix can claim and erase its descendant fence. Match exact generated lines, admit legacy direct type-folder rows for drift diagnostics, serialize shared .gitattributes edits through the Git directory, and publish the file atomically."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T03:03:41.872Z"}],"before_hash":"830ca97bd624b137dc9799f02ceadf3450f12e4b3242a04615318da9129a7630","after_hash":"77810d400f4af67b553e10b337dd9debdbfddba42a420a56c2e43480f9bec1e3","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":"c661d47c57cc574c53464baba88fcd020bc5f65af24e587fb451bad34fc7d615"} diff --git a/.agents/pm/issues/pm-kynkl8.toon b/.agents/pm/issues/pm-kynkl8.toon index 6477c7fc9..8d7823298 100644 --- a/.agents/pm/issues/pm-kynkl8.toon +++ b/.agents/pm/issues/pm-kynkl8.toon @@ -6,7 +6,7 @@ status: closed priority: 1 tags[4]: "area:init","area:merge",gh-1320,multi-tracker created_at: "2026-09-26T23:39:49.274Z" -updated_at: "2026-09-27T01:54:54.961Z" +updated_at: "2026-09-27T03:03:41.872Z" closed_at: "2026-09-27T01:49:16.819Z" completed_at: "2026-09-27T01:49:16.819Z" author: "harness:codex" @@ -21,14 +21,17 @@ dependencies[5]{id,kind,created_at,author,source_kind,author_source}: pm-t05d8d,discovered_from,"2026-09-27T00:30:21.598Z","harness:codex","cli:update:dep",detected pm-g5sx,implements,"2026-09-27T01:47:42.715Z","harness:codex","cli:update:dep",detected pm-l56d0o,verifies,"2026-09-27T01:47:42.715Z","harness:codex","cli:update:dep",detected -comments[5]{created_at,author,text}: +comments[7]{created_at,author,text}: "2026-09-26T23:39:49.274Z","harness:codex","created_at=now,text=Duplicate check 2026-09-27: all-status searches for GH-1320, nested init merge attributes, merge fence ownership, and tracker fence drift found closed pm-1w3ljt, pm-i4fx, pm-l56d0o, and pm-g5sx. They delivered initial fence installation, coverage, and version-skew handling but not preservation across nested trackers. Source report https://github.com/unbraind/pm-cli/issues/1320." "2026-09-27T00:32:56.876Z","harness:codex","Root cause: shared .gitattributes reconciliation removed the first managed block without checking tracker ownership, so a nested tracker install could erase the root tracker fence (and vice versa). The fix preserves sibling blocks, replaces only the active tracker block, collapses duplicate owned blocks, and makes strict health report missing/drifted active fences. Focused integration tests passed (2/2), including git check-attr and reinstall for both roots. GitHub #1320 carries the canonical PM link; tracker validation now reports zero orphaned paths." "2026-09-27T00:56:03.425Z","harness:codex","Full coverage run surfaced a real truncated-fence audit edge case: an unterminated managed block still contained expected patterns, so the audit marked drift but reported zero missing patterns. The parser now records whether each block closed, excludes unclosed content from committed mappings, and retains a drift verdict. Updated strict-health expectation; the focused merge and health suite now passes 25 tests." "2026-09-27T01:37:13.523Z","harness:codex","Final verification: pnpm test:coverage passed 748 test files / 9,371 tests with exact 100/100/100/100 coverage (65,708 statements; 50,236 branches; 13,602 functions; 62,756 lines), 2 Windows-only tests skipped on Linux. pm test pm-kynkl8 --run passed 4 integration cases after the final edits. The new cases cover mixed legacy/versioned fences, root-mounted trackers with siblings, ownerless legacy migration, and lone nested-block preservation. pnpm build, pnpm typecheck, ESLint, docstring/static scanner, docs-skills gate, and npm audit high passed. Packaged CLI smoke in a disposable Git project passed, including strict health and npx/bunx version commands." "2026-09-27T01:49:01.054Z","harness:codex",Graph-composition gate identified two prose-only references in this item’s duplicate-check history. Added typed verifies link to closed version-skew issue pm-l56d0o and implements link to closed merge-semantics feature pm-g5sx; graph-composition now passes with prose-edge gaps at its 1236 ceiling. Record-integrity assurance and the mandatory mutation gate also passed (323/330 mutants killed; seven classified equivalent). -learnings[1]{created_at,author,text}: + "2026-09-27T02:40:30.447Z","harness:codex","PR #1322 review follow-up: CodeRabbit identified ancestor/descendant fence ownership and inferred concurrent shared-file write loss. A new integration regression first failed when parent reinstall erased a child tracker fence. Exact generated-pattern matching now preserves that child; legacy direct type-folder rows remain attributable for drift diagnostics. A separate Git-directory merge-attributes lock serializes shared .gitattributes read/modify/write across trackers, and the existing atomic file writer publishes the result. The focused merge/validate suite passed 122 tests, including concurrent refresh and child extension exclusion. The first full coverage attempt exposed two test contract mismatches and one 10.265 s performance ceiling under host load; the two contract failures are fixed and the performance case passed focused at 5.78 s. Full rerun pending." + "2026-09-27T03:03:27.480Z","harness:codex","PR #1322 review-fix verification complete: node scripts/run-tests.mjs test -- tests/unit/sdk/merge-safety.spec.ts tests/unit/commands/governance/validate-command.spec.ts tests/integration/merge-fence-version-skew.integration.spec.ts passed 122/122; linked pm test passed 6/6; pnpm test:coverage passed 748 files and 9,373 tests (2 Windows-only skipped) with exact 100/100/100/100 coverage (65,706 statements, 50,238 branches, 13,601 functions, 62,755 lines); pnpm build, pnpm typecheck, focused ESLint, and full pnpm quality:static passed, including CLI transport, graph composition, record integrity, docstring, and mutation gates (323 killed, 7 equivalent). pm-changelog 2026.9.25 --check reported unchanged. The new concurrent refresh and ancestor-child cases are part of the same merge-fence contract." +learnings[2]{created_at,author,text}: "2026-09-27T00:40:54.856Z","harness:codex","A repository may host multiple PM roots sharing one .gitattributes. Merge-fence maintenance must identify ownership by tracker-relative pattern prefix, preserve sibling blocks, and audit the active tracker separately from clone-local Git driver configuration." + "2026-09-27T03:03:41.872Z","harness:codex","Correction to the earlier prefix-ownership note: a tracker can live inside another tracker root, so a broad prefix can claim and erase its descendant fence. Match exact generated lines, admit legacy direct type-folder rows for drift diagnostics, serialize shared .gitattributes edits through the Git directory, and publish the file atomically." files[10]{path,scope}: src/core/diagnostics/remediation.ts,project src/sdk/generated/generated-error-code-catalog-part-1.ts,project @@ -42,7 +45,7 @@ files[10]{path,scope}: tests/unit/sdk/merge-safety.spec.ts,project tests[1]{command,scope,timeout_seconds,provenance{author,created_at,source_kind,source_ref}}: node scripts/run-tests.mjs test -- tests/integration/merge-fence-version-skew.integration.spec.ts,project,2400,"harness:codex","2026-09-27T00:31:51.396Z",local_mutation,fix/isolated-project-context-and-merge-fences -test_runs[2]: +test_runs[3]: - run_id: test-local-muj33ain-jamd0w kind: test status: passed @@ -65,6 +68,17 @@ test_runs[2]: 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/merge-fence-version-skew.integration.spec.ts,schema,schema,source,local_source_ref + - run_id: test-local-muj7opjs-mdcpnq + kind: test + status: passed + started_at: "2026-09-27T02:40:35.690Z" + finished_at: "2026-09-27T02:40:47.704Z" + recorded_at: "2026-09-27T02:40:47.704Z" + 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/merge-fence-version-skew.integration.spec.ts,schema,schema,source,local_source_ref docs[3]{path,scope}: CHANGELOG.md,project docs/generated/REFUSAL_CLOSURE_CENSUS.md,project diff --git a/src/sdk/merge/install.ts b/src/sdk/merge/install.ts index 1cda0d09c..c4583f9f9 100644 --- a/src/sdk/merge/install.ts +++ b/src/sdk/merge/install.ts @@ -8,8 +8,8 @@ * mutations refresh the fenced block automatically once it is installed. */ import { assertInitializedTracker } from "../environment/tracker-preflight.js"; -import { isFileMissingError } from "../../core/fs/fs-utils.js"; -import { access, readFile, realpath, writeFile } from "node:fs/promises"; +import { isFileMissingError, readFileIfExists, writeFileAtomic } from "../../core/fs/fs-utils.js"; +import { access, readFile, realpath } from "node:fs/promises"; import { constants } from "node:fs"; import { execFile } from "node:child_process"; import path from "node:path"; @@ -31,7 +31,10 @@ import { import { isPathOutsideRoot } from "../workspace.js"; import { resolveSourceContextWritePolicy } from "../environment/source-context.js"; -import { gitWorkspaceEnvironment, resolveMergeDriverConfigScope } from "./worktree-config.js"; +import { + gitWorkspaceEnvironment, + resolveMergeDriverConfigScope, +} from "./worktree-config.js"; import { discoverProjectRuntimeVersionPins } from "../environment/project-runtime-compatibility.js"; const execFileAsync = promisify(execFile); @@ -46,6 +49,7 @@ export const PM_GITATTRIBUTES_V2_START = "# pm-cli:merge-drivers:v2:start"; export const PM_GITATTRIBUTES_V2_END = "# pm-cli:merge-drivers:v2:end"; const MERGE_FENCE_LOCK_ID = "merge-fence"; +const MERGE_ATTRIBUTES_LOCK_ID = "merge-attributes"; /** * Quote one argument for the shell used by Git merge-driver configuration. @@ -235,7 +239,13 @@ export async function findGitWorkspaceRoot( const { stdout } = await execFileAsync( "git", ["rev-parse", "--show-toplevel"], - { cwd, env: gitWorkspaceEnvironment(), encoding: "utf8", windowsHide: true, timeout: 10_000 }, + { + cwd, + env: gitWorkspaceEnvironment(), + encoding: "utf8", + windowsHide: true, + timeout: 10_000, + }, ); return await realpath(stdout.trim()); } catch { @@ -286,15 +296,30 @@ function readSingleQuotedArgument( } /** Reject version-pin drift and sibling-worktree runtimes while accepting independent installations. */ -async function isMergeDriverWorkspaceCompatible(packageRoot: string, workspaceRoot: string, version: unknown): Promise { - const exactPins = discoverProjectRuntimeVersionPins(workspaceRoot).filter((pin) => pin.constraint === "exact"); +async function isMergeDriverWorkspaceCompatible( + packageRoot: string, + workspaceRoot: string, + version: unknown, +): Promise { + const exactPins = discoverProjectRuntimeVersionPins(workspaceRoot).filter( + (pin) => pin.constraint === "exact", + ); if (exactPins.some((pin) => pin.version !== version)) return false; const driverWorkspace = await findGitWorkspaceRoot(packageRoot); - if (driverWorkspace !== null && driverWorkspace !== await realpath(workspaceRoot)) { - const commonDirectories = await Promise.all([driverWorkspace, workspaceRoot].map(async (cwd) => { - const result = await execFileAsync("git", ["rev-parse", "--path-format=absolute", "--git-common-dir"], { cwd, env: gitWorkspaceEnvironment(), timeout: 10_000 }); - return realpath(result.stdout.trim()); - })); + if ( + driverWorkspace !== null && + driverWorkspace !== (await realpath(workspaceRoot)) + ) { + const commonDirectories = await Promise.all( + [driverWorkspace, workspaceRoot].map(async (cwd) => { + const result = await execFileAsync( + "git", + ["rev-parse", "--path-format=absolute", "--git-common-dir"], + { cwd, env: gitWorkspaceEnvironment(), timeout: 10_000 }, + ); + return realpath(result.stdout.trim()); + }), + ); if (commonDirectories[0] === commonDirectories[1]) return false; } return true; @@ -333,13 +358,22 @@ async function isPortableMergeDriverCommand( const packageRoot = path.dirname(path.dirname(cliArgument.value)); const manifest = JSON.parse( await readFile(path.join(packageRoot, "package.json"), "utf8"), - ) as { name?: unknown; bin?: string | Record | null; version?: unknown }; - const pmBin = typeof manifest.bin === "string" ? manifest.bin : manifest.bin?.pm; - if (!(await isMergeDriverWorkspaceCompatible(packageRoot, workspaceRoot, manifest.version))) return false; - return ( - manifest.name === "@unbrained/pm-cli" && - pmBin === "dist/cli.js" - ); + ) as { + name?: unknown; + bin?: string | Record | null; + version?: unknown; + }; + const pmBin = + typeof manifest.bin === "string" ? manifest.bin : manifest.bin?.pm; + if ( + !(await isMergeDriverWorkspaceCompatible( + packageRoot, + workspaceRoot, + manifest.version, + )) + ) + return false; + return manifest.name === "@unbrained/pm-cli" && pmBin === "dist/cli.js"; } catch { return false; } @@ -453,20 +487,22 @@ function managedMergeBlocks(content: string): Array<{ return blocks; } -/** Match the tracker's own prefix, including incomplete pre-versioned fences. */ +/** Match exact generated lines or a legacy direct type-folder row for this tracker. */ function blockBelongsToTracker( lines: string[], + patterns: string[], trackerRelativeRoot: string, ): boolean { - if (trackerRelativeRoot === "") { - return lines.some((line) => - line.startsWith(quoteGitAttributePattern("**/*.toon")), - ); - } - const prefix = `${trackerRelativeRoot}/`; - const quotedPrefix = quoteGitAttributePattern(prefix).slice(0, -1); - return lines.some( - (line) => line.startsWith(quotedPrefix) || line.startsWith(prefix), + const ownedPatterns = new Set(patterns); + const prefix = quoteGitAttributePattern( + trackerRelativeRoot.length > 0 ? `${trackerRelativeRoot}/` : "", + ).slice(0, -1); + return lines.some((line) => + ownedPatterns.has(line) || + (line.startsWith(prefix) && + /^[^/]+\/\*\.(?:toon|md)" merge=pm-item-(?:toon|markdown)$/u.test( + line.slice(prefix.length), + )), ); } @@ -477,54 +513,74 @@ async function reconcileGitattributesBlock( dryRun: boolean, ): Promise<{ path: string; changed: boolean }> { const gitattributesPath = path.join(workspaceRoot, ".gitattributes"); - let current = ""; + // Every tracker in this worktree writes the same file. Serialize the whole + // read/modify/write transaction through its Git directory, not a tracker lock. + const gitDirectory = dryRun + ? null + : ( + await execFileAsync("git", ["rev-parse", "--absolute-git-dir"], { + cwd: workspaceRoot, + env: gitWorkspaceEnvironment(), + encoding: "utf8", + windowsHide: true, + timeout: 10_000, + }) + ).stdout.trim(); + const releaseLock = + gitDirectory === null + ? null + : await acquireLock( + gitDirectory, + MERGE_ATTRIBUTES_LOCK_ID, + 120, + "merge-fence", + false, + true, + 5_000, + ); try { - current = await readFile(gitattributesPath, "utf8"); - } catch (error: unknown) { + const current = (await readFileIfExists(gitattributesPath)) ?? ""; + const block = [ + PM_GITATTRIBUTES_V2_START, + ...patterns, + PM_GITATTRIBUTES_V2_END, + ].join("\n"); + const blocks = managedMergeBlocks(current); + const owned = blocks.filter((candidate) => + blockBelongsToTracker(candidate.lines, patterns, trackerRelativeRoot), + ); + // A marker-only legacy fence carries no owner. The default tracker can + // migrate it safely when it is the sole managed block in the repository. if ( - !(error instanceof Error && "code" in error && isFileMissingError(error)) + owned.length === 0 && + trackerRelativeRoot === ".agents/pm" && + blocks.length === 1 && + blocks[0].lines.every((line) => !line.includes(".agents/pm/")) ) { - throw error; + owned.push(blocks[0]); } - } - const block = [ - PM_GITATTRIBUTES_V2_START, - ...patterns, - PM_GITATTRIBUTES_V2_END, - ].join("\n"); - const blocks = managedMergeBlocks(current); - const owned = blocks.filter((candidate) => - blockBelongsToTracker(candidate.lines, trackerRelativeRoot), - ); - // A marker-only legacy fence carries no owner. The default tracker can - // migrate it safely when it is the sole managed block in the repository. - if ( - owned.length === 0 && - trackerRelativeRoot === ".agents/pm" && - blocks.length === 1 && - blocks[0].lines.every((line) => !line.includes(".agents/pm/")) - ) { - owned.push(blocks[0]); - } - let next: string; - if (owned.length > 0) { - const first = owned[0]; - next = current; - for (const duplicate of owned.slice(1).reverse()) { - next = `${next.slice(0, duplicate.start)}${next.slice(duplicate.end)}`; + let next: string; + if (owned.length > 0) { + const first = owned[0]; + next = current; + for (const duplicate of owned.slice(1).reverse()) { + next = `${next.slice(0, duplicate.start)}${next.slice(duplicate.end)}`; + } + next = `${next.slice(0, first.start)}${block}${next.slice(first.end)}`; + } else { + const prefix = current.trimEnd(); + next = `${prefix.length > 0 ? `${prefix}\n\n` : ""}${block}\n`; } - next = `${next.slice(0, first.start)}${block}${next.slice(first.end)}`; - } else { - const prefix = current.trimEnd(); - next = `${prefix.length > 0 ? `${prefix}\n\n` : ""}${block}\n`; - } - if (next === current) { - return { path: gitattributesPath, changed: false }; - } - if (!dryRun) { - await writeFile(gitattributesPath, next, "utf8"); + if (next === current) { + return { path: gitattributesPath, changed: false }; + } + if (!dryRun) { + await writeFileAtomic(gitattributesPath, next); + } + return { path: gitattributesPath, changed: true }; + } finally { + await releaseLock?.(); } - return { path: gitattributesPath, changed: true }; } /** Result of comparing the committed merge-fence block against the coverage the active schema requires. */ @@ -599,23 +655,21 @@ export async function auditMergeAttributeFence( const fencePath = path.join(fenceDirectory, ".gitattributes"); const relativeRoot = toPosixRelative(fenceDirectory, resolvedRoot); const blocks = managedMergeBlocks(fenceContent); + const expected = buildMergeAttributePatterns(relativeRoot, typeFolders); const owned = blocks.filter((block) => - blockBelongsToTracker(block.lines, relativeRoot), - ); - const committed = owned.flatMap((block) => - block.closed ? block.lines : [], - ); - const expected = buildMergeAttributePatterns( - relativeRoot, - typeFolders, + blockBelongsToTracker(block.lines, expected, relativeRoot), ); + const committed = owned.flatMap((block) => (block.closed ? block.lines : [])); const committedSet = new Set(committed); const expectedSet = new Set(expected); const missing = expected.filter((line) => !committedSet.has(line)); const stale = committed.filter((line) => !expectedSet.has(line)); return { status: - owned.length !== 1 || !owned[0].closed || missing.length > 0 || stale.length > 0 + owned.length !== 1 || + !owned[0].closed || + missing.length > 0 || + stale.length > 0 ? "drift" : "ok", path: fencePath, @@ -635,20 +689,20 @@ export async function auditMergeDriverConfiguration( const key = `merge.${definition.key}.driver`; const expectedSuffix = ` merge driver ${definition.artifact} "%O" "%A" "%B"${definition.itemPath === undefined ? "" : ` --item-path ${definition.itemPath}`}`; try { - const { stdout } = await execFileAsync( - "git", - ["config", "--get", key], - { - cwd: workspaceRoot, - env: gitWorkspaceEnvironment(), - encoding: "utf8", - windowsHide: true, - timeout: 10_000, - }, - ); + const { stdout } = await execFileAsync("git", ["config", "--get", key], { + cwd: workspaceRoot, + env: gitWorkspaceEnvironment(), + encoding: "utf8", + windowsHide: true, + timeout: 10_000, + }); const configured = stdout.trim(); if ( - !(await isPortableMergeDriverCommand(configured, expectedSuffix, workspaceRoot)) + !(await isPortableMergeDriverCommand( + configured, + expectedSuffix, + workspaceRoot, + )) ) { driftedKeys.push(key); } @@ -845,7 +899,9 @@ export async function installMergeFence(options: { } if (!dryRun) { try { - const configScope = await resolveMergeDriverConfigScope(canonicalWorkspaceRoot); + const configScope = await resolveMergeDriverConfigScope( + canonicalWorkspaceRoot, + ); for (const entry of gitConfigEntries) { await execFileAsync( "git", diff --git a/tests/integration/merge-fence-version-skew.integration.spec.ts b/tests/integration/merge-fence-version-skew.integration.spec.ts index 5be6f7c8c..efd0318d6 100644 --- a/tests/integration/merge-fence-version-skew.integration.spec.ts +++ b/tests/integration/merge-fence-version-skew.integration.spec.ts @@ -13,11 +13,105 @@ import { PM_GITATTRIBUTES_START, PM_GITATTRIBUTES_V2_END, PM_GITATTRIBUTES_V2_START, + refreshMergeAttributeFenceIfInstalled, resolveProjectMergeTypeFolders, } from "../../src/sdk/merge/install.js"; import { withTempPmPath } from "../helpers/withTempPmPath.js"; describe("merge fence version-skew compatibility", () => { + it("keeps a child fence inside the parent tracker after reinstalling the parent", async () => { + await withTempPmPath(async ({ pmPath, tempRoot }) => { + execFileSync("git", ["init", "-q"], { cwd: tempRoot }); + await installMergeFence({ pmRoot: pmPath, workspaceRoot: tempRoot }); + const childPmPath = path.join( + pmPath, + "projects", + "child", + ".agents", + "pm", + ); + await runInit( + undefined, + { path: childPmPath }, + { defaults: true, agentGuidance: "skip" }, + ); + const childExtension = `${path.relative(tempRoot, childPmPath)}/extensions/package/settings.json`; + const attributesPath = path.join(tempRoot, ".gitattributes"); + expect( + execFileSync("git", ["check-attr", "merge", "--", childExtension], { + cwd: tempRoot, + encoding: "utf8", + }), + ).toContain("merge: unset"); + + await installMergeFence({ pmRoot: pmPath, workspaceRoot: tempRoot }); + expect( + (await readFile(attributesPath, "utf8")).match( + /# pm-cli:merge-drivers:v2:start/g, + ), + ).toHaveLength(2); + expect( + execFileSync("git", ["check-attr", "merge", "--", childExtension], { + cwd: tempRoot, + encoding: "utf8", + }), + ).toContain("merge: unset"); + for (const tracker of [pmPath, childPmPath]) { + expect( + ( + await auditMergeAttributeFence( + tracker, + resolveProjectMergeTypeFolders(await readSettings(tracker)), + ) + ).status, + ).toBe("ok"); + } + }); + }); + + it("retains both tracker fences when independent schema refreshes run concurrently", async () => { + await withTempPmPath(async ({ pmPath, tempRoot }) => { + execFileSync("git", ["init", "-q"], { cwd: tempRoot }); + await installMergeFence({ pmRoot: pmPath, workspaceRoot: tempRoot }); + const secondPmPath = path.join(tempRoot, "nested", ".agents", "pm"); + await runInit( + undefined, + { path: secondPmPath }, + { defaults: true, agentGuidance: "skip" }, + ); + const attributesPath = path.join(tempRoot, ".gitattributes"); + const current = await readFile(attributesPath, "utf8"); + await writeFile( + attributesPath, + current + .replaceAll('".agents/pm/tasks/*.toon" merge=pm-item-toon', "") + .replaceAll( + '"nested/.agents/pm/tasks/*.toon" merge=pm-item-toon', + "", + ), + ); + + const results = await Promise.all([ + refreshMergeAttributeFenceIfInstalled(pmPath), + refreshMergeAttributeFenceIfInstalled(secondPmPath), + ]); + expect(results.map((result) => result.status)).toEqual([ + "refreshed", + "refreshed", + ]); + for (const tracker of [pmPath, secondPmPath]) { + expect( + ( + await auditMergeAttributeFence( + tracker, + resolveProjectMergeTypeFolders(await readSettings(tracker)), + ) + ).status, + ).toBe("ok"); + } + }); + }); + it("keeps root and nested tracker mappings after either tracker is reinstalled", async () => { await withTempPmPath(async ({ pmPath, tempRoot }) => { execFileSync("git", ["init", "-q"], { cwd: tempRoot }); @@ -57,10 +151,14 @@ describe("merge fence version-skew compatibility", () => { ).toBe("ok"); } expect( - execFileSync("git", ["check-attr", "merge", "--", ".agents/pm/tasks/root.toon"], { - cwd: tempRoot, - encoding: "utf8", - }), + execFileSync( + "git", + ["check-attr", "merge", "--", ".agents/pm/tasks/root.toon"], + { + cwd: tempRoot, + encoding: "utf8", + }, + ), ).toContain("merge: pm-item-toon"); const attributesPath = path.join(tempRoot, ".gitattributes"); const attributes = await readFile(attributesPath, "utf8"); @@ -82,9 +180,14 @@ describe("merge fence version-skew compatibility", () => { "", ), ); - const health = await runHealth({ path: pmPath }, { strictExit: true, skipDrift: true, skipVectors: true }); + const health = await runHealth( + { path: pmPath }, + { strictExit: true, skipDrift: true, skipVectors: true }, + ); expect(health.ok).toBe(false); - expect(health.warnings).toContainEqual(expect.stringMatching(/^merge_fence_drift:/)); + expect(health.warnings).toContainEqual( + expect.stringMatching(/^merge_fence_drift:/), + ); }); }); @@ -113,7 +216,9 @@ describe("merge fence version-skew compatibility", () => { expect(current).toContain(PM_GITATTRIBUTES_V2_START); expect(current).toContain(PM_GITATTRIBUTES_V2_END); expect(current).not.toContain(PM_GITATTRIBUTES_START); - expect(current).toContain('"nested/.agents/pm/tasks/*.toon" merge=pm-item-toon'); + expect(current).toContain( + '"nested/.agents/pm/tasks/*.toon" merge=pm-item-toon', + ); // Releases through 2026.8.7 only recognize the legacy marker, so their // automatic refresh path now leaves the versioned contract unchanged. @@ -142,7 +247,9 @@ describe("merge fence version-skew compatibility", () => { "", ].join("\n"), ); - expect((await auditMergeAttributeFence(tempRoot, ["tasks"])).status).toBe("ok"); + expect((await auditMergeAttributeFence(tempRoot, ["tasks"])).status).toBe( + "ok", + ); expect( execFileSync("git", ["check-attr", "merge", "--", "tasks/root.toon"], { cwd: tempRoot, From 856baabcae3608c4420a58eaec078821b80d7f9b Mon Sep 17 00:00:00 2001 From: Stefan Preu Date: Sun, 27 Sep 2026 05:17:20 +0200 Subject: [PATCH 3/5] Regenerate changelog against the current release baseline The daily 2026.9.27 release commit advanced main during PR review. After merging that commit, regenerate the pm-changelog output on the combined PM history and record the CI reproduction and resolution in pm-kynkl8. --- .agents/pm/extensions/.managed-extensions.json | 2 +- .agents/pm/history/pm-kynkl8.jsonl | 1 + .agents/pm/issues/pm-kynkl8.toon | 5 +++-- CHANGELOG.md | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.agents/pm/extensions/.managed-extensions.json b/.agents/pm/extensions/.managed-extensions.json index dd09b27f6..9ee8f6883 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-27T03:03:08.367Z", + "updated_at": "2026-09-27T03:17:06.460Z", "entries": [ { "name": "pm-changelog", diff --git a/.agents/pm/history/pm-kynkl8.jsonl b/.agents/pm/history/pm-kynkl8.jsonl index 6b258545d..fcb77d63e 100644 --- a/.agents/pm/history/pm-kynkl8.jsonl +++ b/.agents/pm/history/pm-kynkl8.jsonl @@ -23,3 +23,4 @@ {"hash_algorithm":"sha256","ts":"2026-09-27T02:40:47.732Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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/2","value":{"run_id":"test-local-muj7opjs-mdcpnq","kind":"test","status":"passed","started_at":"2026-09-27T02:40:35.690Z","finished_at":"2026-09-27T02:40:47.704Z","recorded_at":"2026-09-27T02:40:47.704Z","passed":1,"failed":0,"skipped":0,"executions":[{"command":"node scripts/run-tests.mjs test -- tests/integration/merge-fence-version-skew.integration.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-27T02:40:47.732Z"}],"before_hash":"5e73ae7af9c42edd217851d6e67811c7ae09c868d1bbc8ae77c40ad95fff608f","after_hash":"c6e8da01440969097c4cc42f456d4b6dd9a1a23befa636c8a4931a25110634d7","item_hash_version":3,"message":"Track test run summary (test-local-muj7opjs-mdcpnq)","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":"95080d98aa37d9b67ee700f2cdc9e4844dbaf228a3baaf930cd6fe73b8b4b376"} {"hash_algorithm":"sha256","ts":"2026-09-27T03:03:27.480Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/6","value":{"created_at":"2026-09-27T03:03:27.480Z","author":"harness:codex","text":"PR #1322 review-fix verification complete: node scripts/run-tests.mjs test -- tests/unit/sdk/merge-safety.spec.ts tests/unit/commands/governance/validate-command.spec.ts tests/integration/merge-fence-version-skew.integration.spec.ts passed 122/122; linked pm test passed 6/6; pnpm test:coverage passed 748 files and 9,373 tests (2 Windows-only skipped) with exact 100/100/100/100 coverage (65,706 statements, 50,238 branches, 13,601 functions, 62,755 lines); pnpm build, pnpm typecheck, focused ESLint, and full pnpm quality:static passed, including CLI transport, graph composition, record integrity, docstring, and mutation gates (323 killed, 7 equivalent). pm-changelog 2026.9.25 --check reported unchanged. The new concurrent refresh and ancestor-child cases are part of the same merge-fence contract."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T03:03:27.480Z"}],"before_hash":"c6e8da01440969097c4cc42f456d4b6dd9a1a23befa636c8a4931a25110634d7","after_hash":"830ca97bd624b137dc9799f02ceadf3450f12e4b3242a04615318da9129a7630","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":"ed8797ede6555a94b05ad1b925e7038675e6b54192b291120e8e1f9b507de072"} {"hash_algorithm":"sha256","ts":"2026-09-27T03:03:41.872Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"learning_add","patch":[{"op":"add","path":"/metadata/learnings/1","value":{"created_at":"2026-09-27T03:03:41.872Z","author":"harness:codex","text":"Correction to the earlier prefix-ownership note: a tracker can live inside another tracker root, so a broad prefix can claim and erase its descendant fence. Match exact generated lines, admit legacy direct type-folder rows for drift diagnostics, serialize shared .gitattributes edits through the Git directory, and publish the file atomically."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T03:03:41.872Z"}],"before_hash":"830ca97bd624b137dc9799f02ceadf3450f12e4b3242a04615318da9129a7630","after_hash":"77810d400f4af67b553e10b337dd9debdbfddba42a420a56c2e43480f9bec1e3","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":"c661d47c57cc574c53464baba88fcd020bc5f65af24e587fb451bad34fc7d615"} +{"hash_algorithm":"sha256","ts":"2026-09-27T03:16:54.898Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/7","value":{"created_at":"2026-09-27T03:16:54.898Z","author":"harness:codex","text":"PR #1322 CI static gate failed because the 2026.9.27 daily release commit landed on main while this PR was running. A clean temporary merge preview reproduced the pm-changelog --check mismatch: the merged release heading said 2026.9.27 while latest pm-changelog 2026.9.25 generated Unreleased for this PR lineage. Merged current main into the PR branch without rewriting history, regenerated CHANGELOG.md through pm-changelog, and verified changelog:pm:check against the combined tree. No pm-changelog package code change was needed."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T03:16:54.898Z"}],"before_hash":"77810d400f4af67b553e10b337dd9debdbfddba42a420a56c2e43480f9bec1e3","after_hash":"2271fd4c3742b5148a85a403671af91a25b64c171ba8cddf7d092f8775fd5e75","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":"8062d694886f87b12c924c56965fcd3541a3e285d3cdc967222f8d23f4291a94"} diff --git a/.agents/pm/issues/pm-kynkl8.toon b/.agents/pm/issues/pm-kynkl8.toon index 8d7823298..0f67d7510 100644 --- a/.agents/pm/issues/pm-kynkl8.toon +++ b/.agents/pm/issues/pm-kynkl8.toon @@ -6,7 +6,7 @@ status: closed priority: 1 tags[4]: "area:init","area:merge",gh-1320,multi-tracker created_at: "2026-09-26T23:39:49.274Z" -updated_at: "2026-09-27T03:03:41.872Z" +updated_at: "2026-09-27T03:16:54.898Z" closed_at: "2026-09-27T01:49:16.819Z" completed_at: "2026-09-27T01:49:16.819Z" author: "harness:codex" @@ -21,7 +21,7 @@ dependencies[5]{id,kind,created_at,author,source_kind,author_source}: pm-t05d8d,discovered_from,"2026-09-27T00:30:21.598Z","harness:codex","cli:update:dep",detected pm-g5sx,implements,"2026-09-27T01:47:42.715Z","harness:codex","cli:update:dep",detected pm-l56d0o,verifies,"2026-09-27T01:47:42.715Z","harness:codex","cli:update:dep",detected -comments[7]{created_at,author,text}: +comments[8]{created_at,author,text}: "2026-09-26T23:39:49.274Z","harness:codex","created_at=now,text=Duplicate check 2026-09-27: all-status searches for GH-1320, nested init merge attributes, merge fence ownership, and tracker fence drift found closed pm-1w3ljt, pm-i4fx, pm-l56d0o, and pm-g5sx. They delivered initial fence installation, coverage, and version-skew handling but not preservation across nested trackers. Source report https://github.com/unbraind/pm-cli/issues/1320." "2026-09-27T00:32:56.876Z","harness:codex","Root cause: shared .gitattributes reconciliation removed the first managed block without checking tracker ownership, so a nested tracker install could erase the root tracker fence (and vice versa). The fix preserves sibling blocks, replaces only the active tracker block, collapses duplicate owned blocks, and makes strict health report missing/drifted active fences. Focused integration tests passed (2/2), including git check-attr and reinstall for both roots. GitHub #1320 carries the canonical PM link; tracker validation now reports zero orphaned paths." "2026-09-27T00:56:03.425Z","harness:codex","Full coverage run surfaced a real truncated-fence audit edge case: an unterminated managed block still contained expected patterns, so the audit marked drift but reported zero missing patterns. The parser now records whether each block closed, excludes unclosed content from committed mappings, and retains a drift verdict. Updated strict-health expectation; the focused merge and health suite now passes 25 tests." @@ -29,6 +29,7 @@ comments[7]{created_at,author,text}: "2026-09-27T01:49:01.054Z","harness:codex",Graph-composition gate identified two prose-only references in this item’s duplicate-check history. Added typed verifies link to closed version-skew issue pm-l56d0o and implements link to closed merge-semantics feature pm-g5sx; graph-composition now passes with prose-edge gaps at its 1236 ceiling. Record-integrity assurance and the mandatory mutation gate also passed (323/330 mutants killed; seven classified equivalent). "2026-09-27T02:40:30.447Z","harness:codex","PR #1322 review follow-up: CodeRabbit identified ancestor/descendant fence ownership and inferred concurrent shared-file write loss. A new integration regression first failed when parent reinstall erased a child tracker fence. Exact generated-pattern matching now preserves that child; legacy direct type-folder rows remain attributable for drift diagnostics. A separate Git-directory merge-attributes lock serializes shared .gitattributes read/modify/write across trackers, and the existing atomic file writer publishes the result. The focused merge/validate suite passed 122 tests, including concurrent refresh and child extension exclusion. The first full coverage attempt exposed two test contract mismatches and one 10.265 s performance ceiling under host load; the two contract failures are fixed and the performance case passed focused at 5.78 s. Full rerun pending." "2026-09-27T03:03:27.480Z","harness:codex","PR #1322 review-fix verification complete: node scripts/run-tests.mjs test -- tests/unit/sdk/merge-safety.spec.ts tests/unit/commands/governance/validate-command.spec.ts tests/integration/merge-fence-version-skew.integration.spec.ts passed 122/122; linked pm test passed 6/6; pnpm test:coverage passed 748 files and 9,373 tests (2 Windows-only skipped) with exact 100/100/100/100 coverage (65,706 statements, 50,238 branches, 13,601 functions, 62,755 lines); pnpm build, pnpm typecheck, focused ESLint, and full pnpm quality:static passed, including CLI transport, graph composition, record integrity, docstring, and mutation gates (323 killed, 7 equivalent). pm-changelog 2026.9.25 --check reported unchanged. The new concurrent refresh and ancestor-child cases are part of the same merge-fence contract." + "2026-09-27T03:16:54.898Z","harness:codex","PR #1322 CI static gate failed because the 2026.9.27 daily release commit landed on main while this PR was running. A clean temporary merge preview reproduced the pm-changelog --check mismatch: the merged release heading said 2026.9.27 while latest pm-changelog 2026.9.25 generated Unreleased for this PR lineage. Merged current main into the PR branch without rewriting history, regenerated CHANGELOG.md through pm-changelog, and verified changelog:pm:check against the combined tree. No pm-changelog package code change was needed." learnings[2]{created_at,author,text}: "2026-09-27T00:40:54.856Z","harness:codex","A repository may host multiple PM roots sharing one .gitattributes. Merge-fence maintenance must identify ownership by tracker-relative pattern prefix, preserve sibling blocks, and audit the active tracker separately from clone-local Git driver configuration." "2026-09-27T03:03:41.872Z","harness:codex","Correction to the earlier prefix-ownership note: a tracker can live inside another tracker root, so a broad prefix can claim and erase its descendant fence. Match exact generated lines, admit legacy direct type-folder rows for drift diagnostics, serialize shared .gitattributes edits through the Git directory, and publish the file atomically." diff --git a/CHANGELOG.md b/CHANGELOG.md index 94620ce18..316d5382f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 2026.9.27 - 2026-09-27 +## Unreleased ### Fixed From 42b6f8c66d2d4a5c98af351de24a7d5848fa97b2 Mon Sep 17 00:00:00 2001 From: Stefan Preu Date: Sun, 27 Sep 2026 05:29:47 +0200 Subject: [PATCH 4/5] Generate changelog with the published release tag inventory Fetch the existing v2026.9.27 tag before running pm-changelog so the PR changes remain Unreleased and earlier items stay under their release heading. Record the local versus CI tag parity finding in pm-kynkl8. --- .agents/pm/extensions/.managed-extensions.json | 2 +- .agents/pm/history/pm-kynkl8.jsonl | 1 + .agents/pm/issues/pm-kynkl8.toon | 5 +++-- CHANGELOG.md | 5 +++++ 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.agents/pm/extensions/.managed-extensions.json b/.agents/pm/extensions/.managed-extensions.json index 9ee8f6883..f97954091 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-27T03:17:06.460Z", + "updated_at": "2026-09-27T03:29:34.172Z", "entries": [ { "name": "pm-changelog", diff --git a/.agents/pm/history/pm-kynkl8.jsonl b/.agents/pm/history/pm-kynkl8.jsonl index fcb77d63e..1ea0f35eb 100644 --- a/.agents/pm/history/pm-kynkl8.jsonl +++ b/.agents/pm/history/pm-kynkl8.jsonl @@ -24,3 +24,4 @@ {"hash_algorithm":"sha256","ts":"2026-09-27T03:03:27.480Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/6","value":{"created_at":"2026-09-27T03:03:27.480Z","author":"harness:codex","text":"PR #1322 review-fix verification complete: node scripts/run-tests.mjs test -- tests/unit/sdk/merge-safety.spec.ts tests/unit/commands/governance/validate-command.spec.ts tests/integration/merge-fence-version-skew.integration.spec.ts passed 122/122; linked pm test passed 6/6; pnpm test:coverage passed 748 files and 9,373 tests (2 Windows-only skipped) with exact 100/100/100/100 coverage (65,706 statements, 50,238 branches, 13,601 functions, 62,755 lines); pnpm build, pnpm typecheck, focused ESLint, and full pnpm quality:static passed, including CLI transport, graph composition, record integrity, docstring, and mutation gates (323 killed, 7 equivalent). pm-changelog 2026.9.25 --check reported unchanged. The new concurrent refresh and ancestor-child cases are part of the same merge-fence contract."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T03:03:27.480Z"}],"before_hash":"c6e8da01440969097c4cc42f456d4b6dd9a1a23befa636c8a4931a25110634d7","after_hash":"830ca97bd624b137dc9799f02ceadf3450f12e4b3242a04615318da9129a7630","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":"ed8797ede6555a94b05ad1b925e7038675e6b54192b291120e8e1f9b507de072"} {"hash_algorithm":"sha256","ts":"2026-09-27T03:03:41.872Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"learning_add","patch":[{"op":"add","path":"/metadata/learnings/1","value":{"created_at":"2026-09-27T03:03:41.872Z","author":"harness:codex","text":"Correction to the earlier prefix-ownership note: a tracker can live inside another tracker root, so a broad prefix can claim and erase its descendant fence. Match exact generated lines, admit legacy direct type-folder rows for drift diagnostics, serialize shared .gitattributes edits through the Git directory, and publish the file atomically."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T03:03:41.872Z"}],"before_hash":"830ca97bd624b137dc9799f02ceadf3450f12e4b3242a04615318da9129a7630","after_hash":"77810d400f4af67b553e10b337dd9debdbfddba42a420a56c2e43480f9bec1e3","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":"c661d47c57cc574c53464baba88fcd020bc5f65af24e587fb451bad34fc7d615"} {"hash_algorithm":"sha256","ts":"2026-09-27T03:16:54.898Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/7","value":{"created_at":"2026-09-27T03:16:54.898Z","author":"harness:codex","text":"PR #1322 CI static gate failed because the 2026.9.27 daily release commit landed on main while this PR was running. A clean temporary merge preview reproduced the pm-changelog --check mismatch: the merged release heading said 2026.9.27 while latest pm-changelog 2026.9.25 generated Unreleased for this PR lineage. Merged current main into the PR branch without rewriting history, regenerated CHANGELOG.md through pm-changelog, and verified changelog:pm:check against the combined tree. No pm-changelog package code change was needed."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T03:16:54.898Z"}],"before_hash":"77810d400f4af67b553e10b337dd9debdbfddba42a420a56c2e43480f9bec1e3","after_hash":"2271fd4c3742b5148a85a403671af91a25b64c171ba8cddf7d092f8775fd5e75","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":"8062d694886f87b12c924c56965fcd3541a3e285d3cdc967222f8d23f4291a94"} +{"hash_algorithm":"sha256","ts":"2026-09-27T03:29:25.443Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/8","value":{"created_at":"2026-09-27T03:29:25.443Z","author":"harness:codex","text":"Correction to the previous CI note: the new main commit also has an existing remote v2026.9.27 tag. The local checkout had not fetched that tag, so pm-changelog placed earlier released items under Unreleased and local --check gave a false green for CI parity. After git fetch --tags origin, the check reproduced CI failure; pm-changelog 2026.9.25 regenerated the correct two sections: this PR items under Unreleased and prior items under 2026.9.27. The tagged local --check now passes. No tag was created or rewritten."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T03:29:25.443Z"}],"before_hash":"2271fd4c3742b5148a85a403671af91a25b64c171ba8cddf7d092f8775fd5e75","after_hash":"f8b98a4c3c457024b98b8674184a54ce8f41e8b179fbd3e8195119f2f7c3f221","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":"0e973a98201153a6da2435dca07c06d645352c17029d0267a91a495e4cc25a7b"} diff --git a/.agents/pm/issues/pm-kynkl8.toon b/.agents/pm/issues/pm-kynkl8.toon index 0f67d7510..7dab82026 100644 --- a/.agents/pm/issues/pm-kynkl8.toon +++ b/.agents/pm/issues/pm-kynkl8.toon @@ -6,7 +6,7 @@ status: closed priority: 1 tags[4]: "area:init","area:merge",gh-1320,multi-tracker created_at: "2026-09-26T23:39:49.274Z" -updated_at: "2026-09-27T03:16:54.898Z" +updated_at: "2026-09-27T03:29:25.443Z" closed_at: "2026-09-27T01:49:16.819Z" completed_at: "2026-09-27T01:49:16.819Z" author: "harness:codex" @@ -21,7 +21,7 @@ dependencies[5]{id,kind,created_at,author,source_kind,author_source}: pm-t05d8d,discovered_from,"2026-09-27T00:30:21.598Z","harness:codex","cli:update:dep",detected pm-g5sx,implements,"2026-09-27T01:47:42.715Z","harness:codex","cli:update:dep",detected pm-l56d0o,verifies,"2026-09-27T01:47:42.715Z","harness:codex","cli:update:dep",detected -comments[8]{created_at,author,text}: +comments[9]{created_at,author,text}: "2026-09-26T23:39:49.274Z","harness:codex","created_at=now,text=Duplicate check 2026-09-27: all-status searches for GH-1320, nested init merge attributes, merge fence ownership, and tracker fence drift found closed pm-1w3ljt, pm-i4fx, pm-l56d0o, and pm-g5sx. They delivered initial fence installation, coverage, and version-skew handling but not preservation across nested trackers. Source report https://github.com/unbraind/pm-cli/issues/1320." "2026-09-27T00:32:56.876Z","harness:codex","Root cause: shared .gitattributes reconciliation removed the first managed block without checking tracker ownership, so a nested tracker install could erase the root tracker fence (and vice versa). The fix preserves sibling blocks, replaces only the active tracker block, collapses duplicate owned blocks, and makes strict health report missing/drifted active fences. Focused integration tests passed (2/2), including git check-attr and reinstall for both roots. GitHub #1320 carries the canonical PM link; tracker validation now reports zero orphaned paths." "2026-09-27T00:56:03.425Z","harness:codex","Full coverage run surfaced a real truncated-fence audit edge case: an unterminated managed block still contained expected patterns, so the audit marked drift but reported zero missing patterns. The parser now records whether each block closed, excludes unclosed content from committed mappings, and retains a drift verdict. Updated strict-health expectation; the focused merge and health suite now passes 25 tests." @@ -30,6 +30,7 @@ comments[8]{created_at,author,text}: "2026-09-27T02:40:30.447Z","harness:codex","PR #1322 review follow-up: CodeRabbit identified ancestor/descendant fence ownership and inferred concurrent shared-file write loss. A new integration regression first failed when parent reinstall erased a child tracker fence. Exact generated-pattern matching now preserves that child; legacy direct type-folder rows remain attributable for drift diagnostics. A separate Git-directory merge-attributes lock serializes shared .gitattributes read/modify/write across trackers, and the existing atomic file writer publishes the result. The focused merge/validate suite passed 122 tests, including concurrent refresh and child extension exclusion. The first full coverage attempt exposed two test contract mismatches and one 10.265 s performance ceiling under host load; the two contract failures are fixed and the performance case passed focused at 5.78 s. Full rerun pending." "2026-09-27T03:03:27.480Z","harness:codex","PR #1322 review-fix verification complete: node scripts/run-tests.mjs test -- tests/unit/sdk/merge-safety.spec.ts tests/unit/commands/governance/validate-command.spec.ts tests/integration/merge-fence-version-skew.integration.spec.ts passed 122/122; linked pm test passed 6/6; pnpm test:coverage passed 748 files and 9,373 tests (2 Windows-only skipped) with exact 100/100/100/100 coverage (65,706 statements, 50,238 branches, 13,601 functions, 62,755 lines); pnpm build, pnpm typecheck, focused ESLint, and full pnpm quality:static passed, including CLI transport, graph composition, record integrity, docstring, and mutation gates (323 killed, 7 equivalent). pm-changelog 2026.9.25 --check reported unchanged. The new concurrent refresh and ancestor-child cases are part of the same merge-fence contract." "2026-09-27T03:16:54.898Z","harness:codex","PR #1322 CI static gate failed because the 2026.9.27 daily release commit landed on main while this PR was running. A clean temporary merge preview reproduced the pm-changelog --check mismatch: the merged release heading said 2026.9.27 while latest pm-changelog 2026.9.25 generated Unreleased for this PR lineage. Merged current main into the PR branch without rewriting history, regenerated CHANGELOG.md through pm-changelog, and verified changelog:pm:check against the combined tree. No pm-changelog package code change was needed." + "2026-09-27T03:29:25.443Z","harness:codex","Correction to the previous CI note: the new main commit also has an existing remote v2026.9.27 tag. The local checkout had not fetched that tag, so pm-changelog placed earlier released items under Unreleased and local --check gave a false green for CI parity. After git fetch --tags origin, the check reproduced CI failure; pm-changelog 2026.9.25 regenerated the correct two sections: this PR items under Unreleased and prior items under 2026.9.27. The tagged local --check now passes. No tag was created or rewritten." learnings[2]{created_at,author,text}: "2026-09-27T00:40:54.856Z","harness:codex","A repository may host multiple PM roots sharing one .gitattributes. Merge-fence maintenance must identify ownership by tracker-relative pattern prefix, preserve sibling blocks, and audit the active tracker separately from clone-local Git driver configuration." "2026-09-27T03:03:41.872Z","harness:codex","Correction to the earlier prefix-ownership note: a tracker can live inside another tracker root, so a broad prefix can claim and erase its descendant fence. Match exact generated lines, admit legacy direct type-folder rows for drift diagnostics, serialize shared .gitattributes edits through the Git directory, and publish the file atomically." diff --git a/CHANGELOG.md b/CHANGELOG.md index 316d5382f..1c3ced58f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ - Allow self-isolating linked package tests without inherited PM_PATH ([pm-t05d8d](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/issues/pm-t05d8d.toon)) - Preserve every tracker merge fence during nested init and detect lost active mappings ([pm-kynkl8](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/issues/pm-kynkl8.toon)) + +## 2026.9.27 - 2026-09-27 + +### Fixed + - SDK complete-list receipt falsely reports legacy aliases for canonical helper ([pm-vf9iaf](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/issues/pm-vf9iaf.toon)) ### Security From 17ea87ec32b100647201270bee0f9c9d51481f62 Mon Sep 17 00:00:00 2001 From: Stefan Preu Date: Sun, 27 Sep 2026 05:46:28 +0200 Subject: [PATCH 5/5] Show direct runner for unbound linked SDK tests Document that scripts/run-tests.mjs creates its own PM_PATH after the linked none-mode runner removes the inherited value. Use a direct Vitest command in the snapshot example and record temporary-clone verification plus merge-fence review disposition in the linked PM items. --- .agents/pm/extensions/.managed-extensions.json | 2 +- .agents/pm/history/pm-kynkl8.jsonl | 1 + .agents/pm/history/pm-t05d8d.jsonl | 2 ++ .agents/pm/issues/pm-kynkl8.toon | 5 +++-- .agents/pm/issues/pm-t05d8d.toon | 6 ++++-- docs/TESTING.md | 6 ++++-- 6 files changed, 15 insertions(+), 7 deletions(-) diff --git a/.agents/pm/extensions/.managed-extensions.json b/.agents/pm/extensions/.managed-extensions.json index f97954091..74730f052 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-27T03:29:34.172Z", + "updated_at": "2026-09-27T03:46:20.555Z", "entries": [ { "name": "pm-changelog", diff --git a/.agents/pm/history/pm-kynkl8.jsonl b/.agents/pm/history/pm-kynkl8.jsonl index 1ea0f35eb..428bbefcb 100644 --- a/.agents/pm/history/pm-kynkl8.jsonl +++ b/.agents/pm/history/pm-kynkl8.jsonl @@ -25,3 +25,4 @@ {"hash_algorithm":"sha256","ts":"2026-09-27T03:03:41.872Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"learning_add","patch":[{"op":"add","path":"/metadata/learnings/1","value":{"created_at":"2026-09-27T03:03:41.872Z","author":"harness:codex","text":"Correction to the earlier prefix-ownership note: a tracker can live inside another tracker root, so a broad prefix can claim and erase its descendant fence. Match exact generated lines, admit legacy direct type-folder rows for drift diagnostics, serialize shared .gitattributes edits through the Git directory, and publish the file atomically."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T03:03:41.872Z"}],"before_hash":"830ca97bd624b137dc9799f02ceadf3450f12e4b3242a04615318da9129a7630","after_hash":"77810d400f4af67b553e10b337dd9debdbfddba42a420a56c2e43480f9bec1e3","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":"c661d47c57cc574c53464baba88fcd020bc5f65af24e587fb451bad34fc7d615"} {"hash_algorithm":"sha256","ts":"2026-09-27T03:16:54.898Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/7","value":{"created_at":"2026-09-27T03:16:54.898Z","author":"harness:codex","text":"PR #1322 CI static gate failed because the 2026.9.27 daily release commit landed on main while this PR was running. A clean temporary merge preview reproduced the pm-changelog --check mismatch: the merged release heading said 2026.9.27 while latest pm-changelog 2026.9.25 generated Unreleased for this PR lineage. Merged current main into the PR branch without rewriting history, regenerated CHANGELOG.md through pm-changelog, and verified changelog:pm:check against the combined tree. No pm-changelog package code change was needed."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T03:16:54.898Z"}],"before_hash":"77810d400f4af67b553e10b337dd9debdbfddba42a420a56c2e43480f9bec1e3","after_hash":"2271fd4c3742b5148a85a403671af91a25b64c171ba8cddf7d092f8775fd5e75","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":"8062d694886f87b12c924c56965fcd3541a3e285d3cdc967222f8d23f4291a94"} {"hash_algorithm":"sha256","ts":"2026-09-27T03:29:25.443Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/8","value":{"created_at":"2026-09-27T03:29:25.443Z","author":"harness:codex","text":"Correction to the previous CI note: the new main commit also has an existing remote v2026.9.27 tag. The local checkout had not fetched that tag, so pm-changelog placed earlier released items under Unreleased and local --check gave a false green for CI parity. After git fetch --tags origin, the check reproduced CI failure; pm-changelog 2026.9.25 regenerated the correct two sections: this PR items under Unreleased and prior items under 2026.9.27. The tagged local --check now passes. No tag was created or rewritten."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T03:29:25.443Z"}],"before_hash":"2271fd4c3742b5148a85a403671af91a25b64c171ba8cddf7d092f8775fd5e75","after_hash":"f8b98a4c3c457024b98b8674184a54ce8f41e8b179fbd3e8195119f2f7c3f221","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":"0e973a98201153a6da2435dca07c06d645352c17029d0267a91a495e4cc25a7b"} +{"hash_algorithm":"sha256","ts":"2026-09-27T03:45:14.301Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/9","value":{"created_at":"2026-09-27T03:45:14.301Z","author":"harness:codex","text":"PR #1322 review disposition: CodeRabbit suggested returning not_installed when a sibling tracker has a fence but the active tracker has none. The existing audit intentionally returns drift in that case: expected patterns are missing from a managed .gitattributes file, so validate.ts emits validate_merge_fence_drift and strict health emits merge_fence_drift. The integration regression removes the active block while retaining a sibling and asserts the strict failure. Returning not_installed without changing validation would suppress this warning; changing the public status/diagnostic contract is unnecessary for this defect. Kept the tested drift behavior and explained it in the inline review thread."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T03:45:14.301Z"}],"before_hash":"f8b98a4c3c457024b98b8674184a54ce8f41e8b179fbd3e8195119f2f7c3f221","after_hash":"6c0944add041d88edbb356aad9179427ea6501a4112038259b29accac7185337","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":"78e80b72bc87dba13285b2033e031ee367532b410c7819fe2e3ca967afcd1cd5"} diff --git a/.agents/pm/history/pm-t05d8d.jsonl b/.agents/pm/history/pm-t05d8d.jsonl index d5926a206..ff8f44795 100644 --- a/.agents/pm/history/pm-t05d8d.jsonl +++ b/.agents/pm/history/pm-t05d8d.jsonl @@ -21,3 +21,5 @@ {"hash_algorithm":"sha256","ts":"2026-09-27T01:49:18.624Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-t05d8d","release:pm-kynkl8"]},"topic":{"value":"pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-t05d8d","release:pm-kynkl8"]}},"op":"close","patch":[{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T01:49:18.624Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/closed_at","value":"2026-09-27T01:49:18.597Z"},{"op":"add","path":"/metadata/completed_at","value":"2026-09-27T01:49:18.597Z"},{"op":"add","path":"/metadata/resolution","value":"Added explicit pm_context_mode=none across parser, SDK types, CLI/MCP contracts, help, completion, execution preflight, and docs; non-PM commands run only in isolated or snapshot workspaces, with PM_GLOBAL_PATH still sandboxed."},{"op":"add","path":"/metadata/expected_result","value":"Linked package acceptance creates independent SDK tracker roots without touching source data or weakening default context and trust rules."},{"op":"add","path":"/metadata/actual_result","value":"Three focused linked-context integration cases and linked PM test passed; packed-package snapshot acceptance created and read two independent roots while source remained clean; 9,371 repository tests passed with exact 100/100/100/100 coverage; npx and bunx resolved the packed CLI."},{"op":"add","path":"/metadata/close_reason","value":"Self-isolating SDK package tests run through linked PM tests without inherited PM_PATH."}],"before_hash":"2078be93d8c50b9d00b705e9e9513e428b7d4f9b4f57fc310dc49915f6c42bb9","after_hash":"e2464b46f22be27ab110a7b7414913889393b88c8b55f507aaf4414e7a59121d","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":"a571d92645c8fad6c7d0b6a2b076849d997f885bc2d926e104f502c7278ad1e8"} {"hash_algorithm":"sha256","ts":"2026-09-27T01:49:19.415Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-t05d8d","release:pm-kynkl8"]},"topic":{"value":"pm-t05d8d","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-t05d8d","release:pm-kynkl8"]}},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T01:49:19.415Z"}],"before_hash":"e2464b46f22be27ab110a7b7414913889393b88c8b55f507aaf4414e7a59121d","after_hash":"e7e637535e47742021206925e88f8be2e7a44eae773506a6c2d505209eb2d484","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":"c73f472674545475dbecff651b8020e4985d4aa839abffc9c77f52235f23b29e"} {"hash_algorithm":"sha256","ts":"2026-09-27T01:54:55.658Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-27T01:54:55.658Z"},{"op":"add","path":"/metadata/escape_class","value":"production_defect"},{"op":"add","path":"/metadata/gate_evidence","value":{"disposition":"gate_strengthened","owner":"pm-t05d8d","gate_id":"linked-test-independent-sdk-workspaces","negative_control":"node scripts/run-tests.mjs test -- tests/integration/linked-test-context-trust.integration.spec.ts against the pre-fix runner: isolated SDK workspace test inherits PM_PATH and rejects a second fresh init","local_checks":["node scripts/run-tests.mjs test -- tests/integration/linked-test-context-trust.integration.spec.ts","node scripts/run-tests.mjs coverage","pnpm quality:static"],"hosted_checks":["CI / Gates (static)","CI / Gates (coverage)"]}}],"before_hash":"e7e637535e47742021206925e88f8be2e7a44eae773506a6c2d505209eb2d484","after_hash":"1207850cec1fe795d68fc93dbbd41abf320f4450ddee7a938c41cbca090d9408","item_hash_version":3,"message":"Classify shipped linked-test context defect and record gate-strengthening evidence","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"bfbdb53a59fa1aa073bcba73728eacf42d8308b188a842b47f8034847917d30d"} +{"hash_algorithm":"sha256","ts":"2026-09-27T03:45:14.206Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-27T03:45:14.206Z","author":"harness:codex","text":"PR #1322 review follow-up: CodeRabbit found the none-mode documentation example used scripts/run-tests.mjs, which creates its own PM_PATH after the linked runner omits inherited PM_PATH. The example now invokes Vitest directly in snapshot mode and the Testing guide states why. pnpm build and pnpm quality:docs-skills passed. This changes guidance only; the SDK none-mode implementation and its coverage remain unchanged."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T03:45:14.206Z"}],"before_hash":"1207850cec1fe795d68fc93dbbd41abf320f4450ddee7a938c41cbca090d9408","after_hash":"09d4ae1408cc83eeb6bca9799676e91d8e78ae700c075013832e4d5ea33617ba","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":"232e0e77411060697844894e3c228bf0b5bf95de431f0c310c5549fbb291cb47"} +{"hash_algorithm":"sha256","ts":"2026-09-27T03:46:08.453Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"f9d60a3a144e8d9ec3f9b203","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-27T03:46:08.453Z","author":"harness:codex","text":"Direct-runner documentation proof: in a disposable clone with PM_PATH unset and a temporary PM_GLOBAL_PATH, pnpm exec vitest run tests/integration/linked-test-context-trust.integration.spec.ts passed 3/3 tests. This confirms the documented runner command does not need scripts/run-tests.mjs to build a new PM_PATH."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-27T03:46:08.453Z"}],"before_hash":"09d4ae1408cc83eeb6bca9799676e91d8e78ae700c075013832e4d5ea33617ba","after_hash":"04d794ef89fded8a7f0651a5137775f3e02e82027fecdad4265ef2751262acb4","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":"694dced91b8aaab6254112b616f99b3dd79251aa93c57cd9800a72054993bf0b"} diff --git a/.agents/pm/issues/pm-kynkl8.toon b/.agents/pm/issues/pm-kynkl8.toon index 7dab82026..6ab4be814 100644 --- a/.agents/pm/issues/pm-kynkl8.toon +++ b/.agents/pm/issues/pm-kynkl8.toon @@ -6,7 +6,7 @@ status: closed priority: 1 tags[4]: "area:init","area:merge",gh-1320,multi-tracker created_at: "2026-09-26T23:39:49.274Z" -updated_at: "2026-09-27T03:29:25.443Z" +updated_at: "2026-09-27T03:45:14.301Z" closed_at: "2026-09-27T01:49:16.819Z" completed_at: "2026-09-27T01:49:16.819Z" author: "harness:codex" @@ -21,7 +21,7 @@ dependencies[5]{id,kind,created_at,author,source_kind,author_source}: pm-t05d8d,discovered_from,"2026-09-27T00:30:21.598Z","harness:codex","cli:update:dep",detected pm-g5sx,implements,"2026-09-27T01:47:42.715Z","harness:codex","cli:update:dep",detected pm-l56d0o,verifies,"2026-09-27T01:47:42.715Z","harness:codex","cli:update:dep",detected -comments[9]{created_at,author,text}: +comments[10]{created_at,author,text}: "2026-09-26T23:39:49.274Z","harness:codex","created_at=now,text=Duplicate check 2026-09-27: all-status searches for GH-1320, nested init merge attributes, merge fence ownership, and tracker fence drift found closed pm-1w3ljt, pm-i4fx, pm-l56d0o, and pm-g5sx. They delivered initial fence installation, coverage, and version-skew handling but not preservation across nested trackers. Source report https://github.com/unbraind/pm-cli/issues/1320." "2026-09-27T00:32:56.876Z","harness:codex","Root cause: shared .gitattributes reconciliation removed the first managed block without checking tracker ownership, so a nested tracker install could erase the root tracker fence (and vice versa). The fix preserves sibling blocks, replaces only the active tracker block, collapses duplicate owned blocks, and makes strict health report missing/drifted active fences. Focused integration tests passed (2/2), including git check-attr and reinstall for both roots. GitHub #1320 carries the canonical PM link; tracker validation now reports zero orphaned paths." "2026-09-27T00:56:03.425Z","harness:codex","Full coverage run surfaced a real truncated-fence audit edge case: an unterminated managed block still contained expected patterns, so the audit marked drift but reported zero missing patterns. The parser now records whether each block closed, excludes unclosed content from committed mappings, and retains a drift verdict. Updated strict-health expectation; the focused merge and health suite now passes 25 tests." @@ -31,6 +31,7 @@ comments[9]{created_at,author,text}: "2026-09-27T03:03:27.480Z","harness:codex","PR #1322 review-fix verification complete: node scripts/run-tests.mjs test -- tests/unit/sdk/merge-safety.spec.ts tests/unit/commands/governance/validate-command.spec.ts tests/integration/merge-fence-version-skew.integration.spec.ts passed 122/122; linked pm test passed 6/6; pnpm test:coverage passed 748 files and 9,373 tests (2 Windows-only skipped) with exact 100/100/100/100 coverage (65,706 statements, 50,238 branches, 13,601 functions, 62,755 lines); pnpm build, pnpm typecheck, focused ESLint, and full pnpm quality:static passed, including CLI transport, graph composition, record integrity, docstring, and mutation gates (323 killed, 7 equivalent). pm-changelog 2026.9.25 --check reported unchanged. The new concurrent refresh and ancestor-child cases are part of the same merge-fence contract." "2026-09-27T03:16:54.898Z","harness:codex","PR #1322 CI static gate failed because the 2026.9.27 daily release commit landed on main while this PR was running. A clean temporary merge preview reproduced the pm-changelog --check mismatch: the merged release heading said 2026.9.27 while latest pm-changelog 2026.9.25 generated Unreleased for this PR lineage. Merged current main into the PR branch without rewriting history, regenerated CHANGELOG.md through pm-changelog, and verified changelog:pm:check against the combined tree. No pm-changelog package code change was needed." "2026-09-27T03:29:25.443Z","harness:codex","Correction to the previous CI note: the new main commit also has an existing remote v2026.9.27 tag. The local checkout had not fetched that tag, so pm-changelog placed earlier released items under Unreleased and local --check gave a false green for CI parity. After git fetch --tags origin, the check reproduced CI failure; pm-changelog 2026.9.25 regenerated the correct two sections: this PR items under Unreleased and prior items under 2026.9.27. The tagged local --check now passes. No tag was created or rewritten." + "2026-09-27T03:45:14.301Z","harness:codex","PR #1322 review disposition: CodeRabbit suggested returning not_installed when a sibling tracker has a fence but the active tracker has none. The existing audit intentionally returns drift in that case: expected patterns are missing from a managed .gitattributes file, so validate.ts emits validate_merge_fence_drift and strict health emits merge_fence_drift. The integration regression removes the active block while retaining a sibling and asserts the strict failure. Returning not_installed without changing validation would suppress this warning; changing the public status/diagnostic contract is unnecessary for this defect. Kept the tested drift behavior and explained it in the inline review thread." learnings[2]{created_at,author,text}: "2026-09-27T00:40:54.856Z","harness:codex","A repository may host multiple PM roots sharing one .gitattributes. Merge-fence maintenance must identify ownership by tracker-relative pattern prefix, preserve sibling blocks, and audit the active tracker separately from clone-local Git driver configuration." "2026-09-27T03:03:41.872Z","harness:codex","Correction to the earlier prefix-ownership note: a tracker can live inside another tracker root, so a broad prefix can claim and erase its descendant fence. Match exact generated lines, admit legacy direct type-folder rows for drift diagnostics, serialize shared .gitattributes edits through the Git directory, and publish the file atomically." diff --git a/.agents/pm/issues/pm-t05d8d.toon b/.agents/pm/issues/pm-t05d8d.toon index 36a4cede9..22340c9c5 100644 --- a/.agents/pm/issues/pm-t05d8d.toon +++ b/.agents/pm/issues/pm-t05d8d.toon @@ -6,7 +6,7 @@ status: closed priority: 1 tags[5]: "area:sdk","area:test",context-management,gh-1318,packages created_at: "2026-09-26T20:34:16.625Z" -updated_at: "2026-09-27T01:54:55.658Z" +updated_at: "2026-09-27T03:46:08.453Z" closed_at: "2026-09-27T01:49:18.597Z" completed_at: "2026-09-27T01:49:18.597Z" author: "harness:codex" @@ -20,11 +20,13 @@ dependencies[4]{id,kind,created_at,author,source_kind,author_source}: pm-alhqbz,verifies,"2026-09-26T20:34:16.625Z","harness:codex","cli:create:dep",detected pm-e97jyf,discovered_from,"2026-09-26T20:34:16.625Z","harness:codex","cli:create:dep",detected pm-6pij,verifies,"2026-09-26T22:18:49.388Z","harness:codex","cli:update:dep",detected -comments[4]{created_at,author,text}: +comments[6]{created_at,author,text}: "2026-09-26T20:34:16.625Z","harness:codex","Duplicate check 2026-09-26: all-status PM searches for GH-1318, inherited PM_PATH, self-isolating linked tests, and context mode found closed pm-e97jyf, pm-alhqbz, pm-954h0o, and pm-6pij. Those delivered source/tracker parity and nested-write protection but do not provide a no-inherited-PM_PATH mode. GitHub report: https://github.com/unbraind/pm-cli/issues/1318." "2026-09-27T00:32:57.827Z","harness:codex","Added opt-in pm_context_mode=none for isolated/snapshot linked tests that create their own SDK tracker roots. The preflight rejects source-workspace or direct PM commands, and execution clears inherited PM_PATH while retaining sandboxed PM_GLOBAL_PATH. Focused integration tests passed (3/3). A temporary Git project outside this repo ran a snapshot linked test importing the built PmClient SDK, created/read two independent tracker roots, and left the source project untouched. SDK public-surface snapshot was explicitly updated because extending the union changes public TypeScript signatures; existing modes keep their prior behavior. Full repo gates are running." "2026-09-27T00:56:04.231Z","harness:codex","First full coverage pass found two existing contract assertions listing only schema, tracker, and auto. Updated them to include the new none mode; focused parser and public SDK barrel tests pass. Full 100-percent coverage is rerunning." "2026-09-27T01:37:14.269Z","harness:codex","Final verification: pnpm test:coverage passed 748 test files / 9,371 tests with exact 100/100/100/100 coverage; pm test pm-t05d8d --run passed 3 focused integration cases. Packed this checkout, installed its tarball into a disposable project, linked a snapshot test with pm_context_mode=none, and verified the packaged SDK created/read two independent PM roots while source project stayed clean; strict project health returned ok=true, warning_count=0. npx and bunx both resolved the packed CLI at 2026.9.26. Full static gate is the remaining delivery check." + "2026-09-27T03:45:14.206Z","harness:codex","PR #1322 review follow-up: CodeRabbit found the none-mode documentation example used scripts/run-tests.mjs, which creates its own PM_PATH after the linked runner omits inherited PM_PATH. The example now invokes Vitest directly in snapshot mode and the Testing guide states why. pnpm build and pnpm quality:docs-skills passed. This changes guidance only; the SDK none-mode implementation and its coverage remain unchanged." + "2026-09-27T03:46:08.453Z","harness:codex","Direct-runner documentation proof: in a disposable clone with PM_PATH unset and a temporary PM_GLOBAL_PATH, pnpm exec vitest run tests/integration/linked-test-context-trust.integration.spec.ts passed 3/3 tests. This confirms the documented runner command does not need scripts/run-tests.mjs to build a new PM_PATH." notes[1]{created_at,author,text}: "2026-09-26T20:39:23.664Z","harness:codex","Investigation 2026-09-26: linked-test runner currently injects sandbox PM_PATH for every child; parser, item serialization, import normalization, SDK/CLI help, and execution receipts all model schema|tracker|auto. A no-inherited-PM_PATH mode must refuse source working-directory execution: nested pm commands would otherwise rediscover the real source tracker. Safer design is an explicit mode paired with a disposable snapshot or isolated working directory; snapshot retains package files and binds .agents/pm to a sandbox tracker while explicit SDK init cwd can select a new temp tracker. Preserve protected env keys and clone trust checks. No source implementation or acceptance proof yet." learnings[1]{created_at,author,text}: diff --git a/docs/TESTING.md b/docs/TESTING.md index f598f4a16..033277d2b 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -15,7 +15,9 @@ Tracked implementation updates: [pm-52eh](../.agents/pm/features/pm-52eh.toon), in disposable workspaces. It applies stored-command provenance checks before process creation. Direct runners are accepted; prefer `node scripts/run-tests.mjs ...` when the repository provides it because the - wrapper also reproduces build, coverage, and cleanup policy. + wrapper also reproduces build, coverage, and cleanup policy. Use a direct + runner when a `none`-mode test must observe an unset `PM_PATH`; the wrapper + creates its own sandboxed `PM_PATH`. - Run linked tests before closing the item that owns the work. Tracked documentation work: [pm-u9d0](../.agents/pm/epics/pm-u9d0.toon). @@ -601,7 +603,7 @@ Use explicit modes when needed: pm test --run --pm-context schema pm test --run --pm-context tracker pm test --run --pm-context auto --check-context --auto-pm-context -pm test --add-json '{"command":"node scripts/run-tests.mjs test -- tests/integration/independent-sdk-workspaces.spec.ts","pm_context_mode":"none","workspace_context_mode":"snapshot"}' +pm test --add-json '{"command":"pnpm exec vitest run tests/integration/linked-test-context-trust.integration.spec.ts","pm_context_mode":"none","workspace_context_mode":"snapshot"}' ``` For complex linked-test commands, prefer JSON input so shell syntax survives unchanged: