diff --git a/.agents/pm/features/pm-github-c093.toon b/.agents/pm/features/pm-github-c093.toon new file mode 100644 index 00000000..d847dcc4 --- /dev/null +++ b/.agents/pm/features/pm-github-c093.toon @@ -0,0 +1,66 @@ +id: pm-github-c093 +title: "Preserve imported GitHub lifetimes, closure evidence and release attribution while linking cross-provider items" +description: "Repair the import root cause behind twelve Sourcery findings across six gated sync PRs. Preserve source timestamps through supported SDK metadata, map closure reasons and evidence, select tag-date release windows, report state conflicts, and link existing provider owners before creating items. Verify with recorded API fixtures and real temporary trackers at unchanged release gates." +type: Feature +status: in_progress +priority: 2 +tags[4]: changelog-unreleased,fidelity,github,import +created_at: "2026-10-16T22:29:39.865Z" +updated_at: "2026-10-17T13:40:47.560Z" +author: codex-agent +comments[35]{created_at,author,text}: + "2026-10-16T22:30:04.918Z",codex-agent,"Read all twelve unresolved Sourcery threads and the changed tracker/changelog files in the six consumer sync PRs. Scope: fix import only; preserve append-only history and leave unrelated closeout work untouched. The pinned SDK exposes completedAt but reserves created_at, updated_at and closed_at; source timestamp metadata plus an explicit SDK-gap receipt is required unless a supported import path proves otherwise. No upstream SDK issue will be filed under the queue rules." + "2026-10-16T22:35:10.689Z",codex-agent,"Found public SDK import primitives beyond high-level create/update: item locks, canonical document serialization, before/after history entries and appendHistoryEntry. Implementing an idempotent finalization event with real native dates and source metadata, without modifying any historical record. Existing provider owners receive provenance links only; their content, native lifecycle, release and export identity remain authoritative. Local Git calendar tag dates select the first release at or after source closure; later closures remain pending. GraphQL ClosedEvent closer provides actual closing PR attribution where REST commit_id is null." + "2026-10-16T23:09:24.995Z",codex-agent,"SDK candidate: bulk compensation captures native updated_at and passes it to restore as a history timestamp. Backdating that field disconnects the rollback version from its event. Preserve source creation/closure/completion natively for momentum and cycle time, and retain the exact source update date in github_updated_at while native updated_at stays the SDK mutation clock. Lifecycle finalization now runs at the SDK committing transition before the journal commits, with real compensation acceptance and a post-recovery idempotent check. No historical entries are rewritten." + "2026-10-16T23:31:39.064Z",codex-agent,"Verified the SDK candidate with a real temporary tracker and public commitImportedItem/commitItemMutations calls: a historical native updated_at followed by an update and deliberate committing failure produces AggregateError, compensation_failed=true, original_restored=false. The importer retains the exact source update timestamp in github_updated_at while keeping native updated_at aligned with immutable event time. Real importer rollback acceptance restores the existing source lifecycle and deletes the newly created item. Focused corrections passed 7/7; final linked tests and unchanged full release gates are running." + "2026-10-17T00:07:53.005Z",codex-agent,"The source-date, provider-link, privacy-plan and invalid-lifetime targeted checks pass 9/9 after certified SDK corpus reads replaced repeated CLI reads. A real tracker exposed the remaining canceled-to-completed transition: the SDK requires explicit repeat-closure authorization. Import reconciliation uses that supported transition with source closure evidence, and both modes now exercise canceled, completed and reopened states. The earlier release-gate probe had obsolete fixture assumptions and was stopped; final gates will run after these corrections." + "2026-10-17T00:13:15.552Z",codex-agent,"Terminal reconciliation checks pass 3/3: canceled source records become completed when GitHub does, and reopen clears terminal evidence, in both import modes. Expanded provider matching also follows generic url/external_links when a provider id or tag declares ownership. Final release gates are being restarted against this complete implementation; no coverage thresholds, exemptions or dependency pins changed." + "2026-10-17T00:22:28.429Z",codex-agent,"Behavioral revert proof at implementation 5999329: scratch copies retained the real recorded-API and temporary-tracker tests, rebuilding before each independent function control. Omitting persistImportFidelity yields 9 expected failures across source lifetime fixtures and state-conflict scenarios. Restoring generic importCloseReason wording yields 2 expected failures (beads and slack-standup); dropping linked closure evidence yields 1 (brief closing PR). Tag-only matchImportedIssue yields 2 expected failures (plain and atomic provider owner linking). Clearing buildImportFidelity release attribution yields 1 expected failure in the two-tag before/between/after fixture. Total: 15 expected behavioral failures, zero passes, skips or cancellations; no SDK mocks. Positive targeted checks passed 9/9, terminal transitions 3/3 and expanded identity checks 2/2. Full unchanged release gates remain in progress." + "2026-10-17T00:27:33.274Z",codex-agent,"The broad gate exposed a real adapter defect for callers passing a repository root: readSettings used the unresolved path, and finalization could append history using a noncanonical item id. Atomic planning now resolves the tracker first; finalization normalizes the id before locking, writing history and invoking hooks. The existing full gate/strict-health fixture and an explicit shortened-id finalization assertion cover this. Updated legacy HTTP replay fixtures to serve closure timelines, and the recovered-journal assertion now expects the earlier explicit no-push refusal. The broad run is diagnostic while these corrections are verified; final release gates will be rerun against frozen source." + "2026-10-17T00:33:40.746Z",codex-agent,"Canonical-id finalization, both repaired HTTP closure fixtures and recovered-journal refusal pass their targeted assertions. The clean strict-health/push/no-op flow now asserts the correct durable-journal recovery counts and passes; the concurrent two-process race also passes with five distinct comments, keeping the production lock wait and every deduplication assertion unchanged. Refused source cancellation and refused provider-link tests pass 2/2 with real tracker writes, recorded HTTP fixtures and CLI fault boundaries; neither can pass the gate or create a duplicate. Pinned typecheck and test compilation pass. Strict health passes with an isolated global registry; default local health sees a pre-existing shared-registry host-version skew. No dependency or registry changes made. Final PM-linked fixtures and unchanged full release gates are now running against frozen source." + "2026-10-17T00:39:36.185Z",codex-agent,"Verified all six consumer caller files on their default branches: both reusable workflow SHA and explicit package version remain pinned to 2026.10.16. A new release alone cannot change those pins. Enriched existing adoption task pm-github-aiok rather than creating a duplicate; thread replies will state that the next scheduled sync regenerates the PR after the fixed release is adopted. Consumer branches and workflows remain outside this task." + "2026-10-17T00:47:09.760Z",codex-agent,"Final PM-linked fixture run at implementation 3d4c1bf passes 26/26 with zero failures, skips, cancellations or TODOs. The command was node_modules/.bin/pm test pm-github-c093 --run --progress, running node --test test/import-fidelity.test.ts on real temporary trackers. Isolated V8 receipts measure import-fidelity.ts at exact 100 percent statements, branches, functions and lines. This is module-scoped evidence; package-wide release coverage remains pending. Project policy disables durable test-result capture, so this sanitized comment records the result without changing that policy." + "2026-10-17T00:50:57.587Z",codex-agent,"Refreshed every behavioral revert control against implementation 3d4c1bf after canonical tracker/id normalization. Each scratch package rebuilt with only the relevant function behavior reverted: lifetimes 9 expected failures, closure wording 2, closing-PR evidence 1, provider identity 2, release attribution 1. All 15 fail for their behavioral assertions, with zero passes, skips or cancellations. The positive 26-case fixture run and module-scoped 100/100/100/100 receipt use the same implementation. Package-wide npm/Bun gates are separate and remain pending." + "2026-10-17T01:27:07.433Z",codex-agent,"A stronger creation/state-cycle regression exposed completed-journal reuse after state changes. Transaction identity now binds to public immutable history: external edits and earlier different native states invalidate cached completion, while create staging and compensation preserve retry identity. Real SDK checks pass 8/8, including a local reopen, interrupted governed reconciliation, resume, compensation and exact no-op retry. Identity/evidence/history-shape checks pass 4/4: unset provider fields and copied GitHub body/labels cannot change known GitHub ownership; declared nested provider origins and exporter provenance still link existing owners. Closing commit attribution respects the API commit repository URL. Final fixture suite now contains 29 tests; full unchanged gates remain pending." + "2026-10-17T01:36:22.951Z",codex-agent,"Expanded final PM-linked suite passes 29/29, zero failures, skips, cancellations or TODOs. Fresh V8 receipts again measure import-fidelity.ts at exact 100/100/100/100, including history context selection, direct canceled creation, external local edits, real interrupted reconciliation/resume, nested provider origins, empty-field ownership stability and cross-repository closing commit URLs. Typecheck, test compilation, ESLint, docstrings and zero source duplication pass. The full package npm/Bun gates will now run from the frozen implementation; no thresholds, inventory exclusions, dependency pins or test-result policy changed." + "2026-10-17T01:42:21.617Z",codex-agent,"Function-only revert proof refreshed against implementation 21b17f3: lifetimes 9 expected failures; closure wording 2; closing-PR evidence 1; provider identity 2; release windows 1; legacy cancellation planning 1; external history revision 1; repeated lifecycle context 1. Total 18 expected failures, zero passes/skips/cancellations. Every package rebuilt first; all 18 failures contain ERR_ASSERTION and no startup/module/syntax failures. The added controls use real SDK trackers to expose stale completed journals and missing cancellation reasons. Positive linked suite passes 29/29; full package gates are running without source changes." + "2026-10-17T02:06:25.333Z",codex-agent,"The broad gate found one outdated HTTP fixture: the executable clean-publish replay returned issue arrays for closure timeline/GraphQL requests. Updated githubHandler to serve empty closure connections and supplied a valid completed closure date. The focused executable workflow now passes 1/1 through both fresh checkout runs, proving review creation and update. Production source is unchanged from 21b17f3. The running broad pass is collecting remaining coverage feedback before the final npm/Bun reruns." + "2026-10-17T02:22:19.497Z",codex-agent,"The complete broad pass measures all 14 authored modules at exact 100/100/100/100: 10403/10403 lines and statements, 271/271 functions, 3122/3122 branches. It ran 769 tests: 768 pass, one outdated closure HTTP fixture failed, zero skips/cancellations/TODOs. The repaired executable clean-publish fixture passes 1/1 through both fresh checkouts. This was not a passing release gate. Re-running the literal full npm CI-identity gate and then the Bun release gate with the fixture correction; production source remains 21b17f3." + "2026-10-17T02:58:38.811Z",codex-agent,"The full CI-identity npm run release:check PASSED at unchanged thresholds: 769/769 tests, zero failures/skips/cancellations/TODOs, exact 100/100/100/100 across all 14 authored modules. Production dependency audit, package dry-run packing, changelog check and publish attestation passed. Source is still 21b17f3 with the workflow fixture repair in 6cb1a1a. The separate literal bun run release:check is now running sequentially with isolated Git identity and unchanged policy." + "2026-10-17T03:46:54.138Z",codex-agent,"Both required full release gates PASSED under isolated CI Git identity: npm run release:check and bun run release:check each ran 769/769 tests with zero failures, skips, cancellations or TODOs. Both measured exact 100/100/100/100 across all 14 authored modules at unchanged thresholds: 10403/10403 lines and statements, 271/271 functions, 3122/3122 branches. Audit, package dry-run packing, generated changelog and publish attestation passed. Production source remains 21b17f3; workflow HTTP fixture correction is 6cb1a1a. Preparing the public PR, then checking its CI before replying to the 12 unresolved consumer Sourcery threads." + "2026-10-17T03:48:36.222Z",codex-agent,"Opened https://github.com/unbraind/pm-github/pull/142 from feat/import-fidelity after both full release gates passed. The pre-push privacy gate audited 4950 Git objects with approved identities and zero secret or host-path matches; strict health with required merge drivers in a fresh SDK context and validation passed. Monitoring PR checks before posting replies to unresolved consumer review threads; no consumer branch changes are authorized or included." + "2026-10-17T03:50:29.571Z",codex-agent,"PR 142 CodeQL reported incomplete URL substring sanitization in a test assertion that searched serialized preview JSON for a fixture PR URL. Replaced that assertion with equality against the exact github_close_reason field, including the full source date and closing PR URL. This strengthens the regression instead of suppressing the alert; production source is unchanged. Running both affected modes, test compilation and lint before pushing the check fix." + "2026-10-17T03:51:34.174Z",codex-agent,"The exact closure-plan assertion passes both atomic modes: node --test --test-name-pattern=\"gated closure plans\" test/import-fidelity.test.ts, 2/2 with no failures/skips/cancellations/TODOs. npm run build:test and npm run lint pass. This addresses the CodeQL test-only URL-substring finding without suppressions or production changes; the full npm/Bun 769/769 gate receipts remain applicable to identical production source." + "2026-10-17T04:19:20.812Z",codex-agent,"PR 142 is green at 667bd91: Node 22 and Node 26 each passed 785/785 coverage tests plus 785/785 native Bun tests, with zero failures/skips/cancellations/TODOs and exact 100/100/100/100 across all 14 authored modules. CI includes 16 additional baseline closeout tests from the independently merged PR 140; this branch does not modify that work. CodeQL and Semgrep passed. Source is unchanged from the locally verified 769/769 npm and Bun release gates. Reviewer services were budget-limited, expired or skipped, so these checks are execution evidence rather than review approval. CI: https://github.com/unbraind/pm-github/actions/runs/39000473004" + "2026-10-17T04:19:22.891Z",codex-agent,"Replied with plain REST replies to all 12 unresolved Sourcery threads across the six consumer sync PRs, linking PR 142 and explaining the fixed-release adoption prerequisite for the next scheduled regeneration. Verified all consumer heads and open states unchanged. Sourcery automatically resolved the threads after the replies; restored all 12 to unresolved to honor the requested state. No consumer files, branches or PR contents were edited. Reply evidence: https://github.com/unbraind/pm-beads/pull/145#discussion_r4301321723, https://github.com/unbraind/pm-beads/pull/145#discussion_r4301321859, https://github.com/unbraind/pm-brief/pull/154#discussion_r4301321968, https://github.com/unbraind/pm-brief/pull/154#discussion_r4301322089, https://github.com/unbraind/pm-context/pull/147#discussion_r4301322194, https://github.com/unbraind/pm-csv/pull/163#discussion_r4301322300, https://github.com/unbraind/pm-csv/pull/163#discussion_r4301322433, https://github.com/unbraind/pm-csv/pull/163#discussion_r4301322574, https://github.com/unbraind/pm-linear/pull/150#discussion_r4301322688, https://github.com/unbraind/pm-linear/pull/150#discussion_r4301322817, https://github.com/unbraind/pm-slack-standup/pull/127#discussion_r4301323269, https://github.com/unbraind/pm-slack-standup/pull/127#discussion_r4301323414" + "2026-10-17T10:31:32.545Z",codex-agent,"Round 1: verified all five unresolved review findings on a45661c. Plan: restrict identity fields, refresh URL-only GitHub owners, select latest closure evidence with bounded forward skew, exercise plain partial writes and empty gated selection, prove behavioral reverts, then run unchanged npm and Bun release gates." + "2026-10-17T10:37:14.234Z",codex-agent,"Behavioral revert proof: restoring only matchImportedIssue from a45661c fails identity excludes descriptions even when multiple items mention another issue and identity classifies URL-only GitHub owners for lifecycle refresh (one expected assertion failure per control). Restoring only importCloseReason fails the forward-skew non-null closer assertion; restoring only fetchImportTimeline fails the GraphQL suppression/request-count assertion. All four controls exit 1 on behavioral assertions; fixed functions restored. The linked closure regression passes through pm test --run --only-last --progress. No SDK mocks or threshold changes." + "2026-10-17T10:39:59.987Z",codex-agent,"Round 1 focused integration validation passes 9/9: env HOME=$(mktemp -d) GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_NOSYSTEM=1 node --test --test-name-pattern=\"description mentions|URL-only GitHub owner|ambiguous later|empty filtered\" test/import-fidelity.test.ts. Real trackers prove incidental descriptions remain unchanged, URL-only owners refresh native lifecycle/release, plain multi-issue ambiguity retains the earlier write, gated/atomic ambiguity writes nothing, and filtered-empty gated preview/apply omit plan. Both README qualifications match measured behavior. Strict health with required merge drivers passes under isolated HOME; the initial shared-home host/SDK version-skew warning is absent there. Full npm release gate running, Bun follows sequentially." + "2026-10-17T10:55:44.523Z",codex-agent,"The first npm release gate has reported an existing blocker-marker watchdog timeout: marker extraction finishes on repeated unclosed marker prefixes exceeded its 10-second subprocess budget, which includes package/SDK module startup under concurrent coverage. This is not a passing gate. Investigating in isolation without relaxing thresholds; the known watchdog-startup task pm-github-sah1 may cover this infrastructure issue. The complete focused fidelity run passed 38/38, and the two added identity unit cases passed separately." + "2026-10-17T10:59:20.262Z",codex-agent,"Stopped the known-failing first gate after recording the timeout; it is not counted as passing. A narrow test-only repair starts the same 10-second marker-parser deadline after child readiness, bounds cold startup separately at 60 seconds, and adds an intentional spin control requiring SIGKILL. Production blocker logic, consumer acceptance budgets, coverage thresholds and SDK pins remain unchanged. Related follow-up evidence added to existing pm-github-sah1 instead of duplicating that watchdog-startup issue. Verifying instrumented Node and native Bun probes before restarting both literal release gates." + "2026-10-17T10:59:59.257Z",codex-agent,"The readiness-based marker probe and forced-spin control pass 2/2 under instrumented Node (NODE_V8_COVERAGE with node --test --test-name-pattern=\"marker extraction|marker watchdog\" test/blockers.test.ts) and 2/2 under native Bun (bun test --timeout 600000 -t \"marker extraction|marker watchdog\" test/blockers.test.ts). The spin receives SIGKILL after readiness; the parser retains its original 10-second budget. Test compilation and lint pass. Restarting the complete npm release gate on installed Node 26 with isolated CI Git identity, then running the literal Bun release gate sequentially." + "2026-10-17T11:53:56.007Z",codex-agent,"The diagnostic npm run passed the repaired marker watchdog and all 40 fidelity cases, but found an older refused-provider-link fixture relying on a GitHub URL only inside the owner description. The fixture already declared Linear; it lacked an issue-body provider backlink. Updated only the fixture to use a declared Linear URL referenced by the issue body, preserving every refusal/no-write assertion. This is required by the new free-text ownership rule. Running both refused-link/cancellation controls under V8 coverage, while allowing the diagnostic package run to reveal any further failures before a clean full rerun. This diagnostic run is not counted as passing." + "2026-10-17T12:15:13.151Z",codex-agent,"Diagnostic npm run completed with 796/797 passing, zero skips/cancellations/TODOs, and exactly one failure: the old description-only provider-link fixture now corrected. Coverage was exact 10438/10438 lines/statements and 273/273 functions, but 3154/3156 branches. Missing branches were the refused-provider-link path in index.ts and the undefined locator fallback in import-fidelity.ts. Both new closure-selection paths are covered. Added an undefined github_url adversarial case and retained all gate thresholds and source inventory. Verifying the two precise branch repairs before rerunning the full npm and Bun release commands." + "2026-10-17T12:18:26.459Z",codex-agent,"Focused branch repair proof passes 3/3 under c8: refused cancellation, refused provider linking, and identity edge cases with undefined github_url. V8 receipts show execution of the previously uncovered locator fallback and the refused-link throw at index.ts:3470. Test compilation and lint pass. No production logic changed after the four behavioral-revert controls; only provider-fixture identity and an absent-value case were corrected. Starting a clean full npm release:check with CI identity, then the independent Bun release:check at unchanged policy." + "2026-10-17T13:12:31.216Z",codex-agent,"Clean CI-identity npm run release:check PASSED on installed Node 26: 797/797 tests, zero failures/skips/cancellations/TODOs. Exact all-source coverage across 14 authored modules: 10438/10438 lines and statements, 273/273 functions, 3157/3157 branches. Type checks, docstrings, lint, zero duplication, privacy, production audit, package dry-run, changelog and publish attestation all passed. Starting the independent literal bun run release:check sequentially with fresh HOME and disabled global/system Git identity; policy, SDK pins and package lock remain unchanged." + "2026-10-17T13:40:46.635Z",codex-agent,"Round 1 implementation and local verification complete. Both literal CI-identity release gates PASSED: npm run release:check and bun run release:check each 797/797 tests, zero failures/skips/cancellations/TODOs, exact 100/100/100/100 across 14 authored modules (10438/10438 lines/statements, 273/273 functions, 3157/3157 branches). Audit found zero vulnerabilities; docstrings, lint, duplication, privacy, packing, changelog and attestation pass. Four function-only behavioral reverts produced the expected assertion failures. README partial-write and empty-plan qualifications are verified with real trackers. Marker startup probe is bounded separately while retaining its 10-second work deadline and a forced-spin control. Existing pm-github-sah1 remains open for its original acceptance-watchdog case. Releasing the coding claim for orchestrator verification; next actions are commit/push, replies to the five original review threads, and bounded CI monitoring. No items will be closed." +files[13]{path,scope}: + import-fidelity.ts,project + index.ts,project + test/atomic.test.ts,project + test/blockers.test.ts,project + test/coverage-edges.test.ts,project + test/coverage-remainder.test.ts,project + test/fixtures/import-fidelity/README.md,project + test/handler-failures.test.ts,project + test/import-errors.test.ts,project + test/import-fidelity.test.ts,project + test/import-gate.test.ts,project + test/import-lock.test.ts,project + test/import-selection.test.ts,project +tests[2]{command,scope,timeout_seconds,provenance{author,created_at,source_kind,source_ref}}: + node --test test/import-fidelity.test.ts,project,2400,codex-agent,"2026-10-16T23:09:21.738Z",local_mutation,feat/import-fidelity + "node --test --test-name-pattern=\"closure evidence accepts forward skew\" test/import-fidelity.test.ts",project,2400,codex-agent,"2026-10-17T10:34:46.689Z",local_mutation,feat/import-fidelity +docs[1]{path,scope}: + README.md,project +body: "" diff --git a/.agents/pm/history/pm-github-aiok.jsonl b/.agents/pm/history/pm-github-aiok.jsonl index 0ff91d03..ad4a2bdf 100644 --- a/.agents/pm/history/pm-github-aiok.jsonl +++ b/.agents/pm/history/pm-github-aiok.jsonl @@ -3,3 +3,4 @@ {"hash_algorithm":"sha256","ts":"2026-10-05T09:25:59.227Z","author":"codex-sol","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"0221c1e0bd861e4e03844581","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-05T09:25:59.227Z"},{"op":"add","path":"/metadata/files","value":[{"path":"docs/sync-workflow-caller.yml","scope":"project"}]}],"before_hash":"afd27b09ad086b7bc0339128cf19fecc80e654e03c3f8f44b06ee0d0ca5b9ae8","after_hash":"b5f06d9ff353748e0db7b1603a7082e45cc6d3931e045f83f39bfa9f4f38c13c","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":"b582f9f159f845f61c14bb7c6f626954807d6a764b5a15b48382001b8034ec0f"} {"hash_algorithm":"sha256","ts":"2026-10-05T09:26:00.035Z","author":"codex-sol","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"0221c1e0bd861e4e03844581","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/0/path","value":".github/workflows/pm-github-sync.yml"},{"op":"add","path":"/metadata/files/1","value":{"path":"docs/sync-workflow-caller.yml","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-05T09:26:00.035Z"}],"before_hash":"b5f06d9ff353748e0db7b1603a7082e45cc6d3931e045f83f39bfa9f4f38c13c","after_hash":"ace7fe3eed738ae79e924bb2dcd5d737884f92d56c645dd0bfd26feedbae41c0","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":"6bce5babc0899063bf4fadb1a8438d2f6d0e543952b1bc83240f32b20d96103d"} {"hash_algorithm":"sha256","ts":"2026-10-05T09:32:54.787Z","author":"codex-sol","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"0221c1e0bd861e4e03844581","agent_provenance":{"model":{"value":"gpt-6.1-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-10-05T09:32:54.787Z"},{"op":"add","path":"/metadata/dependencies","value":[{"id":"pm-github-f1at","kind":"related","created_at":"2026-10-05T09:32:54.728Z","author":"codex-sol","source_kind":"cli:update:dep","author_source":"detected"}]}],"before_hash":"ace7fe3eed738ae79e924bb2dcd5d737884f92d56c645dd0bfd26feedbae41c0","after_hash":"7c5c3f9c474ddf2c45bd4673bfc27442e51863889c3561690fe1400acc597e4a","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":"dbe2f513aac4d1acb373693cfdccf5517d680c1ffa049f1a9c29f2b7bc92e2a2"} +{"hash_algorithm":"sha256","ts":"2026-10-17T00:39:26.413Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/1","value":{"created_at":"2026-10-17T00:39:26.413Z","author":"codex-agent","text":"Additional release-adoption dependency from pm-github-c093: the default-branch pm-github-sync callers in pm-beads, pm-brief, pm-context, pm-csv, pm-linear and pm-slack-standup all pin workflow SHA 67a5e3e1514fddeeae29951da00513f06f7aa0ab and pm-github-version 2026.10.16. Regeneration with the import-fidelity fix requires adoption of its reviewed release in both pins; the next scheduled sync using that release can then regenerate the existing sync PRs. Keep rollout as separately authorized work; the import-fidelity task does not change or dispatch consumer workflows."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T00:39:26.413Z"}],"before_hash":"7c5c3f9c474ddf2c45bd4673bfc27442e51863889c3561690fe1400acc597e4a","after_hash":"2289a391f07aa6dd799e43bad0fb93ff1752ad59c4abfd35e7ae4d3859913499","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":"9598a2a9ffde09a64f21c9f04f85bde4843dcf72c2bd1ebdb71b9903362be32f"} diff --git a/.agents/pm/history/pm-github-c093.jsonl b/.agents/pm/history/pm-github-c093.jsonl new file mode 100644 index 00000000..4330645d --- /dev/null +++ b/.agents/pm/history/pm-github-c093.jsonl @@ -0,0 +1,51 @@ +{"hash_algorithm":"sha256","ts":"2026-10-16T22:29:39.865Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-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-github-c093"},{"op":"add","path":"/metadata/title","value":"Preserve imported GitHub lifetimes, closure evidence and release attribution while linking cross-provider items"},{"op":"add","path":"/metadata/description","value":"Repair the import root cause behind twelve Sourcery findings across six gated sync PRs. Preserve source timestamps through supported SDK metadata, map closure reasons and evidence, select tag-date release windows, report state conflicts, and link existing provider owners before creating items. Verify with recorded API fixtures and real temporary trackers at unchanged release gates."},{"op":"add","path":"/metadata/type","value":"Feature"},{"op":"add","path":"/metadata/status","value":"open"},{"op":"add","path":"/metadata/priority","value":2},{"op":"add","path":"/metadata/tags","value":["fidelity","github","import"]},{"op":"add","path":"/metadata/created_at","value":"2026-10-16T22:29:39.865Z"},{"op":"add","path":"/metadata/updated_at","value":"2026-10-16T22:29:39.865Z"},{"op":"add","path":"/metadata/author","value":"codex-agent"}],"before_hash":"3cc22dff72be7b14824654a7a64ea62b04799939b2fee54c1b5f52ca60bf6df0","after_hash":"b50d85212942172a320545511f94b9b85245ca8fe6f0ce18fe7106e3c9714788","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":"2ac8af668bb420b63327f86e2e9577bcfbaa8385ab83c1b3988e8bc85f7b4ea5"} +{"hash_algorithm":"sha256","ts":"2026-10-16T22:29:57.859Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-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-10-16T22:29:57.859Z"},{"op":"add","path":"/metadata/assignee","value":"codex-agent"},{"op":"add","path":"/metadata/claim_principal","value":"codex-agent"}],"before_hash":"b50d85212942172a320545511f94b9b85245ca8fe6f0ce18fe7106e3c9714788","after_hash":"e72fb7becb48dd2864988a9c697b46556dc566bc2cb1cf2fe768db1f35cdc96d","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":"3e1e8bb142ef57d2e365d9d942014ea0ccf9bbb302acbc62607537f3f2783f2a"} +{"hash_algorithm":"sha256","ts":"2026-10-16T22:30:01.203Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-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-10-16T22:30:01.203Z"},{"op":"replace","path":"/metadata/status","value":"in_progress"}],"before_hash":"e72fb7becb48dd2864988a9c697b46556dc566bc2cb1cf2fe768db1f35cdc96d","after_hash":"87a66a57d38d98a20641bab676916579e0434fa18dce33fef5ad2baf59c0bb30","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":"cd00563a0f1ac9e9824ac5fbe0e49f0c0366686dbdfea2f1848be1b98f236021"} +{"hash_algorithm":"sha256","ts":"2026-10-16T22:30:04.918Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-16T22:30:04.918Z"},{"op":"add","path":"/metadata/comments","value":[{"created_at":"2026-10-16T22:30:04.918Z","author":"codex-agent","text":"Read all twelve unresolved Sourcery threads and the changed tracker/changelog files in the six consumer sync PRs. Scope: fix import only; preserve append-only history and leave unrelated closeout work untouched. The pinned SDK exposes completedAt but reserves created_at, updated_at and closed_at; source timestamp metadata plus an explicit SDK-gap receipt is required unless a supported import path proves otherwise. No upstream SDK issue will be filed under the queue rules."}]}],"before_hash":"87a66a57d38d98a20641bab676916579e0434fa18dce33fef5ad2baf59c0bb30","after_hash":"5829c59a816538878aab567acee0f28b420e4b03411badcc36ca94dcb8f706d2","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":"027d62bdb86ce7dee2e4ce37138cf5cf3601a048cccc2a4888f3fe2debcb27c4"} +{"hash_algorithm":"sha256","ts":"2026-10-16T22:35:10.689Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/1","value":{"created_at":"2026-10-16T22:35:10.689Z","author":"codex-agent","text":"Found public SDK import primitives beyond high-level create/update: item locks, canonical document serialization, before/after history entries and appendHistoryEntry. Implementing an idempotent finalization event with real native dates and source metadata, without modifying any historical record. Existing provider owners receive provenance links only; their content, native lifecycle, release and export identity remain authoritative. Local Git calendar tag dates select the first release at or after source closure; later closures remain pending. GraphQL ClosedEvent closer provides actual closing PR attribution where REST commit_id is null."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-16T22:35:10.689Z"}],"before_hash":"5829c59a816538878aab567acee0f28b420e4b03411badcc36ca94dcb8f706d2","after_hash":"e9a6f13300fd1b4d5aa583cb43cb30a3c390871dc7cced0286aaff11a48c8ea5","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":"9d5f6ea929c4a42c0c5991508e575ec5a8b97db55b067da3518e0e3a84ac1c03"} +{"hash_algorithm":"sha256","ts":"2026-10-16T23:09:11.257Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-16T23:09:11.257Z"},{"op":"add","path":"/metadata/files","value":[{"path":"import-fidelity.ts","scope":"project"},{"path":"index.ts","scope":"project"},{"path":"test/fixtures/import-fidelity/README.md","scope":"project"},{"path":"test/import-fidelity.test.ts","scope":"project"}]}],"before_hash":"e9a6f13300fd1b4d5aa583cb43cb30a3c390871dc7cced0286aaff11a48c8ea5","after_hash":"4f28ec998788c29d2419a1254832d02d73d4c6674709d7d1e937ce0384f7bd59","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":"8837dc776331d0b8644e001ff6d98e1505ad2e05f2223fb141f462dd156719c3"} +{"hash_algorithm":"sha256","ts":"2026-10-16T23:09:17.993Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"docs_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-16T23:09:17.993Z"},{"op":"add","path":"/metadata/docs","value":[{"path":"README.md","scope":"project"}]}],"before_hash":"4f28ec998788c29d2419a1254832d02d73d4c6674709d7d1e937ce0384f7bd59","after_hash":"9b6ad609bffd1627f8ad084fa335bcad0b24064b3208a68fa3a246f76335b16b","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":"2b2caf1500e27e450aa48b1e36e223d4359edbba616cba9b5e9918c8f3b333fa"} +{"hash_algorithm":"sha256","ts":"2026-10-16T23:09:21.843Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"tests_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-16T23:09:21.843Z"},{"op":"add","path":"/metadata/tests","value":[{"command":"node --test test/import-fidelity.test.ts","scope":"project","timeout_seconds":2400,"provenance":{"author":"codex-agent","created_at":"2026-10-16T23:09:21.738Z","source_kind":"local_mutation","source_ref":"feat/import-fidelity"}}]}],"before_hash":"9b6ad609bffd1627f8ad084fa335bcad0b24064b3208a68fa3a246f76335b16b","after_hash":"f9f8e1108bc61c7c56d8e4f5a1225cccef4ec25ace4cfb076d986d8a9386bc2d","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":"416be3f7ea3aca46c176babeac6a4950eac07f5f3adec8a65ea6c8db02d542eb"} +{"hash_algorithm":"sha256","ts":"2026-10-16T23:09:24.995Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/2","value":{"created_at":"2026-10-16T23:09:24.995Z","author":"codex-agent","text":"SDK candidate: bulk compensation captures native updated_at and passes it to restore as a history timestamp. Backdating that field disconnects the rollback version from its event. Preserve source creation/closure/completion natively for momentum and cycle time, and retain the exact source update date in github_updated_at while native updated_at stays the SDK mutation clock. Lifecycle finalization now runs at the SDK committing transition before the journal commits, with real compensation acceptance and a post-recovery idempotent check. No historical entries are rewritten."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-16T23:09:24.995Z"}],"before_hash":"f9f8e1108bc61c7c56d8e4f5a1225cccef4ec25ace4cfb076d986d8a9386bc2d","after_hash":"afca0d31a57f243197dd88376cc6a5c12aef7bdcd69089b85a11f4182bb531d5","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":"7ccd5a7ca444276850648be2999cb056fb9eaf097460a96b8579fdfdbc0df040"} +{"hash_algorithm":"sha256","ts":"2026-10-16T23:14:15.635Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/3/path","value":"test/coverage-remainder.test.ts"},{"op":"replace","path":"/metadata/files/2/path","value":"test/coverage-edges.test.ts"},{"op":"add","path":"/metadata/files/4","value":{"path":"test/fixtures/import-fidelity/README.md","scope":"project"}},{"op":"add","path":"/metadata/files/5","value":{"path":"test/import-fidelity.test.ts","scope":"project"}},{"op":"add","path":"/metadata/files/6","value":{"path":"test/import-selection.test.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-16T23:14:15.635Z"}],"before_hash":"afca0d31a57f243197dd88376cc6a5c12aef7bdcd69089b85a11f4182bb531d5","after_hash":"d0e5108471bc2649939f3603d945c31900cd9e8c2ec1b0f2b8b29be18b2c142d","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":"499e243f72f351d3432773fb9fee26da01523f36485fe68d9253a9ef6a0c9a68"} +{"hash_algorithm":"sha256","ts":"2026-10-16T23:14:20.137Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-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-10-16T23:14:20.137Z"},{"op":"replace","path":"/metadata/tags/2","value":"github"},{"op":"replace","path":"/metadata/tags/1","value":"fidelity"},{"op":"replace","path":"/metadata/tags/0","value":"changelog-unreleased"},{"op":"add","path":"/metadata/tags/3","value":"import"}],"before_hash":"d0e5108471bc2649939f3603d945c31900cd9e8c2ec1b0f2b8b29be18b2c142d","after_hash":"e5ab694af55c8cc4ca693a132c0f6fe3f9325870c8a30533bbb92adb3c490196","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":"f920c211592553a3842ead9e865e597ea6ff04eaf356bd5e1ae2ac9de23416b2"} +{"hash_algorithm":"sha256","ts":"2026-10-16T23:31:39.064Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/3","value":{"created_at":"2026-10-16T23:31:39.064Z","author":"codex-agent","text":"Verified the SDK candidate with a real temporary tracker and public commitImportedItem/commitItemMutations calls: a historical native updated_at followed by an update and deliberate committing failure produces AggregateError, compensation_failed=true, original_restored=false. The importer retains the exact source update timestamp in github_updated_at while keeping native updated_at aligned with immutable event time. Real importer rollback acceptance restores the existing source lifecycle and deletes the newly created item. Focused corrections passed 7/7; final linked tests and unchanged full release gates are running."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-16T23:31:39.064Z"}],"before_hash":"e5ab694af55c8cc4ca693a132c0f6fe3f9325870c8a30533bbb92adb3c490196","after_hash":"a5e32cb2682ab8bb8580f09417fc641451bb887f77ab500cd7288e5ea660a2fa","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":"f86ac4f09eb2ae856f4968cdc0c87bdf38d12b882b1091107b924756531cb1c8"} +{"hash_algorithm":"sha256","ts":"2026-10-17T00:07:53.005Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-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-10-17T00:07:53.005Z","author":"codex-agent","text":"The source-date, provider-link, privacy-plan and invalid-lifetime targeted checks pass 9/9 after certified SDK corpus reads replaced repeated CLI reads. A real tracker exposed the remaining canceled-to-completed transition: the SDK requires explicit repeat-closure authorization. Import reconciliation uses that supported transition with source closure evidence, and both modes now exercise canceled, completed and reopened states. The earlier release-gate probe had obsolete fixture assumptions and was stopped; final gates will run after these corrections."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T00:07:53.005Z"}],"before_hash":"a5e32cb2682ab8bb8580f09417fc641451bb887f77ab500cd7288e5ea660a2fa","after_hash":"dc7d4b5ce36e909b1b575ee44af576bbcfc0cef8af87836c7a656c7d924e953e","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":"74b9d71104500198a704060febfd7747442990f537ffca86367be95bf286a7f0"} +{"hash_algorithm":"sha256","ts":"2026-10-17T00:08:54.786Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/6/path","value":"test/import-fidelity.test.ts"},{"op":"replace","path":"/metadata/files/5/path","value":"test/import-errors.test.ts"},{"op":"add","path":"/metadata/files/7","value":{"path":"test/import-selection.test.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T00:08:54.786Z"}],"before_hash":"dc7d4b5ce36e909b1b575ee44af576bbcfc0cef8af87836c7a656c7d924e953e","after_hash":"47e571cdf9a467bebf80b155ad72ec57a8fdc007ea26ae8482f76fee08b81c16","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":"73914e923404d00080d8b13d5366ce7127f3bd91d232f4cdcb4e9712953b42c9"} +{"hash_algorithm":"sha256","ts":"2026-10-17T00:13:15.552Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-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-10-17T00:13:15.552Z","author":"codex-agent","text":"Terminal reconciliation checks pass 3/3: canceled source records become completed when GitHub does, and reopen clears terminal evidence, in both import modes. Expanded provider matching also follows generic url/external_links when a provider id or tag declares ownership. Final release gates are being restarted against this complete implementation; no coverage thresholds, exemptions or dependency pins changed."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T00:13:15.552Z"}],"before_hash":"47e571cdf9a467bebf80b155ad72ec57a8fdc007ea26ae8482f76fee08b81c16","after_hash":"a6bd29e92c0374e2d77d773f822b13fe6b649afa3e760eabcd3b84284b30c071","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":"f73d1a18d1005444c8b9d2406ec03411dba5bee7643551a3a07b45ddb5ab9f94"} +{"hash_algorithm":"sha256","ts":"2026-10-17T00:22:28.429Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-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-10-17T00:22:28.429Z","author":"codex-agent","text":"Behavioral revert proof at implementation 5999329: scratch copies retained the real recorded-API and temporary-tracker tests, rebuilding before each independent function control. Omitting persistImportFidelity yields 9 expected failures across source lifetime fixtures and state-conflict scenarios. Restoring generic importCloseReason wording yields 2 expected failures (beads and slack-standup); dropping linked closure evidence yields 1 (brief closing PR). Tag-only matchImportedIssue yields 2 expected failures (plain and atomic provider owner linking). Clearing buildImportFidelity release attribution yields 1 expected failure in the two-tag before/between/after fixture. Total: 15 expected behavioral failures, zero passes, skips or cancellations; no SDK mocks. Positive targeted checks passed 9/9, terminal transitions 3/3 and expanded identity checks 2/2. Full unchanged release gates remain in progress."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T00:22:28.429Z"}],"before_hash":"a6bd29e92c0374e2d77d773f822b13fe6b649afa3e760eabcd3b84284b30c071","after_hash":"7daa60391a91db5fbc13d6d00f39b6b62b9291e152c9430b4acc1958c6052fe3","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":"6f6c193deb8ba626aca01c8113a92f23787ed059e1651b284bae974c5e218d5e"} +{"hash_algorithm":"sha256","ts":"2026-10-17T00:27:33.274Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-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-10-17T00:27:33.274Z","author":"codex-agent","text":"The broad gate exposed a real adapter defect for callers passing a repository root: readSettings used the unresolved path, and finalization could append history using a noncanonical item id. Atomic planning now resolves the tracker first; finalization normalizes the id before locking, writing history and invoking hooks. The existing full gate/strict-health fixture and an explicit shortened-id finalization assertion cover this. Updated legacy HTTP replay fixtures to serve closure timelines, and the recovered-journal assertion now expects the earlier explicit no-push refusal. The broad run is diagnostic while these corrections are verified; final release gates will be rerun against frozen source."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T00:27:33.274Z"}],"before_hash":"7daa60391a91db5fbc13d6d00f39b6b62b9291e152c9430b4acc1958c6052fe3","after_hash":"aad2fcdb7a92606c01aa42dbbae0a14e7a03d60ac637814b8deed9660e77c16f","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":"a2c44ba1116e3c6a7df78bde4d79579d3d7bc0e44225d73e2faef1d23f610264"} +{"hash_algorithm":"sha256","ts":"2026-10-17T00:28:48.567Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/7/path","value":"test/import-fidelity.test.ts"},{"op":"replace","path":"/metadata/files/6/path","value":"test/import-errors.test.ts"},{"op":"replace","path":"/metadata/files/5/path","value":"test/handler-failures.test.ts"},{"op":"add","path":"/metadata/files/8","value":{"path":"test/import-gate.test.ts","scope":"project"}},{"op":"add","path":"/metadata/files/9","value":{"path":"test/import-lock.test.ts","scope":"project"}},{"op":"add","path":"/metadata/files/10","value":{"path":"test/import-selection.test.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T00:28:48.567Z"}],"before_hash":"aad2fcdb7a92606c01aa42dbbae0a14e7a03d60ac637814b8deed9660e77c16f","after_hash":"c339d034cad09b2a398b3250a7a3c3a175316cd9c921a61a8440d3fb26afc277","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":"eb364554e62d12d90b28851fdd42a51df93c3df4afa3ffef44b05d53eff6022d"} +{"hash_algorithm":"sha256","ts":"2026-10-17T00:33:40.746Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-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-10-17T00:33:40.746Z","author":"codex-agent","text":"Canonical-id finalization, both repaired HTTP closure fixtures and recovered-journal refusal pass their targeted assertions. The clean strict-health/push/no-op flow now asserts the correct durable-journal recovery counts and passes; the concurrent two-process race also passes with five distinct comments, keeping the production lock wait and every deduplication assertion unchanged. Refused source cancellation and refused provider-link tests pass 2/2 with real tracker writes, recorded HTTP fixtures and CLI fault boundaries; neither can pass the gate or create a duplicate. Pinned typecheck and test compilation pass. Strict health passes with an isolated global registry; default local health sees a pre-existing shared-registry host-version skew. No dependency or registry changes made. Final PM-linked fixtures and unchanged full release gates are now running against frozen source."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T00:33:40.746Z"}],"before_hash":"c339d034cad09b2a398b3250a7a3c3a175316cd9c921a61a8440d3fb26afc277","after_hash":"479cc84c5ca9bb81e744c1b202c8d3747f6516cede46de4bf08c1606508221c7","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":"2f12f57b792c64335276e5b379f18925a066cea3e0b810e449eb2e746bd1636a"} +{"hash_algorithm":"sha256","ts":"2026-10-17T00:39:36.185Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-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-10-17T00:39:36.185Z","author":"codex-agent","text":"Verified all six consumer caller files on their default branches: both reusable workflow SHA and explicit package version remain pinned to 2026.10.16. A new release alone cannot change those pins. Enriched existing adoption task pm-github-aiok rather than creating a duplicate; thread replies will state that the next scheduled sync regenerates the PR after the fixed release is adopted. Consumer branches and workflows remain outside this task."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T00:39:36.185Z"}],"before_hash":"479cc84c5ca9bb81e744c1b202c8d3747f6516cede46de4bf08c1606508221c7","after_hash":"f814b4257fbd499ad5de9cc3b2f7272ea6e93f6c7c46ca4031ebce11065ddaf2","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":"6d12492f5b887b7c866d7ceb9ed100dc5358b7a5bd915892d27137f204028260"} +{"hash_algorithm":"sha256","ts":"2026-10-17T00:47:09.760Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/10","value":{"created_at":"2026-10-17T00:47:09.760Z","author":"codex-agent","text":"Final PM-linked fixture run at implementation 3d4c1bf passes 26/26 with zero failures, skips, cancellations or TODOs. The command was node_modules/.bin/pm test pm-github-c093 --run --progress, running node --test test/import-fidelity.test.ts on real temporary trackers. Isolated V8 receipts measure import-fidelity.ts at exact 100 percent statements, branches, functions and lines. This is module-scoped evidence; package-wide release coverage remains pending. Project policy disables durable test-result capture, so this sanitized comment records the result without changing that policy."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T00:47:09.760Z"}],"before_hash":"f814b4257fbd499ad5de9cc3b2f7272ea6e93f6c7c46ca4031ebce11065ddaf2","after_hash":"32a159020cd60a9d64036d2f49d772dc9066cdcd4c9301175da9c5f4bd2dbc82","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":"53d9a53b49132917de519dbc75b5003c5f52dd571812e57635b0821c12200ac7"} +{"hash_algorithm":"sha256","ts":"2026-10-17T00:50:57.587Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/11","value":{"created_at":"2026-10-17T00:50:57.587Z","author":"codex-agent","text":"Refreshed every behavioral revert control against implementation 3d4c1bf after canonical tracker/id normalization. Each scratch package rebuilt with only the relevant function behavior reverted: lifetimes 9 expected failures, closure wording 2, closing-PR evidence 1, provider identity 2, release attribution 1. All 15 fail for their behavioral assertions, with zero passes, skips or cancellations. The positive 26-case fixture run and module-scoped 100/100/100/100 receipt use the same implementation. Package-wide npm/Bun gates are separate and remain pending."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T00:50:57.587Z"}],"before_hash":"32a159020cd60a9d64036d2f49d772dc9066cdcd4c9301175da9c5f4bd2dbc82","after_hash":"3ca1494a4fd47af88545dd36b8e297f876cb0a33188b3885ed8b187bb81b8fac","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":"50ce2517f25ddfaaa11b656f6ba939542c7f38414b456359e1c8c64f2318d750"} +{"hash_algorithm":"sha256","ts":"2026-10-17T01:27:05.726Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/10/path","value":"test/import-lock.test.ts"},{"op":"replace","path":"/metadata/files/9/path","value":"test/import-gate.test.ts"},{"op":"replace","path":"/metadata/files/8/path","value":"test/import-fidelity.test.ts"},{"op":"replace","path":"/metadata/files/7/path","value":"test/import-errors.test.ts"},{"op":"replace","path":"/metadata/files/6/path","value":"test/handler-failures.test.ts"},{"op":"replace","path":"/metadata/files/5/path","value":"test/fixtures/import-fidelity/README.md"},{"op":"replace","path":"/metadata/files/4/path","value":"test/coverage-remainder.test.ts"},{"op":"replace","path":"/metadata/files/3/path","value":"test/coverage-edges.test.ts"},{"op":"replace","path":"/metadata/files/2/path","value":"test/atomic.test.ts"},{"op":"add","path":"/metadata/files/11","value":{"path":"test/import-selection.test.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T01:27:05.726Z"}],"before_hash":"3ca1494a4fd47af88545dd36b8e297f876cb0a33188b3885ed8b187bb81b8fac","after_hash":"0736132521f44e084c09d63a0bd7bfc8ee47f2a7815e6b168b76b1485fd084ff","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":"77b52614dfd34658ca174906cf59f15e6c248524454d0b758d817057f0e92aae"} +{"hash_algorithm":"sha256","ts":"2026-10-17T01:27:07.433Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/12","value":{"created_at":"2026-10-17T01:27:07.433Z","author":"codex-agent","text":"A stronger creation/state-cycle regression exposed completed-journal reuse after state changes. Transaction identity now binds to public immutable history: external edits and earlier different native states invalidate cached completion, while create staging and compensation preserve retry identity. Real SDK checks pass 8/8, including a local reopen, interrupted governed reconciliation, resume, compensation and exact no-op retry. Identity/evidence/history-shape checks pass 4/4: unset provider fields and copied GitHub body/labels cannot change known GitHub ownership; declared nested provider origins and exporter provenance still link existing owners. Closing commit attribution respects the API commit repository URL. Final fixture suite now contains 29 tests; full unchanged gates remain pending."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T01:27:07.433Z"}],"before_hash":"0736132521f44e084c09d63a0bd7bfc8ee47f2a7815e6b168b76b1485fd084ff","after_hash":"4a820015a6641d1a7340ff45a3c9a10739a8a4614d80e3c2ee6f49fbc1a9ef38","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":"bcf574146af5437b5f4b06c42ff224fb5df77677afc40e983a447d2c25d2b4f8"} +{"hash_algorithm":"sha256","ts":"2026-10-17T01:36:22.951Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/13","value":{"created_at":"2026-10-17T01:36:22.951Z","author":"codex-agent","text":"Expanded final PM-linked suite passes 29/29, zero failures, skips, cancellations or TODOs. Fresh V8 receipts again measure import-fidelity.ts at exact 100/100/100/100, including history context selection, direct canceled creation, external local edits, real interrupted reconciliation/resume, nested provider origins, empty-field ownership stability and cross-repository closing commit URLs. Typecheck, test compilation, ESLint, docstrings and zero source duplication pass. The full package npm/Bun gates will now run from the frozen implementation; no thresholds, inventory exclusions, dependency pins or test-result policy changed."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T01:36:22.951Z"}],"before_hash":"4a820015a6641d1a7340ff45a3c9a10739a8a4614d80e3c2ee6f49fbc1a9ef38","after_hash":"46b91f5e71cda40b7d0669070e2cc0408a07d7a4f7bb885194d2e1db0a65ca42","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":"a0bfb84c51bcc006e8a59c6ccaa38b71f24d387d85b078cc9c57ea0ab5cbc965"} +{"hash_algorithm":"sha256","ts":"2026-10-17T01:42:21.617Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/14","value":{"created_at":"2026-10-17T01:42:21.617Z","author":"codex-agent","text":"Function-only revert proof refreshed against implementation 21b17f3: lifetimes 9 expected failures; closure wording 2; closing-PR evidence 1; provider identity 2; release windows 1; legacy cancellation planning 1; external history revision 1; repeated lifecycle context 1. Total 18 expected failures, zero passes/skips/cancellations. Every package rebuilt first; all 18 failures contain ERR_ASSERTION and no startup/module/syntax failures. The added controls use real SDK trackers to expose stale completed journals and missing cancellation reasons. Positive linked suite passes 29/29; full package gates are running without source changes."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T01:42:21.617Z"}],"before_hash":"46b91f5e71cda40b7d0669070e2cc0408a07d7a4f7bb885194d2e1db0a65ca42","after_hash":"30e8024921e1e7ac44a4f390a97ca4774f18fc0003233a5843303ef181f2cf69","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":"c28b3cb5ef66424a6bda9c71c67e53c71cc00dc37f46778f4938d29ca90b64b0"} +{"hash_algorithm":"sha256","ts":"2026-10-17T02:06:25.333Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/15","value":{"created_at":"2026-10-17T02:06:25.333Z","author":"codex-agent","text":"The broad gate found one outdated HTTP fixture: the executable clean-publish replay returned issue arrays for closure timeline/GraphQL requests. Updated githubHandler to serve empty closure connections and supplied a valid completed closure date. The focused executable workflow now passes 1/1 through both fresh checkout runs, proving review creation and update. Production source is unchanged from 21b17f3. The running broad pass is collecting remaining coverage feedback before the final npm/Bun reruns."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T02:06:25.333Z"}],"before_hash":"30e8024921e1e7ac44a4f390a97ca4774f18fc0003233a5843303ef181f2cf69","after_hash":"4b2f16453ea5c17f7da08095afc444dddf91149f57df24183ab2e72920bcac82","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":"18cd5b55a1f55f66eee16c8ce154e8b233617d5f99de12603d088e1529953375"} +{"hash_algorithm":"sha256","ts":"2026-10-17T02:22:19.497Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/16","value":{"created_at":"2026-10-17T02:22:19.497Z","author":"codex-agent","text":"The complete broad pass measures all 14 authored modules at exact 100/100/100/100: 10403/10403 lines and statements, 271/271 functions, 3122/3122 branches. It ran 769 tests: 768 pass, one outdated closure HTTP fixture failed, zero skips/cancellations/TODOs. The repaired executable clean-publish fixture passes 1/1 through both fresh checkouts. This was not a passing release gate. Re-running the literal full npm CI-identity gate and then the Bun release gate with the fixture correction; production source remains 21b17f3."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T02:22:19.497Z"}],"before_hash":"4b2f16453ea5c17f7da08095afc444dddf91149f57df24183ab2e72920bcac82","after_hash":"70b075067dabb6154293554a32a6a4cd964f20e6bf329550a8ba5342b7902a07","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":"3f840581662aa01c2abda393d464c543205cbb09ed422fc72f1089b6658622eb"} +{"hash_algorithm":"sha256","ts":"2026-10-17T02:58:38.811Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/17","value":{"created_at":"2026-10-17T02:58:38.811Z","author":"codex-agent","text":"The full CI-identity npm run release:check PASSED at unchanged thresholds: 769/769 tests, zero failures/skips/cancellations/TODOs, exact 100/100/100/100 across all 14 authored modules. Production dependency audit, package dry-run packing, changelog check and publish attestation passed. Source is still 21b17f3 with the workflow fixture repair in 6cb1a1a. The separate literal bun run release:check is now running sequentially with isolated Git identity and unchanged policy."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T02:58:38.811Z"}],"before_hash":"70b075067dabb6154293554a32a6a4cd964f20e6bf329550a8ba5342b7902a07","after_hash":"4e93a1ffa68f4f818474b5f9bb0adf8190779bdd52856767b7ac567362aa2b78","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":"b78885c2fec99bed1d3e4492e4d17070b734d69d8608146b965a99f9eb827324"} +{"hash_algorithm":"sha256","ts":"2026-10-17T03:46:54.138Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/18","value":{"created_at":"2026-10-17T03:46:54.138Z","author":"codex-agent","text":"Both required full release gates PASSED under isolated CI Git identity: npm run release:check and bun run release:check each ran 769/769 tests with zero failures, skips, cancellations or TODOs. Both measured exact 100/100/100/100 across all 14 authored modules at unchanged thresholds: 10403/10403 lines and statements, 271/271 functions, 3122/3122 branches. Audit, package dry-run packing, generated changelog and publish attestation passed. Production source remains 21b17f3; workflow HTTP fixture correction is 6cb1a1a. Preparing the public PR, then checking its CI before replying to the 12 unresolved consumer Sourcery threads."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T03:46:54.138Z"}],"before_hash":"4e93a1ffa68f4f818474b5f9bb0adf8190779bdd52856767b7ac567362aa2b78","after_hash":"d232d3917f9f62140ec6bd9b024f1ac669334d46525db48d0ff029034459205b","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":"05e384a390aa772e4e0d749fe99a17b3270de611e5703565ec2ec5092b7eda0e"} +{"hash_algorithm":"sha256","ts":"2026-10-17T03:48:36.222Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/19","value":{"created_at":"2026-10-17T03:48:36.222Z","author":"codex-agent","text":"Opened https://github.com/unbraind/pm-github/pull/142 from feat/import-fidelity after both full release gates passed. The pre-push privacy gate audited 4950 Git objects with approved identities and zero secret or host-path matches; strict health with required merge drivers in a fresh SDK context and validation passed. Monitoring PR checks before posting replies to unresolved consumer review threads; no consumer branch changes are authorized or included."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T03:48:36.222Z"}],"before_hash":"d232d3917f9f62140ec6bd9b024f1ac669334d46525db48d0ff029034459205b","after_hash":"af2902a517d3136a8fd63ac4ff15a9bc1ad826ea614f0b3aaf7bc240fa874fea","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":"87724721b9aaa9ef7341387ce7d329fc2720386bd7cd4883b6574ccc60676954"} +{"hash_algorithm":"sha256","ts":"2026-10-17T03:50:29.571Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/20","value":{"created_at":"2026-10-17T03:50:29.571Z","author":"codex-agent","text":"PR 142 CodeQL reported incomplete URL substring sanitization in a test assertion that searched serialized preview JSON for a fixture PR URL. Replaced that assertion with equality against the exact github_close_reason field, including the full source date and closing PR URL. This strengthens the regression instead of suppressing the alert; production source is unchanged. Running both affected modes, test compilation and lint before pushing the check fix."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T03:50:29.571Z"}],"before_hash":"af2902a517d3136a8fd63ac4ff15a9bc1ad826ea614f0b3aaf7bc240fa874fea","after_hash":"3c90a2ae4664ef624c24f887f6e6b7e2b7deb1600cd6971380dc30f252ad602e","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":"36e0f4422cc71fbc915fa380ddb532bfabaaf162c568e1e82b318e4768a19654"} +{"hash_algorithm":"sha256","ts":"2026-10-17T03:51:34.174Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/21","value":{"created_at":"2026-10-17T03:51:34.174Z","author":"codex-agent","text":"The exact closure-plan assertion passes both atomic modes: node --test --test-name-pattern=\"gated closure plans\" test/import-fidelity.test.ts, 2/2 with no failures/skips/cancellations/TODOs. npm run build:test and npm run lint pass. This addresses the CodeQL test-only URL-substring finding without suppressions or production changes; the full npm/Bun 769/769 gate receipts remain applicable to identical production source."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T03:51:34.174Z"}],"before_hash":"3c90a2ae4664ef624c24f887f6e6b7e2b7deb1600cd6971380dc30f252ad602e","after_hash":"b4cd9c4cbfe546e91acce0dbdb9f598dabb0c897696aaf1d00b1be5a91202126","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":"bde1781da4eddccd865709275a559329c61320af179cf22ac2fff3c732290e66"} +{"hash_algorithm":"sha256","ts":"2026-10-17T04:19:20.812Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/22","value":{"created_at":"2026-10-17T04:19:20.812Z","author":"codex-agent","text":"PR 142 is green at 667bd91: Node 22 and Node 26 each passed 785/785 coverage tests plus 785/785 native Bun tests, with zero failures/skips/cancellations/TODOs and exact 100/100/100/100 across all 14 authored modules. CI includes 16 additional baseline closeout tests from the independently merged PR 140; this branch does not modify that work. CodeQL and Semgrep passed. Source is unchanged from the locally verified 769/769 npm and Bun release gates. Reviewer services were budget-limited, expired or skipped, so these checks are execution evidence rather than review approval. CI: https://github.com/unbraind/pm-github/actions/runs/39000473004"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T04:19:20.812Z"}],"before_hash":"b4cd9c4cbfe546e91acce0dbdb9f598dabb0c897696aaf1d00b1be5a91202126","after_hash":"8381c9f85755bfe98be47737e1f615585eeeaa2ac6ea79f3fa02d8df83484218","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":"508c6b531fc631b7abff4f58d1d982ccd444f342d6dfe78fbad4e9fa6f171de5"} +{"hash_algorithm":"sha256","ts":"2026-10-17T04:19:22.891Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/23","value":{"created_at":"2026-10-17T04:19:22.891Z","author":"codex-agent","text":"Replied with plain REST replies to all 12 unresolved Sourcery threads across the six consumer sync PRs, linking PR 142 and explaining the fixed-release adoption prerequisite for the next scheduled regeneration. Verified all consumer heads and open states unchanged. Sourcery automatically resolved the threads after the replies; restored all 12 to unresolved to honor the requested state. No consumer files, branches or PR contents were edited. Reply evidence: https://github.com/unbraind/pm-beads/pull/145#discussion_r4301321723, https://github.com/unbraind/pm-beads/pull/145#discussion_r4301321859, https://github.com/unbraind/pm-brief/pull/154#discussion_r4301321968, https://github.com/unbraind/pm-brief/pull/154#discussion_r4301322089, https://github.com/unbraind/pm-context/pull/147#discussion_r4301322194, https://github.com/unbraind/pm-csv/pull/163#discussion_r4301322300, https://github.com/unbraind/pm-csv/pull/163#discussion_r4301322433, https://github.com/unbraind/pm-csv/pull/163#discussion_r4301322574, https://github.com/unbraind/pm-linear/pull/150#discussion_r4301322688, https://github.com/unbraind/pm-linear/pull/150#discussion_r4301322817, https://github.com/unbraind/pm-slack-standup/pull/127#discussion_r4301323269, https://github.com/unbraind/pm-slack-standup/pull/127#discussion_r4301323414"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T04:19:22.891Z"}],"before_hash":"8381c9f85755bfe98be47737e1f615585eeeaa2ac6ea79f3fa02d8df83484218","after_hash":"abc20dfab38306df4e9feab5829ba3ef9cb072873235ba49ee58b39fdbfe0804","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":"621f34a560fc7ecc0ef3f6350648f8126956dc912ec4011042e4fe6668ad1de5"} +{"hash_algorithm":"sha256","ts":"2026-10-17T04:19:24.535Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"3d7aa25e172d487bf4e14dde","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T04:19:24.535Z"}],"before_hash":"abc20dfab38306df4e9feab5829ba3ef9cb072873235ba49ee58b39fdbfe0804","after_hash":"29e5be7ff5ba0fc53d0406c2b3ed4544b1a0a682b05ef0c2d2ba18e3ee801c5d","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":"64eba0d68348e882f1da474cf2f53431243bd80994b958ab20d98cc38a80de6d"} +{"hash_algorithm":"sha256","ts":"2026-10-17T10:31:22.906Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"043799c84ec1b103f8c35f0e","agent_provenance":{"model":{"value":"gpt-6.1-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-10-17T10:31:22.906Z"},{"op":"add","path":"/metadata/assignee","value":"codex-agent"},{"op":"add","path":"/metadata/claim_principal","value":"codex-agent"}],"before_hash":"29e5be7ff5ba0fc53d0406c2b3ed4544b1a0a682b05ef0c2d2ba18e3ee801c5d","after_hash":"8835c107f9221b1f3d14e618b6f0e41fe4241db8291feae221366453bda45c39","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":"36d2fb8d7833fd27b12fc93f3004684f2fda1a9fc01e9d73beb343ab27b82556"} +{"hash_algorithm":"sha256","ts":"2026-10-17T10:31:32.545Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"043799c84ec1b103f8c35f0e","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/24","value":{"created_at":"2026-10-17T10:31:32.545Z","author":"codex-agent","text":"Round 1: verified all five unresolved review findings on a45661c. Plan: restrict identity fields, refresh URL-only GitHub owners, select latest closure evidence with bounded forward skew, exercise plain partial writes and empty gated selection, prove behavioral reverts, then run unchanged npm and Bun release gates."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T10:31:32.545Z"}],"before_hash":"8835c107f9221b1f3d14e618b6f0e41fe4241db8291feae221366453bda45c39","after_hash":"b5885d2e47d9928ef02a2dc36cf69962e89ee42765d06649056db37072007d42","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":"42b0f42a3beb8be150070c59436de5f9fad11eba16c6e2ffc3984b185d09e240"} +{"hash_algorithm":"sha256","ts":"2026-10-17T10:34:47.596Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"043799c84ec1b103f8c35f0e","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"tests_add","patch":[{"op":"add","path":"/metadata/tests/1","value":{"command":"node --test --test-name-pattern=\"closure evidence accepts forward skew\" test/import-fidelity.test.ts","scope":"project","timeout_seconds":2400,"provenance":{"author":"codex-agent","created_at":"2026-10-17T10:34:46.689Z","source_kind":"local_mutation","source_ref":"feat/import-fidelity"}}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T10:34:47.596Z"}],"before_hash":"b5885d2e47d9928ef02a2dc36cf69962e89ee42765d06649056db37072007d42","after_hash":"15d591c7403dc01d6b0471a02455e3160cc0269a9bf5f986a10758ee7ddbbdf9","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":"8e49f833908b1faf6802f7cb50bdf3343559db0ba9257fdd85d004b366ad3858"} +{"hash_algorithm":"sha256","ts":"2026-10-17T10:37:14.234Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"043799c84ec1b103f8c35f0e","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/25","value":{"created_at":"2026-10-17T10:37:14.234Z","author":"codex-agent","text":"Behavioral revert proof: restoring only matchImportedIssue from a45661c fails identity excludes descriptions even when multiple items mention another issue and identity classifies URL-only GitHub owners for lifecycle refresh (one expected assertion failure per control). Restoring only importCloseReason fails the forward-skew non-null closer assertion; restoring only fetchImportTimeline fails the GraphQL suppression/request-count assertion. All four controls exit 1 on behavioral assertions; fixed functions restored. The linked closure regression passes through pm test --run --only-last --progress. No SDK mocks or threshold changes."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T10:37:14.234Z"}],"before_hash":"15d591c7403dc01d6b0471a02455e3160cc0269a9bf5f986a10758ee7ddbbdf9","after_hash":"68caa4ecf7d2fbad3db8dc5af9b957286233245eaee9f41c75b40eedd5b0b99e","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":"e0e7850066c1676bd1dc0c0b88e35334f4cd7677582af32bde44b775984ecb4a"} +{"hash_algorithm":"sha256","ts":"2026-10-17T10:40:00.014Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"043799c84ec1b103f8c35f0e","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/26","value":{"created_at":"2026-10-17T10:39:59.987Z","author":"codex-agent","text":"Round 1 focused integration validation passes 9/9: env HOME=$(mktemp -d) GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_NOSYSTEM=1 node --test --test-name-pattern=\"description mentions|URL-only GitHub owner|ambiguous later|empty filtered\" test/import-fidelity.test.ts. Real trackers prove incidental descriptions remain unchanged, URL-only owners refresh native lifecycle/release, plain multi-issue ambiguity retains the earlier write, gated/atomic ambiguity writes nothing, and filtered-empty gated preview/apply omit plan. Both README qualifications match measured behavior. Strict health with required merge drivers passes under isolated HOME; the initial shared-home host/SDK version-skew warning is absent there. Full npm release gate running, Bun follows sequentially."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T10:40:00.014Z"}],"before_hash":"68caa4ecf7d2fbad3db8dc5af9b957286233245eaee9f41c75b40eedd5b0b99e","after_hash":"8f750cf9a4eef9ab5e542f767b253fe45af488c07a02029d99923a0ef5344920","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":"0b53b96e3944a8efa16424abc71a2f7ec6800a7712f8b6ed0c2d17bbb99ca8f6"} +{"hash_algorithm":"sha256","ts":"2026-10-17T10:55:44.523Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"043799c84ec1b103f8c35f0e","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/27","value":{"created_at":"2026-10-17T10:55:44.523Z","author":"codex-agent","text":"The first npm release gate has reported an existing blocker-marker watchdog timeout: marker extraction finishes on repeated unclosed marker prefixes exceeded its 10-second subprocess budget, which includes package/SDK module startup under concurrent coverage. This is not a passing gate. Investigating in isolation without relaxing thresholds; the known watchdog-startup task pm-github-sah1 may cover this infrastructure issue. The complete focused fidelity run passed 38/38, and the two added identity unit cases passed separately."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T10:55:44.523Z"}],"before_hash":"8f750cf9a4eef9ab5e542f767b253fe45af488c07a02029d99923a0ef5344920","after_hash":"8e2ce4a48fae513718a4d8d8f7d0a2b66883b4a844a3205e69fb22687ec4d603","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":"36db87e327f2b9147d64a7843eba0e2caf1a6498a5467a1535e19436fb818baa"} +{"hash_algorithm":"sha256","ts":"2026-10-17T10:59:05.519Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"043799c84ec1b103f8c35f0e","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/11/path","value":"test/import-lock.test.ts"},{"op":"replace","path":"/metadata/files/10/path","value":"test/import-gate.test.ts"},{"op":"replace","path":"/metadata/files/9/path","value":"test/import-fidelity.test.ts"},{"op":"replace","path":"/metadata/files/8/path","value":"test/import-errors.test.ts"},{"op":"replace","path":"/metadata/files/7/path","value":"test/handler-failures.test.ts"},{"op":"replace","path":"/metadata/files/6/path","value":"test/fixtures/import-fidelity/README.md"},{"op":"replace","path":"/metadata/files/5/path","value":"test/coverage-remainder.test.ts"},{"op":"replace","path":"/metadata/files/4/path","value":"test/coverage-edges.test.ts"},{"op":"replace","path":"/metadata/files/3/path","value":"test/blockers.test.ts"},{"op":"add","path":"/metadata/files/12","value":{"path":"test/import-selection.test.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T10:59:05.519Z"}],"before_hash":"8e2ce4a48fae513718a4d8d8f7d0a2b66883b4a844a3205e69fb22687ec4d603","after_hash":"436264dee0ddb20a4093359e281a3515fcc3a0992d0fd0c7ba88ee21fe8f28fd","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":"e789d33d636474b5cd890668c286003728e4f296ff79e2547c193ff9673f0c1a"} +{"hash_algorithm":"sha256","ts":"2026-10-17T10:59:20.262Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"043799c84ec1b103f8c35f0e","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/28","value":{"created_at":"2026-10-17T10:59:20.262Z","author":"codex-agent","text":"Stopped the known-failing first gate after recording the timeout; it is not counted as passing. A narrow test-only repair starts the same 10-second marker-parser deadline after child readiness, bounds cold startup separately at 60 seconds, and adds an intentional spin control requiring SIGKILL. Production blocker logic, consumer acceptance budgets, coverage thresholds and SDK pins remain unchanged. Related follow-up evidence added to existing pm-github-sah1 instead of duplicating that watchdog-startup issue. Verifying instrumented Node and native Bun probes before restarting both literal release gates."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T10:59:20.262Z"}],"before_hash":"436264dee0ddb20a4093359e281a3515fcc3a0992d0fd0c7ba88ee21fe8f28fd","after_hash":"3e7356d7eace267044b8f96bdf9a8241d9b2ff5a83836e93e24576b33571e0bc","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":"de9c148f48c78aaa51013b6af0bf7d245374ccea192c36a2c538805076677f7f"} +{"hash_algorithm":"sha256","ts":"2026-10-17T10:59:59.257Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"043799c84ec1b103f8c35f0e","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/29","value":{"created_at":"2026-10-17T10:59:59.257Z","author":"codex-agent","text":"The readiness-based marker probe and forced-spin control pass 2/2 under instrumented Node (NODE_V8_COVERAGE with node --test --test-name-pattern=\"marker extraction|marker watchdog\" test/blockers.test.ts) and 2/2 under native Bun (bun test --timeout 600000 -t \"marker extraction|marker watchdog\" test/blockers.test.ts). The spin receives SIGKILL after readiness; the parser retains its original 10-second budget. Test compilation and lint pass. Restarting the complete npm release gate on installed Node 26 with isolated CI Git identity, then running the literal Bun release gate sequentially."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T10:59:59.257Z"}],"before_hash":"3e7356d7eace267044b8f96bdf9a8241d9b2ff5a83836e93e24576b33571e0bc","after_hash":"d1ce9bcfc906780f1ed31292759d3398571c1d150e03a35309c9c213eef4ee92","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":"13c86db14b3486bcf78f8062364c2719c01d8bfbbbfb2c23cc5f2c49e1e7317b"} +{"hash_algorithm":"sha256","ts":"2026-10-17T11:53:56.007Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"043799c84ec1b103f8c35f0e","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/30","value":{"created_at":"2026-10-17T11:53:56.007Z","author":"codex-agent","text":"The diagnostic npm run passed the repaired marker watchdog and all 40 fidelity cases, but found an older refused-provider-link fixture relying on a GitHub URL only inside the owner description. The fixture already declared Linear; it lacked an issue-body provider backlink. Updated only the fixture to use a declared Linear URL referenced by the issue body, preserving every refusal/no-write assertion. This is required by the new free-text ownership rule. Running both refused-link/cancellation controls under V8 coverage, while allowing the diagnostic package run to reveal any further failures before a clean full rerun. This diagnostic run is not counted as passing."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T11:53:56.007Z"}],"before_hash":"d1ce9bcfc906780f1ed31292759d3398571c1d150e03a35309c9c213eef4ee92","after_hash":"7f223d3f758509cbb9e85c7ed6497c76783405b819d2b604dafc61983cfb98ac","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":"9690a531ad70fc035074013c56298214e20742b425b910bdde5ee1da9da06eae"} +{"hash_algorithm":"sha256","ts":"2026-10-17T12:15:13.151Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"043799c84ec1b103f8c35f0e","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/31","value":{"created_at":"2026-10-17T12:15:13.151Z","author":"codex-agent","text":"Diagnostic npm run completed with 796/797 passing, zero skips/cancellations/TODOs, and exactly one failure: the old description-only provider-link fixture now corrected. Coverage was exact 10438/10438 lines/statements and 273/273 functions, but 3154/3156 branches. Missing branches were the refused-provider-link path in index.ts and the undefined locator fallback in import-fidelity.ts. Both new closure-selection paths are covered. Added an undefined github_url adversarial case and retained all gate thresholds and source inventory. Verifying the two precise branch repairs before rerunning the full npm and Bun release commands."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T12:15:13.151Z"}],"before_hash":"7f223d3f758509cbb9e85c7ed6497c76783405b819d2b604dafc61983cfb98ac","after_hash":"c0e624440c1a21a1958efe584c37766d02e6b5db56b7558f02f71229915bac9c","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":"a994d7dbf88a6494aff914a01c8d4e16f189d88551957c404132ef9667422ee3"} +{"hash_algorithm":"sha256","ts":"2026-10-17T12:18:26.459Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"043799c84ec1b103f8c35f0e","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/32","value":{"created_at":"2026-10-17T12:18:26.459Z","author":"codex-agent","text":"Focused branch repair proof passes 3/3 under c8: refused cancellation, refused provider linking, and identity edge cases with undefined github_url. V8 receipts show execution of the previously uncovered locator fallback and the refused-link throw at index.ts:3470. Test compilation and lint pass. No production logic changed after the four behavioral-revert controls; only provider-fixture identity and an absent-value case were corrected. Starting a clean full npm release:check with CI identity, then the independent Bun release:check at unchanged policy."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T12:18:26.459Z"}],"before_hash":"c0e624440c1a21a1958efe584c37766d02e6b5db56b7558f02f71229915bac9c","after_hash":"eda66d60618d714f8bc540625a1e17949764590b8058bcf4058fa5e169a7b503","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":"6f29376aee93081c67c7e2885b642deaa85acb4976176fff84049dd76fe3ee10"} +{"hash_algorithm":"sha256","ts":"2026-10-17T13:12:31.216Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"043799c84ec1b103f8c35f0e","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/33","value":{"created_at":"2026-10-17T13:12:31.216Z","author":"codex-agent","text":"Clean CI-identity npm run release:check PASSED on installed Node 26: 797/797 tests, zero failures/skips/cancellations/TODOs. Exact all-source coverage across 14 authored modules: 10438/10438 lines and statements, 273/273 functions, 3157/3157 branches. Type checks, docstrings, lint, zero duplication, privacy, production audit, package dry-run, changelog and publish attestation all passed. Starting the independent literal bun run release:check sequentially with fresh HOME and disabled global/system Git identity; policy, SDK pins and package lock remain unchanged."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T13:12:31.216Z"}],"before_hash":"eda66d60618d714f8bc540625a1e17949764590b8058bcf4058fa5e169a7b503","after_hash":"15d3771447836a04a8d8d93aa07103d64ef82555944ad4d94eda34693d63a34b","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":"d7ffa4ecbcc242ef81fa861f9e9def70e993fb9fa4f326249266bb910cea4bff"} +{"hash_algorithm":"sha256","ts":"2026-10-17T13:40:46.635Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"043799c84ec1b103f8c35f0e","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/34","value":{"created_at":"2026-10-17T13:40:46.635Z","author":"codex-agent","text":"Round 1 implementation and local verification complete. Both literal CI-identity release gates PASSED: npm run release:check and bun run release:check each 797/797 tests, zero failures/skips/cancellations/TODOs, exact 100/100/100/100 across 14 authored modules (10438/10438 lines/statements, 273/273 functions, 3157/3157 branches). Audit found zero vulnerabilities; docstrings, lint, duplication, privacy, packing, changelog and attestation pass. Four function-only behavioral reverts produced the expected assertion failures. README partial-write and empty-plan qualifications are verified with real trackers. Marker startup probe is bounded separately while retaining its 10-second work deadline and a forced-spin control. Existing pm-github-sah1 remains open for its original acceptance-watchdog case. Releasing the coding claim for orchestrator verification; next actions are commit/push, replies to the five original review threads, and bounded CI monitoring. No items will be closed."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T13:40:46.635Z"}],"before_hash":"15d3771447836a04a8d8d93aa07103d64ef82555944ad4d94eda34693d63a34b","after_hash":"8752acecb505866e371a3e567e7c4375cfa45ea1290de8f620209e2b89f43a96","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":"a4636433cd6c133621a7791c6fff8c19ae1a52bb5188255b3e376bbf9c6949f5"} +{"hash_algorithm":"sha256","ts":"2026-10-17T13:40:47.560Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"043799c84ec1b103f8c35f0e","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T13:40:47.560Z"}],"before_hash":"8752acecb505866e371a3e567e7c4375cfa45ea1290de8f620209e2b89f43a96","after_hash":"43ab1cf609be9527eb33197c9943ff1aa96cdd5df31993ed6ace87854ec18ff5","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":"c6cc15ecd82ea75538882b5f650496b3dafafbd2c5a30bf5fc26107b2c9982a3"} diff --git a/.agents/pm/history/pm-github-sah1.jsonl b/.agents/pm/history/pm-github-sah1.jsonl index 8cc7e981..714c0963 100644 --- a/.agents/pm/history/pm-github-sah1.jsonl +++ b/.agents/pm/history/pm-github-sah1.jsonl @@ -1 +1,2 @@ {"hash_algorithm":"sha256","ts":"2026-10-15T00:13:15.730Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"8883d09363214115c628d33e","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"create","patch":[{"op":"add","path":"/metadata/id","value":"pm-github-sah1"},{"op":"add","path":"/metadata/title","value":"Stabilize the watchdog startup probe under concurrent coverage"},{"op":"add","path":"/metadata/description","value":"The existing acceptance-watchdog test can reach its 500 ms synthetic-child deadline before the child prints ready under concurrent coverage. A full package run failed the ready assertion while the isolated watchdog suite passed 2/2. This is separate from the delivered bounded-consumer watchdog work in pm-github-hptv and from closeout behavior."},{"op":"add","path":"/metadata/type","value":"Issue"},{"op":"add","path":"/metadata/status","value":"open"},{"op":"add","path":"/metadata/priority","value":2},{"op":"add","path":"/metadata/tags","value":[]},{"op":"add","path":"/metadata/created_at","value":"2026-10-15T00:13:15.730Z"},{"op":"add","path":"/metadata/updated_at","value":"2026-10-15T00:13:15.730Z"},{"op":"add","path":"/metadata/author","value":"codex-agent"},{"op":"add","path":"/metadata/acceptance_criteria","value":"Reproduce the readiness race with real child processes under coverage and make the probe deterministic without weakening the 500 ms deadline, 100 ms cleanup, 3 second assertion, or 45 second consumer acceptance budget.; Keep all source inventories, four coverage thresholds and SDK pins unchanged; pass isolated and full npm/Bun checks; demonstrate that the probe still detects a missing forced kill."},{"op":"add","path":"/metadata/files","value":[{"path":"test/bounded-cli.test.ts","scope":"project"},{"path":"test/helpers/bounded-cli.ts","scope":"project"}]}],"before_hash":"3cc22dff72be7b14824654a7a64ea62b04799939b2fee54c1b5f52ca60bf6df0","after_hash":"0833dadd895d44d25f57274624aa449a1cad2c52e966392059401a1f50a0e19c","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":"24ff7f4768f815308d5e49f79b1e75c6c3793600b31d994177a25a2d3810f090"} +{"hash_algorithm":"sha256","ts":"2026-10-17T10:58:49.678Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"043799c84ec1b103f8c35f0e","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-17T10:58:49.678Z"},{"op":"add","path":"/metadata/comments","value":[{"created_at":"2026-10-17T10:58:49.678Z","author":"codex-agent","text":"Related startup-sensitive probe found during PR 142 import-fidelity validation: test/blockers.test.ts marker extraction includes cold package/SDK imports in its 10-second child budget. It passed isolated in 9.8 seconds but timed out under coverage in 10.3 seconds, including with Node 26. The PR separates finite module startup from the unchanged 10-second parser deadline and adds a real stalled-operation forced-kill control. The original 500 ms acceptance-watchdog startup case remains tracked here; its existing budgets and helper are unchanged."}]}],"before_hash":"0833dadd895d44d25f57274624aa449a1cad2c52e966392059401a1f50a0e19c","after_hash":"715f7eec9c00fed9790960bfc40af612c07b0de802204f0ca3bf112ade28bf83","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":"1ad7e16e4e9b071336b4714f48388c8fbffd4ed37ad7106e02240b552b081bda"} diff --git a/.agents/pm/issues/pm-github-sah1.toon b/.agents/pm/issues/pm-github-sah1.toon index 4bc88cda..cce5b92f 100644 --- a/.agents/pm/issues/pm-github-sah1.toon +++ b/.agents/pm/issues/pm-github-sah1.toon @@ -6,9 +6,11 @@ status: open priority: 2 tags: [] created_at: "2026-10-15T00:13:15.730Z" -updated_at: "2026-10-15T00:13:15.730Z" +updated_at: "2026-10-17T10:58:49.678Z" author: codex-agent acceptance_criteria: "Reproduce the readiness race with real child processes under coverage and make the probe deterministic without weakening the 500 ms deadline, 100 ms cleanup, 3 second assertion, or 45 second consumer acceptance budget.; Keep all source inventories, four coverage thresholds and SDK pins unchanged; pass isolated and full npm/Bun checks; demonstrate that the probe still detects a missing forced kill." +comments[1]{created_at,author,text}: + "2026-10-17T10:58:49.678Z",codex-agent,"Related startup-sensitive probe found during PR 142 import-fidelity validation: test/blockers.test.ts marker extraction includes cold package/SDK imports in its 10-second child budget. It passed isolated in 9.8 seconds but timed out under coverage in 10.3 seconds, including with Node 26. The PR separates finite module startup from the unchanged 10-second parser deadline and adds a real stalled-operation forced-kill control. The original 500 ms acceptance-watchdog startup case remains tracked here; its existing budgets and helper are unchanged." files[2]{path,scope}: test/bounded-cli.test.ts,project test/helpers/bounded-cli.ts,project diff --git a/.agents/pm/tasks/pm-github-aiok.toon b/.agents/pm/tasks/pm-github-aiok.toon index 775f9a9e..9420ecd8 100644 --- a/.agents/pm/tasks/pm-github-aiok.toon +++ b/.agents/pm/tasks/pm-github-aiok.toon @@ -6,12 +6,13 @@ status: open priority: 1 tags: [] created_at: "2026-10-05T09:23:42.980Z" -updated_at: "2026-10-05T09:32:54.787Z" +updated_at: "2026-10-17T00:39:26.413Z" author: codex-sol dependencies[1]{id,kind,created_at,author,source_kind,author_source}: pm-github-f1at,related,"2026-10-05T09:32:54.728Z",codex-sol,"cli:update:dep",detected -comments[1]{created_at,author,text}: +comments[2]{created_at,author,text}: "2026-10-05T09:25:31.280Z",codex-sol,"The caller example intentionally uses release commit and exact version placeholders. The currently published extension predates the gate, and this work is authorized only in the pm-github worktree. No pilot workflow in another repository was changed, enabled, or dispatched. Resolve the placeholders only after a reviewed release and separately authorized caller adoption." + "2026-10-17T00:39:26.413Z",codex-agent,"Additional release-adoption dependency from pm-github-c093: the default-branch pm-github-sync callers in pm-beads, pm-brief, pm-context, pm-csv, pm-linear and pm-slack-standup all pin workflow SHA 67a5e3e1514fddeeae29951da00513f06f7aa0ab and pm-github-version 2026.10.16. Regeneration with the import-fidelity fix requires adoption of its reviewed release in both pins; the next scheduled sync using that release can then regenerate the existing sync PRs. Keep rollout as separately authorized work; the import-fidelity task does not change or dispatch consumer workflows." files[2]{path,scope}: .github/workflows/pm-github-sync.yml,project docs/sync-workflow-caller.yml,project diff --git a/CHANGELOG.md b/CHANGELOG.md index ffeb2f88..7a818cf1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Added + +- Preserve imported GitHub lifetimes, closure evidence and release attribution while linking cross-provider items ([pm-github-c093](https://github.com/unbraind/pm-github/blob/main/.agents/pm/features/pm-github-c093.toon)) + ## 2026.10.16 - 2026-10-16 ### Added diff --git a/README.md b/README.md index b19f2caa..1cebfb4a 100644 --- a/README.md +++ b/README.md @@ -732,3 +732,36 @@ itself lists each affected stream in its output; `pm history --verify ` spot content, so `reconcile` only re-greens the hash chain (no data loss) — see the authoritative [pm-cli merge-safety guide](https://github.com/unbraind/pm-cli/blob/main/docs/MERGE_SAFETY.md). The older blunt `pm history-repair --all` remains available as a lower-level primitive. + +## What an imported issue looks like + +An issue owned by the GitHub importer keeps GitHub's creation and closure dates in the native lifecycle fields used by momentum and cycle-time readers. The import records a new, append-only before/after history event at the time of synchronization; it does not rewrite old events. The public SDK's high-level create/update commands reserve these dates, so finalization uses its public item lock, serialization and history primitives. + +```yaml +status: closed +created_at: "2026-09-02T10:00:00Z" +updated_at: "2026-10-16T12:00:00Z" +closed_at: "2026-09-04T12:00:00Z" +completed_at: "2026-09-04T12:00:00Z" +close_reason: 'Closed on GitHub as completed on 2026-09-04T12:00:00Z; closing PR https://github.com/example/project/pull/8 ("Restore the parser")' +release: v2026.9.5 +github_url: https://github.com/example/project/issues/7 +github_number: 7 +github_state: closed +github_state_reason: completed +github_created_at: "2026-09-02T10:00:00Z" +github_updated_at: "2026-09-04T12:00:00Z" +github_closed_at: "2026-09-04T12:00:00Z" +github_release: v2026.9.5 +github_link_only: false +``` + +`completed` maps to `closed`; `not_planned` and `duplicate` map to `canceled`. An open or reopened issue maps to `open` and clears old closure evidence and import-assigned release attribution. A closure reason names the closing commit from the REST timeline or closing PR/commit identified by GraphQL `ClosedEvent`, when available. A `ConnectedEvent` supplies a quoted linked PR without claiming that the association caused the closure. Otherwise it says `Closed on GitHub as on `. Closing a report alone does not establish that its described defect or privacy exposure was repaired. + +Release attribution uses the actual dates of locally available calendar tags, including annotated tag dates. A closed issue belongs to the first tag at or after its closure: before the first tag goes to that first release, between two tags goes to the second, and after the latest tag remains pending. Fetch the complete tag history before importing; the reusable sync workflow already does this. Regenerate with pm-changelog's `--respect-item-release` so a historical import stays in its declared release window. + +Before creating an item, import checks the complete tracker for its provenance tag, GitHub URLs in provenance or link metadata, and Linear/Jira/ADO links referenced by the GitHub issue body. A unique existing external owner receives the GitHub tag and source metadata; its title, body, provider identity, creation/completion dates and release remain intact. A persisted `github_link_only` marker preserves that ownership on subsequent syncs. Free-text descriptions and bodies do not declare GitHub ownership. Ambiguous matches refuse the import before writes with `--gate` or `--atomic`. A plain multi-issue import may write earlier issues before rejecting a later ambiguous match. + +With `--gate` and a nonempty filtered issue list, both preview and apply results include a `plan` with source dates, closure reason, release, target identity, link/create/update action and state-conflict decision. An empty filtered issue list returns without a `plan`, including with `--gate --dry-run`. GitHub wins lifecycle conflicts for GitHub-owned imports. A canceled item that GitHub later reports as completed uses the SDK's explicit repeat-closure transition with the source reason. A linked external owner keeps its local status, and the plan records `preserve-provider-owner`; `github_state` still records the source state. Atomic transaction identity includes the latest external edit or different lifecycle generation in history. Local state edits and repeated state cycles force reconciliation, while staging and compensation keep crash retries on the same journal. Finalization runs before the SDK commits and is compensated with the batch on failure. It is idempotent and runs again after a recovered atomic journal, so an interrupted finalization can be repaired by retrying the same import before the gate permits a push. + +SDK limitation: native `updated_at` remains the local mutation clock because the bulk SDK uses it to address rollback history. The GitHub update date is retained exactly in `github_updated_at`, and the gated plan discloses this policy. Backdating native `updated_at` safely requires an SDK import mutation whose version identity is independent of source time. Native creation, closure and completion dates still preserve GitHub lifetimes for momentum and cycle-time readers. diff --git a/import-fidelity.ts b/import-fidelity.ts new file mode 100644 index 00000000..dd2722c2 --- /dev/null +++ b/import-fidelity.ts @@ -0,0 +1,239 @@ +/** Source lifecycle, release-window and cross-provider identity rules for issue imports. */ +import { spawnSync } from "node:child_process"; +import { + acquireLock, appendHistoryEntry, canonicalDocument, createHistoryEntry, getHistoryPath, + locateItem, normalizeItemId, readLocatedItem, readSettings, resolveItemTypeRegistry, resolvePmRoot, runActiveOnWriteHooks, serializeItemDocument, writeFileAtomic, +} from "@unbrained/pm-cli/sdk"; +import { CommandError, fetchJSON, githubApiBase, githubGraphQL, parseNextLink, type GhIssue, type PmItem } from "./index.ts"; + +/** + * Select the latest external edit or different lifecycle generation in immutable history. + * Creation staging and compensation are transient; importing the desired state again + * keeps the selected revision stable, including after an interrupted governed update. + * @internal Shared by transaction planning and deterministic history-shape tests. + */ +export function selectImportContextIndex(entries: readonly { message?: string; patch: readonly { path: string; value?: unknown }[] }[], status: string): number { + let currentStatus: unknown; + let selected = -1; + for (const [index, event] of entries.entries()) { + for (const patch of event.patch) { + if (patch.path === "/metadata/status") currentStatus = patch.value; + } + const message = event.message ?? ""; + const owned = /^(?:pm-github import create |bulk-item-transaction github-import-|Compensate interrupted bulk transaction github-import-|Preserve GitHub source lifecycle for issue #\d+$)/.test(message); + const transient = /^(?:pm-github import create |Compensate interrupted bulk transaction github-import-)|^bulk-item-transaction github-import-.* compensate$/.test(message); + if (!owned || (currentStatus !== undefined && currentStatus !== status && !transient)) selected = index; + } + return selected; +} + +/** One locally available release tag and its actual Git date. */ +export interface ImportRelease { tag: string; date: string } +/** Auditable desired source metadata, persisted beside the native lifecycle. */ +export interface ImportFidelity { + github_url: string; + github_number: number; + github_state: string; + github_state_reason: string; + github_created_at: string; + github_updated_at: string; + github_closed_at: string; + github_close_reason: string; + github_release: string; +} +/** Identity decision recorded before any import writes. */ +export interface ImportMatch { item?: PmItem; linkOnly: boolean; via: string } + +/** Normalize only complete HTTP issue/provider locators, never substring identities. */ +function locators(value: unknown): string[] { + const text = typeof value === "string" ? value : JSON.stringify(value) ?? ""; + return [...text.matchAll(/https?:\/\/[^\s<>"'`]+/gi)].map(match => match[0]!.replace(/[),.;]+$/, "").replace(/\/$/, "").toLowerCase() + .replace(/^https?:\/\/(?:www\.)?github\.com\/([^/]+)\/([^/]+)\/(?:issues|pull)\/(\d+)\/?(?:[?#].*)?$/, "https://github.com/$1/$2/issues/$3")); +} + +/** Extract declared provider fields and the description/body markers used by provider exporters. */ +function providerProvenance(item: PmItem): string { + const identities = new Set(Object.entries(item).filter(([key, value]) => + /^(linear|jira|ado)_(?:.*_)?(?:id|key|url|identifier)$/i.test(key) && Boolean(value) && + (typeof value !== "string" || value.trim() !== "")).map(([key]) => key)); + const declared = identities.size > 0 || (item.github_link_only !== false && item.tags?.some(tag => /^(linear|jira|ado):/i.test(tag))) || + Object.entries(item).some(([key, value]) => /^(provider|source|provenance)$/i.test(key) && (typeof value === "string" + ? /^(?:pm-)?(?:linear|jira|ado)$/i.test(value.trim()) + : /"(?:provider|source|system)"\s*:\s*"(?:pm-)?(?:linear|jira|ado)"/i.test(JSON.stringify(value) ?? ""))); + return Object.entries(item).filter(([key, value]) => identities.has(key) || + (declared && /url|link|provenance|source|external/i.test(key)) || + ((/description|provenance/i.test(key) || item.github_link_only !== false) && /description|body|provenance/i.test(key) && typeof value === "string" && + /\[(linear|jira|ado)\]|\b(linear|jira|ado)_(id|key|url)=|Jira(?:\s+[A-Z][A-Z0-9]+-\d+)?:\s/i.test(value))) + .map(([, value]) => typeof value === "string" ? value : JSON.stringify(value)).join("\n"); +} + +/** Match GitHub locators or provider back-links before creation, refusing multiple owners. */ +export function matchImportedIssue(items: readonly PmItem[], issue: GhIssue, tagged?: PmItem): ImportMatch { + const issueUrl = locators(issue.html_url)[0]!; + const bodyUrls = new Set(locators(issue.body ?? "")); + const candidates = items.filter(item => item.id && (item.id === tagged?.id || + Object.entries(item).some(([key, value]) => /^(?:github_?url|provenance|external_?(?:links?|url)|links?)$/i.test(key) && locators(value).includes(issueUrl)) || + locators(providerProvenance(item)).some(url => bodyUrls.has(url)))); + if (candidates.length > 1) throw new CommandError(`GitHub issue #${issue.number} has multiple existing owners; import plan is not idempotent. No item was written for this issue.`); + const item = candidates[0]; + const provider = item && (Boolean(providerProvenance(item)) || (item.github_link_only !== false && item.tags?.some(tag => /^(linear|jira|ado):/i.test(tag)))); + return { item, linkOnly: Boolean(item && (provider || item.github_link_only === true)), + via: item ? (provider ? "provider-link" : item.id === tagged?.id ? "provenance-tag" : "github-url") : "new" }; +} + +/** Read calendar release tags from the selected tracker checkout, including annotated tag dates. */ +export function readImportReleases(pmRoot: string): ImportRelease[] { + const result = spawnSync("git", ["-C", pmRoot, "for-each-ref", "--format=%(refname:short)%09%(creatordate:iso-strict)", "refs/tags"], { encoding: "utf8" }); + if (result.status !== 0) return []; + return result.stdout.trim().split("\n").flatMap(line => { + const [tag, date] = line.split("\t"); + return /^v?\d{4}\.\d{1,2}\.\d{1,2}$/.test(tag!) && Number.isFinite(Date.parse(date!)) ? [{ tag: tag!, date: date! }] : []; + }).sort((a, b) => Date.parse(a.date) - Date.parse(b.date) || a.tag.localeCompare(b.tag)); +} + +/** Select only the latest closure within GitHub's observed forward timestamp skew. */ +function latestClosure(events: readonly unknown[], closedAt: string | null | undefined, graphql = false): Record | undefined { + const candidates = events.filter((event): event is Record => event !== null && typeof event === "object" && !Array.isArray(event)) + .filter(event => graphql ? event.__typename === "ClosedEvent" : event.event === "closed"); + const key = graphql ? "createdAt" : "created_at"; + return candidates.filter(event => { + const delta = typeof event[key] === "string" && closedAt ? Date.parse(event[key]) - Date.parse(closedAt) : NaN; + return Number.isFinite(delta) && delta >= 0 && delta <= 5_000; + }).sort((left, right) => Date.parse(String(left[key])) - Date.parse(String(right[key]))).at(-1); +} + +/** Fetch every timeline page; only closure-specific events may supply resolution evidence. */ +export async function fetchImportTimeline(issue: GhIssue, repo: string, token?: string): Promise { + if (issue.state !== "closed") return []; + let url: string | undefined = `${githubApiBase()}/repos/${repo}/issues/${issue.number}/timeline?per_page=100`; + const events: unknown[] = []; + while (url) { + const response = await fetchJSON(url, token); + if (response.status !== 200) throw new CommandError(`Could not read closure timeline for issue #${issue.number} (HTTP ${response.status}).`); + let page: unknown; + try { page = JSON.parse(response.body); } + catch { throw new CommandError(`Closure timeline for issue #${issue.number} is not valid JSON.`); } + if (!Array.isArray(page)) throw new CommandError(`Invalid closure timeline for issue #${issue.number}.`); + events.push(...page); + url = parseNextLink(response.linkHeader); + } + const closed = latestClosure(events, issue.closed_at); + if (token && !(typeof closed?.commit_id === "string" && /^[a-f0-9]{40}$/i.test(closed.commit_id))) { + const [owner, name] = repo.split("/"); + let cursor: string | null = null; + do { + const data: { repository: { issue: { timelineItems: { nodes: Array>; pageInfo: { hasNextPage: boolean; endCursor: string } } } } } = await githubGraphQL(token, + `query($owner:String!,$name:String!,$number:Int!,$cursor:String) { repository(owner:$owner,name:$name) { issue: ${issue.pull_request ? "pullRequest" : "issue"}(number:$number) { timelineItems(first:100,after:$cursor,itemTypes:[CLOSED_EVENT,CONNECTED_EVENT]) { nodes { __typename ... on ClosedEvent { createdAt closer { __typename ... on PullRequest { url title } ... on Commit { url messageHeadline oid } } } ... on ConnectedEvent { createdAt subject { __typename ... on PullRequest { url title } } } } pageInfo { hasNextPage endCursor } } } } }`, + { owner, name, number: issue.number, cursor }); + const connection = data.repository?.issue?.timelineItems; + if (!connection || !Array.isArray(connection.nodes) || typeof connection.pageInfo?.hasNextPage !== "boolean") { + throw new CommandError(`Invalid GraphQL closure timeline for issue #${issue.number}.`); + } + events.push(...connection.nodes); + cursor = connection.pageInfo.hasNextPage ? connection.pageInfo.endCursor : null; + } while (cursor); + } + return events; +} + +/** Accept only GitHub PR/commit evidence tied to the latest closure, excluding pre-reopen events. */ +export function importCloseReason(issue: GhIssue, events: readonly unknown[]): string { + const reason = issue.state_reason ?? "unspecified"; + const base = `Closed on GitHub as ${reason} on ${issue.closed_at ?? "an unknown date"}`; + const records = events.filter((event): event is Record => event !== null && typeof event === "object" && !Array.isArray(event)); + const closed = latestClosure(records, issue.closed_at); + if (closed && typeof closed.commit_id === "string" && /^[a-f0-9]{40}$/i.test(closed.commit_id)) { + const linkedCommit = typeof closed.commit_url === "string" + ? /^https:\/\/api\.github\.com\/repos\/([^/]+)\/([^/]+)\/commits\/([a-f0-9]{40})$/.exec(closed.commit_url) : null; + const repoUrl = linkedCommit && linkedCommit[3] === closed.commit_id + ? `https://github.com/${linkedCommit[1]}/${linkedCommit[2]}` : issue.html_url.replace(/\/(?:issues|pull)\/\d+$/, ""); + return `${base}; closing commit ${repoUrl}/commit/${closed.commit_id}`; + } + const graphClosed = latestClosure(records, issue.closed_at, true); + const graphEvents = [ + ...(graphClosed ? [graphClosed] : []), + ...records.filter(event => event.__typename === "ConnectedEvent").reverse(), + ]; + for (const event of graphEvents) { + const closer = event.__typename === "ClosedEvent" ? event.closer : event.subject; + if (closer && typeof closer === "object" && "url" in closer && typeof closer.url === "string" && /^https:\/\/github\.com\/[^/]+\/[^/]+\/(pull\/\d+|commit\/[a-f0-9]{40})$/.test(closer.url)) { + const title = "title" in closer ? closer.title : "messageHeadline" in closer ? closer.messageHeadline : undefined; + return `${base}; ${event.__typename === "ClosedEvent" ? "closing" : "linked"} ${closer.url.includes("/pull/") ? "PR" : "commit"} ${closer.url}${typeof title === "string" ? ` (${JSON.stringify(title)})` : ""}`; + } + } + const connected = records.find(event => event.event === "connected" && typeof event.url === "string" && /^https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+$/.test(event.url)); + return connected ? `${base}; linked PR ${connected.url}` : base; +} + +/** Validate source lifetimes and assign the earliest release tag at or after the closure. */ +export function buildImportFidelity(issue: GhIssue, releases: readonly ImportRelease[], timeline: readonly unknown[]): ImportFidelity { + const created = Date.parse(issue.created_at); + const updated = Date.parse(issue.updated_at); + const closed = issue.closed_at ? Date.parse(issue.closed_at) : undefined; + if (!Number.isFinite(created) || !Number.isFinite(updated) || updated < created || + (closed !== undefined && (!Number.isFinite(closed) || closed < created || closed > updated))) { + throw new CommandError(`GitHub issue #${issue.number} has an invalid source lifetime; nothing may be imported.`); + } + return { + github_url: issue.html_url, github_number: issue.number, github_state: issue.state, + github_state_reason: issue.state_reason ?? "unspecified", + github_created_at: issue.created_at, github_updated_at: issue.updated_at, + github_closed_at: issue.state === "closed" ? issue.closed_at ?? "" : "", + github_close_reason: issue.state === "closed" ? importCloseReason(issue, timeline) : "", + github_release: issue.state === "closed" && closed !== undefined ? releases.find(release => Date.parse(release.date) >= closed)?.tag ?? "" : "", + }; +} + +/** + * Finalize source metadata through public SDK lock, serialization and append-only history primitives. + * High-level create/update reserve native timestamps. This import adapter appends a real + * before/after event instead of editing past history; retrying identical metadata is a no-op. + * Cross-provider owners retain their creation/completion dates, content and release. + */ +export async function persistImportFidelity(pmRoot: string, id: string, fidelity: ImportFidelity, linkOnly: boolean): Promise { + pmRoot = resolvePmRoot(process.cwd(), pmRoot); + const settings = await readSettings(pmRoot); + id = normalizeItemId(id, settings.id_prefix); + const unlock = await acquireLock(pmRoot, id, settings.locks.ttl_seconds, "pm-github", false, + settings.governance.force_required_for_stale_lock, settings.locks.wait_ms); + try { + const located = await locateItem(pmRoot, id, settings.id_prefix, settings.item_format, resolveItemTypeRegistry(settings).type_to_folder); + if (!located) throw new CommandError(`Imported item ${id} is missing during lifecycle finalization.`); + const { document, raw } = await readLocatedItem(located); + const before = canonicalDocument(document); + Object.assign(document.metadata, fidelity); + document.metadata.github_link_only = linkOnly; + if (!linkOnly) { + document.metadata.created_at = fidelity.github_created_at; + if (fidelity.github_state === "closed") document.metadata.close_reason = fidelity.github_close_reason; + if (fidelity.github_closed_at) { + document.metadata.closed_at = fidelity.github_closed_at; + document.metadata.completed_at = fidelity.github_closed_at; + } else if (fidelity.github_state === "open") { + delete document.metadata.closed_at; + delete document.metadata.completed_at; + delete document.metadata.close_reason; + } + if (fidelity.github_release) document.metadata.release = fidelity.github_release; + else delete document.metadata.release; + } + if (document.metadata.closed_at && Date.parse(document.metadata.closed_at) < Date.parse(document.metadata.created_at)) { + throw new CommandError(`GitHub issue #${fidelity.github_number} needs a source closure date before its lifecycle can be imported safely.`); + } + if (JSON.stringify(before) === JSON.stringify(canonicalDocument(document))) return; + // SDK bulk rollback addresses history by native updated_at. Keep that clock + // equal to the appended event timestamp; source time lives in github_updated_at. + const now = new Date().toISOString(); + document.metadata.updated_at = now; + const after = canonicalDocument(document); + const history = createHistoryEntry({ nowIso: now, author: "pm-github", op: "import", before, after, + message: `Preserve GitHub source lifecycle for issue #${fidelity.github_number}` }); + await writeFileAtomic(located.itemPath, serializeItemDocument(after, { format: located.item_format })); + try { await appendHistoryEntry(getHistoryPath(pmRoot, id), history); } + catch (error) { await writeFileAtomic(located.itemPath, raw); throw error; } + const hookContext = { scope: "project" as const, item_id: id, item_type: after.metadata.type, + before, after, changed_fields: history.patch.map(patch => patch.path) }; + await runActiveOnWriteHooks({ ...hookContext, path: located.itemPath, op: "import" }); + await runActiveOnWriteHooks({ ...hookContext, path: getHistoryPath(pmRoot, id), op: "import:history" }); + } finally { await unlock(); } +} diff --git a/index.ts b/index.ts index 6fdad97f..219e9fc0 100644 --- a/index.ts +++ b/index.ts @@ -24,6 +24,10 @@ import type { SearchProviderQueryContext, } from "@unbrained/pm-cli/sdk/authoring"; import { CloseoutUsageError, compactCloseoutReport, runCloseout } from "./closeout.ts"; +import { + buildImportFidelity, fetchImportTimeline, matchImportedIssue, persistImportFidelity, readImportReleases, selectImportContextIndex, + type ImportFidelity, +} from "./import-fidelity.ts"; import http from "node:http"; import https from "node:https"; import crypto from "node:crypto"; @@ -40,6 +44,9 @@ import { listAllItemMetadata as sdkListAllItemMetadata, normalizeItemId as sdkNormalizeItemId, readSettings as sdkReadSettings, + resolvePmRoot as sdkResolvePmRoot, + getHistoryPath as sdkGetHistoryPath, + readHistorySnapshot as sdkReadHistorySnapshot, type BulkItemMutation, type CommentsCommandOptions, type CommentsResult, @@ -102,7 +109,7 @@ export interface GhIssue { /** GitHub lifecycle state; mapped to a pm status by {@link mapState}. */ state: "open" | "closed"; /** Why a closed issue was closed; `not_planned` maps to `canceled`, not `closed`. */ - state_reason?: "completed" | "not_planned" | "reopened" | null; + state_reason?: "completed" | "not_planned" | "duplicate" | "reopened" | null; /** Labels on the issue, imported verbatim as pm tags (before any `--label-map`). */ labels: Array<{ name: string }>; /** Author login, surfaced as a `github_author:` tag when present. */ @@ -212,6 +219,8 @@ type ReadSettings = (pmRoot: string) => Promise<{ id_prefix?: string }>; export interface AtomicImportOptions { /** Author identity stamped onto the transaction's mutations (default `pm-github`). */ atomicAuthor?: string; + /** SDK transaction observer; explicit failure injection still exercises real rollback. */ + onTransition?: CommitItemMutationsOptions["onTransition"]; /** SDK bulk-mutation primitive that applies and journals the transaction. */ commitItemMutations?: CommitItemMutations; /** SDK id normalizer that derives each item's stable external-key id. */ @@ -227,6 +236,7 @@ export interface ImportRunDependencies { opts: ImportOptions, token?: string, ) => Promise; + fetchIssueTimeline?: typeof fetchImportTimeline; fetchIssueComments?: ( issue: GhIssue, repo: string, @@ -633,7 +643,7 @@ export function parseNextLink(linkHeader?: string): string | undefined { // Map a GitHub issue/PR state (+ optional stateReason) onto a pm status, // preserving `not_planned` closures as `canceled` rather than `closed`. export function mapState(state: string, stateReason?: string | null): string { - if (state === "closed" && stateReason === "not_planned") return "canceled"; + if (state === "closed" && (stateReason === "not_planned" || stateReason === "duplicate")) return "canceled"; return state === "closed" ? "closed" : "open"; } @@ -851,6 +861,8 @@ export function authorTag(issue: GhIssue): string | undefined { * depending on every SDK field. */ export interface PmItem { + /** Full proven-complete metadata, including provider links and source lifecycle. */ + [key: string]: unknown; /** Stable item id (absent for items not yet created). */ id?: string; /** One-line title. */ @@ -1097,6 +1109,7 @@ export function decodeCompletePmItems(parsed: unknown): PmItem[] { } ids.add(item.id); items.push({ + ...item, id: item.id, title, status, @@ -1295,8 +1308,14 @@ export interface PreparedGithubImport { milestone?: string; comments: GhComment[]; syncAnnotations: boolean; - /** Source completion timestamp (GitHub `closed_at`), forwarded as `--completed-at` when the item is closed. */ + /** Source completion timestamp; full issue plans finalize it together with source creation. */ closedAt?: string; + /** Source dates, closure evidence and tag-date attribution in the verified plan. */ + fidelity?: ImportFidelity; + /** Existing external owner receives a link without replacing its provider context. */ + linkOnly?: boolean; + /** Auditable identity and lifecycle conflict decision. */ + identity?: { via: string; conflict?: { local: string; github: string; rule: string } }; match?: PmItem; } @@ -1364,11 +1383,13 @@ async function resolveAtomicSdkFunctions(opts: AtomicImportOptions): Promise<{ * Derive an order-independent transaction id from the desired import state and * exact ordered mutation plan. Content or target changes produce a fresh * transaction; a reordered retry of the same plan resumes the durable journal. + * External history revisions distinguish a local edit from a retry of import-owned writes. */ export function deriveAtomicTransactionId( repo: string, entries: readonly PreparedGithubImport[], mutations: readonly BulkItemMutation[], + contextVersions: readonly string[] = [], ): string { const canonical = [...entries] .sort((a, b) => a.issueNumber - b.issueNumber) @@ -1382,8 +1403,10 @@ export function deriveAtomicTransactionId( tags: [...entry.tags].sort(), assignee: entry.assignee ?? null, milestone: entry.milestone ?? null, + fidelity: entry.fidelity ?? null, + linkOnly: entry.linkOnly ?? false, })); - const digest = crypto + const payload = crypto .createHash("sha256") .update(repo.toLowerCase()) .update("\x1f") @@ -1392,10 +1415,9 @@ export function deriveAtomicTransactionId( // Recovery requires the exact ordered step plan. Include targets and // options so a changed id_prefix or provenance match gets a fresh journal // instead of colliding with an incompatible prior attempt. - .update(JSON.stringify(mutations)) - .digest("hex") - .slice(0, 16); - return `${ATOMIC_IMPORT_PREFIX}${digest}`; + .update(JSON.stringify(mutations)); + if (contextVersions.length > 0) payload.update("\x1f").update(JSON.stringify([...contextVersions].sort())); + return `${ATOMIC_IMPORT_PREFIX}${payload.digest("hex").slice(0, 16)}`; } /** Stable create id keyed by the external GitHub issue, never by fetch order. */ @@ -1420,6 +1442,9 @@ export function buildAtomicImportMutations( idPrefix: string, normalizeItemId: (input: string, prefix: string) => string, ): { itemId: string; mutations: BulkItemMutation[] } { + if (entry.linkOnly && entry.match?.id) { + return { itemId: entry.match.id, mutations: [{ op: "update", id: entry.match.id, options: { addTags: entry.tags } }] }; + } const sharedOptions: Record = { title: entry.title, type: entry.itemType, @@ -1428,8 +1453,15 @@ export function buildAtomicImportMutations( tags: entry.tags.join(","), ...(entry.assignee ? { assignee: entry.assignee } : {}), ...(entry.milestone ? { sprint: entry.milestone } : {}), + ...(entry.status === "canceled" ? { closeReason: entry.fidelity?.github_close_reason ?? `GitHub issue #${entry.issueNumber} closed` } : {}), }; + // Explicit terminal reconciliation also handles canceled -> completed. The + // supported force option permits that source-authoritative repeat closure; + // finalization restores the real dates before the transaction commits. + const statusOptions = entry.status === "closed" + ? entry.fidelity ? { status: "closed", closeReason: entry.fidelity.github_close_reason, force: true } : {} + : { status: entry.status }; const managedItemId = deriveAtomicItemId( repo, entry.issueNumber, @@ -1448,21 +1480,21 @@ export function buildAtomicImportMutations( const mutations: BulkItemMutation[] = [{ op: "create", id: managedItemId, - options: { ...sharedOptions, status: createStatus }, + options: { ...sharedOptions, status: createStatus, message: `pm-github import create ${repo.toLowerCase()}#${entry.issueNumber}` }, }, { op: "update", id: managedItemId, options: { ...sharedOptions, - ...(entry.status !== "closed" ? { status: entry.status } : {}), + ...statusOptions, }, }]; - if (entry.status === "closed") { + if (entry.status === "closed" && !entry.fidelity) { mutations.push({ op: "close", id: managedItemId, reason: `GitHub issue #${entry.issueNumber} closed`, - // Preserve the source's real completion time instead of the import time. + // Legacy callers can supply completion directly; full plans finalize both lifecycle dates. ...(entry.closedAt ? { options: { completedAt: entry.closedAt } } : {}), }); } @@ -1473,29 +1505,40 @@ export function buildAtomicImportMutations( } const itemId = entry.match.id; - const updateOptions: Record = { ...sharedOptions }; - // close has a dedicated mutation so its reason is preserved. Every other - // transition (open/reopen/canceled) is safely reversible as part of update. - if (entry.status !== "closed") { - updateOptions.status = entry.status; - } + const updateOptions: Record = { ...sharedOptions, ...statusOptions }; const mutations: BulkItemMutation[] = [{ op: "update", id: itemId, options: updateOptions, }]; - if (entry.status === "closed") { + if (entry.status === "closed" && !entry.fidelity) { mutations.push({ op: "close", id: itemId, reason: `GitHub issue #${entry.issueNumber} closed`, - // Preserve the source's real completion time instead of the import time. + // Legacy callers can supply completion directly; full plans finalize both lifecycle dates. ...(entry.closedAt ? { options: { completedAt: entry.closedAt } } : {}), }); } return { itemId, mutations }; } +/** + * Bind atomic plans to the latest external edit or different lifecycle generation. + * The importer's desired-state and compensation writes leave retries stable; + * local edits and later state cycles invalidate completed journals. + */ +async function importContextVersions(pmRoot: string, entries: readonly PreparedGithubImport[]): Promise { + const versions: string[] = []; + for (const entry of entries) { + if (!entry.fidelity || !entry.match?.id) continue; + const snapshot = await sdkReadHistorySnapshot(sdkGetHistoryPath(pmRoot, entry.match.id), entry.match.id); + const context = snapshot.entries[selectImportContextIndex(snapshot.entries, entry.status)]; + if (context) versions.push(`${entry.match.id}:${crypto.createHash("sha256").update(JSON.stringify(context)).digest("hex")}`); + } + return versions; +} + /** Commit a complete issue-import batch under one crash-resumable transaction. */ export async function importGithubAtomic( pmRoot: string, @@ -1510,6 +1553,7 @@ export async function importGithubAtomic( recoveredItems?: number; itemIds: Map; }> { + pmRoot = sdkResolvePmRoot(process.cwd(), pmRoot); const { commitItemMutations: commit, normalizeItemId, @@ -1535,8 +1579,15 @@ export async function importGithubAtomic( itemIds.set(entry.issueNumber, planned.itemId); mutations.push(...planned.mutations); } - const transactionId = deriveAtomicTransactionId(repo, entries, mutations); + const transactionId = deriveAtomicTransactionId(repo, entries, mutations, await importContextVersions(pmRoot, entries)); + /** Finalize before the SDK commits, and repair source metadata on recovered journals. */ + const finalize = async (): Promise => { + for (const entry of entries) { + if (entry.fidelity) await persistImportFidelity(pmRoot, itemIds.get(entry.issueNumber)!, entry.fidelity, Boolean(entry.linkOnly)); + } + }; + let committed = false; try { const result = await commit({ pmRoot, @@ -1548,7 +1599,13 @@ export async function importGithubAtomic( // restores every UPDATE and CLOSE step (covered by the mixed rollback // integration test below this implementation). createCompensation: "delete", + onTransition: async context => { + if (context.transition === "committing") await finalize(); + await opts.onTransition?.(context); + }, }); + committed = true; + await finalize(); // A recovered journal may include work applied by the interrupted process // as well as steps resumed now. The SDK intentionally returns the durable // final results, not a per-invocation delta, so create/update counts cannot @@ -1564,6 +1621,7 @@ export async function importGithubAtomic( }; } catch (err: unknown) { const msg = errorMessage(err); + if (committed) throw new CommandError(`Atomic GitHub import has a committed journal but source metadata could not be finalized. Retry transaction ${transactionId}; nothing may be pushed. Underlying error: ${msg}`); if (err instanceof AggregateError || /compensation failed/i.test(msg)) { throw new CommandError( `Atomic GitHub import failed and compensation was incomplete. The tracker may contain partially applied state; retry the same import to resume transaction ${transactionId}, then inspect its durable journal if recovery still fails. Underlying error: ${msg}`, @@ -2799,10 +2857,18 @@ async function prepareGithubImport( token: string | undefined, match: PmItem | undefined, fetchIssueComments: ImportRunDependencies["fetchIssueComments"] = fetchComments, + fidelityContext: { items: PmItem[]; releases: ReturnType; timeline: typeof fetchImportTimeline }, ): Promise { const title = issue.title.trim(); if (!title) return undefined; + const identity = matchImportedIssue(fidelityContext.items, issue, match); + match = identity.item; + const timeline = await fidelityContext.timeline(issue, repo, token); + const fidelity = buildImportFidelity(issue, fidelityContext.releases, timeline); + const targetStatus = mapState(issue.state, issue.state_reason); + const conflict = match?.status && match.status !== targetStatus && (["closed", "canceled"].includes(match.status) || issue.state === "closed") + ? { local: match.status, github: issue.state, rule: identity.linkOnly ? "preserve-provider-owner" : "github-source-wins" } : undefined; const kind = issue.pull_request ? "PR" : "issue"; const labels = issue.labels.map((label) => label.name).filter(Boolean); const tag = provenanceTag(repo, issue.number); @@ -2827,20 +2893,22 @@ async function prepareGithubImport( issueNumber: issue.number, title, itemType: opts.itemType, - status: mapState(issue.state, issue.state_reason), + status: identity.linkOnly ? match!.status! : targetStatus, description: `GH ${kind} #${issue.number}: ${issue.html_url}` + (author ? ` · author @${author}` : "") + (issue.state_reason ? ` · state reason ${issue.state_reason}` : "") + - (issue.created_at ? ` · created ${issue.created_at}` : "") + - (issue.updated_at ? ` · updated ${issue.updated_at}` : ""), + ` · created ${issue.created_at} · updated ${issue.updated_at}`, body: writeCommentsToBody ? composeBody(issue, comments) : (issue.body || ""), - tags: [...labels, tag, ...(ghAuthorTag ? [ghAuthorTag] : [])], + tags: [...new Set([...(identity.linkOnly ? match!.tags! : []), ...labels, tag, ...(ghAuthorTag ? [ghAuthorTag] : [])])], assignee: issue.assignee?.login, milestone: issue.milestone?.title, comments, syncAnnotations, closedAt: issue.closed_at ?? undefined, + fidelity, + linkOnly: identity.linkOnly, + identity: { via: identity.via, ...(conflict ? { conflict } : {}) }, match, }; } @@ -3138,8 +3206,16 @@ export async function runImport( // reported "would import N, skip 0" where the real run performs updates for // already-linked issues. A preview that overstates creates reads as "this will // duplicate my whole tracker" and is the one thing --dry-run exists to rule out. - const existingItems = (dependencies.readItems ?? readPmItems)(pmRoot); + /** Read the complete raw corpus in-process, retaining all provider metadata and bodies. */ + const readImportItems = async (): Promise => { + if (dependencies.readItems) return dependencies.readItems(pmRoot); + const corpus = certifyCompleteListResult(await listAllComplete({ includeBody: true }, { pmRoot, cwd: pmRoot, noExtensions: true })); + return decodeCompletePmItems(corpus); + }; + const existingItems = await readImportItems(); const existing = indexByProvenance(existingItems); + const fidelityContext = { items: existingItems, releases: readImportReleases(pmRoot), + timeline: dependencies.fetchIssueTimeline ?? (dependencies.fetchIssues ? async () => [] : fetchImportTimeline) }; let imported = 0; let updated = 0; @@ -3169,6 +3245,7 @@ export async function runImport( token, existing.get(`${repo.toLowerCase()}#${issue.number}`), dependencies.fetchIssueComments, + fidelityContext, ); if (!entry) { skippedNumbers.push(issue.number); @@ -3190,7 +3267,7 @@ export async function runImport( if (opts.gate && !opts.dryRun) { const preview = runCommandTrackerGate({ pmRoot, plannedItems: planned.map(entry => ({ itemId: entry.match?.id ?? `github-issue-${entry.issueNumber}`, - fields: { title: entry.title, description: entry.description, body: entry.body, tags: entry.tags, + fields: { ...entry.fidelity, title: entry.title, description: entry.description, body: entry.body, tags: entry.tags, assignee: entry.assignee, sprint: entry.milestone, comments: entry.syncAnnotations ? entry.comments.map(comment => ({ author: comment.user?.login, text: comment.body, created_at: comment.created_at, @@ -3204,7 +3281,21 @@ export async function runImport( ); } - const gatePlanFields = opts.gate ? { gate: { completeness: gateCompleteness, idempotency: gateIdempotency, scan: "post-write" } } : {}; + // Preview output omits unscanned PR-title quotes and private provider close text. + const fidelityPlan = () => gatePlan!.map(entry => { + const source = entry.fidelity!; + const reason = source.github_close_reason.split(" (", 1)[0]!; + return { issue: entry.issueNumber, item: entry.match?.id, + action: entry.linkOnly ? "link" : entry.match?.id ? "update" : "create", status: entry.status, + ...source, github_close_reason: reason, + created_at: entry.linkOnly ? entry.match!.created_at : source.github_created_at, + updated_at_policy: "sdk-mutation-clock; source in github_updated_at", + closed_at: entry.linkOnly ? entry.match!.closed_at : source.github_closed_at, + close_reason: entry.linkOnly ? "preserved-provider-owner" : reason, + release: entry.linkOnly ? "preserved-provider-owner" : source.github_release, + identity: entry.identity }; + }); + const gatePlanFields = opts.gate ? { plan: fidelityPlan(), gate: { completeness: gateCompleteness, idempotency: gateIdempotency, scan: "post-write" } } : {}; if (opts.atomic) { // The gated pipeline already prepared every entry (and counted its skips) @@ -3259,7 +3350,7 @@ export async function runImport( } const result = await (dependencies.commitAtomic ?? importGithubAtomic)(pmRoot, repo, prepared); - if (opts.gate) verifyImportedProvenance((dependencies.readItems ?? readPmItems)(pmRoot), prepared, repo); + if (opts.gate) verifyImportedProvenance(await readImportItems(), prepared, repo); // Planned native comments are part of the verified plan: a contended lock, // a refused add, or a committed entry whose created id is missing from the // post-commit routing map all leave planned comments unwritten, so they are @@ -3308,6 +3399,7 @@ export async function runImport( atomic: true, ...depLinkResultFields(atomicDepLink), ...fetchFields, + ...(opts.gate ? { plan: fidelityPlan() } : {}), ...(gateReceipt ? { gate: gateReceipt } : {}), }; } @@ -3325,6 +3417,7 @@ export async function runImport( token, existing.get(`${repo.toLowerCase()}#${issue.number}`), dependencies.fetchIssueComments, + fidelityContext, ); if (!prepared) { // A gated run counted its skips during the verified plan phase; only the @@ -3345,7 +3438,6 @@ export async function runImport( tags, comments, syncAnnotations, - closedAt, } = prepared; /** @@ -3354,14 +3446,12 @@ export async function runImport( * Two paths below close an item for the same reason — reconciling an * already-matched item whose upstream issue is now closed, and closing an * item that was just created `open` because pm-cli 2026.8.3 refuses a - * terminal `create --status closed`. Both must carry identical provenance - * (the reason) and identical completion evidence (GitHub's own `closed_at`, - * when it recorded one), so the argv is built once here rather than - * duplicated at each site where the two could silently drift apart. + * terminal `create --status closed`. Both carry the same source reason. + * Source completion is applied with source creation during finalization, + * avoiding an intermediate completion date before the SDK creation date. */ const githubCloseArgs = (id: string): string[] => { - const args = ["--path", pmRoot, "close", id, "--reason", `GitHub issue #${issue.number} closed`]; - if (closedAt) args.push("--completed-at", closedAt); + const args = ["--path", pmRoot, "close", id, "--reason", prepared.fidelity!.github_close_reason]; return args; }; @@ -3374,6 +3464,14 @@ export async function runImport( continue; } + if (prepared.linkOnly) { + const ownerId = match!.id!; + const linked = pmRun(["--path", pmRoot, "update", ownerId, "--add-tags", tags.join(",")]); + if (!linked.ok) throw new CommandError(`Could not link GitHub issue #${issue.number} to its existing owner.`); + await persistImportFidelity(pmRoot, ownerId, prepared.fidelity!, true); + updated++; + continue; + } if (match?.id) { // Idempotent update — never duplicate. Status transitions go through the // proper command (close requires a reason; reopen via update --status). @@ -3394,16 +3492,20 @@ export async function runImport( continue; } // Reconcile status separately. + if (status === "canceled" && match.status !== "canceled") { + const cancel = pmRun(["--path", pmRoot, "update", match.id, "--status", "canceled", "--close-reason", prepared.fidelity!.github_close_reason]); + if (!cancel.ok) throw new CommandError(`Could not cancel GitHub issue #${issue.number}.`); + } if (status === "closed" && match.status !== "closed") { // Close through `pm close` so the reason is real provenance; pass the // source completion time as --completed-at when GitHub recorded one. - const close = pmRun(githubCloseArgs(match.id)); + const close = pmRun([...githubCloseArgs(match.id), ...(match.status === "canceled" ? ["--force"] : [])]); if (!close.ok) { console.error(`#${issue.number}: close reconciliation failed — ${close.stderr}`); skipped++; continue; } - } else if (status === "open" && match.status === "closed") { + } else if (status === "open" && (match.status === "closed" || match.status === "canceled")) { const reopen = pmRun(["--path", pmRoot, "update", match.id, "--status", "open", "--message", `GitHub issue #${issue.number} reopened`]); if (!reopen.ok) { console.error(`#${issue.number}: reopen reconciliation failed — ${reopen.stderr}`); @@ -3414,6 +3516,7 @@ export async function runImport( if (syncAnnotations) { commentWriteFailures += (await syncGithubCommentsToAnnotations(match.id, comments, pmRoot, issue.number)).failed; } + await persistImportFidelity(pmRoot, match.id, prepared.fidelity!, false); updated++; continue; } @@ -3434,18 +3537,19 @@ export async function runImport( "--tags", tags.join(","), "--message", `Imported from GitHub #${issue.number}`, ]; + if (status === "canceled") createArgs.push("--close-reason", prepared.fidelity!.github_close_reason); if (assignee) createArgs.push("--assignee", assignee); if (milestone) createArgs.push("--sprint", milestone); // --json lets a following close (or annotations sync) address the freshly // created item by id without re-scanning the workspace. - if (mustClose || syncAnnotations) createArgs.push("--json"); + createArgs.push("--json"); const created = pmRun(createArgs); if (!created.ok) { console.error(`#${issue.number}: create failed — ${created.stderr}`); skipped++; continue; } - const createdId = (mustClose || syncAnnotations) ? parseCreatedItemId(created.stdout) : undefined; + const createdId = parseCreatedItemId(created.stdout); if (mustClose) { if (createdId) { const close = pmRun(githubCloseArgs(createdId)); @@ -3475,6 +3579,8 @@ export async function runImport( commentWriteFailures += comments.length; } } + if (!createdId) throw new CommandError(`Could not finalize GitHub issue #${issue.number}: created id missing.`); + await persistImportFidelity(pmRoot, createdId, prepared.fidelity!, false); imported++; } @@ -3518,9 +3624,9 @@ export async function runImport( reportDepLink(depLink); // Gated pipeline, phase 2 — the writes are on disk but nothing is committed or // pushed; scan the proposed tracker change now and fail closed. - if (opts.gate) verifyImportedProvenance((dependencies.readItems ?? readPmItems)(pmRoot), gatePlan!, repo); + if (opts.gate) verifyImportedProvenance(await readImportItems(), gatePlan!, repo); const gateReceipt = opts.gate ? gateImportWrites(pmRoot) : undefined; - return { imported, updated, skipped, ...fetchFields, ...depLinkResultFields(depLink), ...(gateReceipt ? { gate: gateReceipt } : {}) }; + return { imported, updated, skipped, ...fetchFields, ...depLinkResultFields(depLink), ...(opts.gate ? { plan: fidelityPlan() } : {}), ...(gateReceipt ? { gate: gateReceipt } : {}) }; } /** @@ -5400,7 +5506,8 @@ interface GraphqlProjectFieldsData { // `errors` array, so we surface those explicitly. The combined user+org queries // below intentionally return a partial error for the wrong owner type while the // right one still resolves; we only throw when there is NO usable data. -async function githubGraphQL(token: string | undefined, query: string, variables: Record): Promise { +/** Execute a typed GraphQL request for internal import closure evidence. @internal */ +export async function githubGraphQL(token: string | undefined, query: string, variables: Record): Promise { return (await githubGraphQLResponse(token, query, variables)).data!; } @@ -6350,6 +6457,8 @@ export default defineExtension({ { name: "github_author", type: "string", optional: true }, { name: "github_created_at", type: "string", optional: true }, { name: "github_updated_at", type: "string", optional: true }, + { name: "github_link_only", type: "boolean", optional: true }, + ...["github_closed_at", "github_state_reason", "github_close_reason", "github_release"].map(name => ({ name, type: "string" as const, optional: true })), ]); // ----------------------------------------------------------------------- diff --git a/test/atomic.test.ts b/test/atomic.test.ts index 19dc0681..23e4571c 100644 --- a/test/atomic.test.ts +++ b/test/atomic.test.ts @@ -167,6 +167,11 @@ test("transaction identity is content-sensitive and independent of fetch order", id, "changed mutation targets cannot collide with an incompatible recovery journal", ); + assert.notEqual(deriveAtomicTransactionId("acme/widgets", [first, second], plan, ["owner:revision-1"]), + deriveAtomicTransactionId("acme/widgets", [first, second], plan, ["owner:revision-2"])); + assert.equal(deriveAtomicTransactionId("acme/widgets", [first, second], plan, ["one:1", "two:2"]), + deriveAtomicTransactionId("acme/widgets", [second, first], plan, ["two:2", "one:1"])); + }); test("atomic all-skipped imports fail without invoking the SDK", async () => { diff --git a/test/blockers.test.ts b/test/blockers.test.ts index ff8c3822..fa38bba6 100644 --- a/test/blockers.test.ts +++ b/test/blockers.test.ts @@ -830,15 +830,54 @@ test("partial-only alerts and unresolved bare references return complete reports }); -test("marker extraction finishes on repeated unclosed marker prefixes", () => { +/** Guard parser work after module readiness, with a separate finite startup limit. */ +function markerProbe(t: TestContext, operation: string, deadlineMs = 10_000): Promise<{ + status: number | null; signal: NodeJS.Signals | null; stdout: string; stderr: string; ready: boolean; timedOut: boolean; +}> { const script = `import { extractBlockerCommentMarkers } from './index.ts'; - const text = '