From 5b8c236f3e757f712d65a58bb00839d5f8eb5130 Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Fri, 16 Oct 2026 19:04:47 +0200 Subject: [PATCH 1/3] fix(closeout): refuse unverified required-check policies --- .agents/pm/history/pm-github-f1ev.jsonl | 22 ++++++++ .agents/pm/issues/pm-github-f1ev.toon | 35 ++++++++++++ .github/workflows/pm-github-closeout.yml | 16 ++++-- README.md | 42 ++++++++++----- closeout.ts | 37 ++++++++----- docs/closeout-workflow-caller.yml | 12 +++-- index.ts | 1 + test/closeout-workflow.test.ts | 38 ++++++++++++- test/closeout.test.ts | 68 ++++++++++++++++++------ 9 files changed, 220 insertions(+), 51 deletions(-) create mode 100644 .agents/pm/history/pm-github-f1ev.jsonl create mode 100644 .agents/pm/issues/pm-github-f1ev.toon diff --git a/.agents/pm/history/pm-github-f1ev.jsonl b/.agents/pm/history/pm-github-f1ev.jsonl new file mode 100644 index 00000000..520b4745 --- /dev/null +++ b/.agents/pm/history/pm-github-f1ev.jsonl @@ -0,0 +1,22 @@ +{"hash_algorithm":"sha256","ts":"2026-10-16T16:04:00.890Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e532c36ed40fc318cac03fae","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-f1ev"},{"op":"add","path":"/metadata/title","value":"Refuse closeout when required-check metadata is unverified"},{"op":"add","path":"/metadata/description","value":"Fix https://github.com/unbraind/pm-github/issues/138. Require complete protection metadata or explicit caller-declared contexts; harden caller permissions."},{"op":"add","path":"/metadata/type","value":"Issue"},{"op":"add","path":"/metadata/status","value":"open"},{"op":"add","path":"/metadata/priority","value":1},{"op":"add","path":"/metadata/tags","value":["closeout","validation"]},{"op":"add","path":"/metadata/created_at","value":"2026-10-16T16:04:00.890Z"},{"op":"add","path":"/metadata/updated_at","value":"2026-10-16T16:04:00.890Z"},{"op":"add","path":"/metadata/author","value":"codex-agent"},{"op":"add","path":"/metadata/acceptance_criteria","value":"Unavailable metadata without declarations never closes or publishes; declared contexts require success; metadata-backed behavior remains unchanged; npm and Bun release gates pass."}],"before_hash":"3cc22dff72be7b14824654a7a64ea62b04799939b2fee54c1b5f52ca60bf6df0","after_hash":"8a59cdf7c8f5aa748ad4d926036cf93b490dedc10cbf453b93effa548dc28929","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":"7fe69ec357b27d952e3a8de21050066fb7a029cdabb6e6f16012a56da48a45fe"} +{"hash_algorithm":"sha256","ts":"2026-10-16T16:04:10.258Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e532c36ed40fc318cac03fae","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-16T16:04:10.258Z"},{"op":"add","path":"/metadata/assignee","value":"codex-agent"},{"op":"add","path":"/metadata/claim_principal","value":"codex-agent"}],"before_hash":"8a59cdf7c8f5aa748ad4d926036cf93b490dedc10cbf453b93effa548dc28929","after_hash":"6980e4944bedadfec9082d3d7a3b873dfe11522b019dfd491d9beb45a9e12626","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":"5d4d41f1e0c264dec94360a66c9d3d84e63e1af6467c9182bf15e9d9b1bf419b"} +{"hash_algorithm":"sha256","ts":"2026-10-16T16:04:12.364Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e532c36ed40fc318cac03fae","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-16T16:04:12.364Z"},{"op":"replace","path":"/metadata/status","value":"in_progress"}],"before_hash":"6980e4944bedadfec9082d3d7a3b873dfe11522b019dfd491d9beb45a9e12626","after_hash":"2e9d29b5341200b1eda7f7c964c6ba92df21685dc117fc0c7401ea869d0c35d6","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":"5b01f5e28064d3f963a9f91666588fad9fd2e5ed0d7d2b71d4870e29716f8fcd"} +{"hash_algorithm":"sha256","ts":"2026-10-16T16:04:15.081Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e532c36ed40fc318cac03fae","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-16T16:04:15.081Z"},{"op":"add","path":"/metadata/comments","value":[{"created_at":"2026-10-16T16:04:15.081Z","author":"codex-agent","text":"Plan: distinguish unavailable protection metadata from a readable empty policy. Refuse with required_checks_unverified unless required-checks declares contexts; retain readable app-bound requirements. Exercise real command and workflow shell against injected HTTP evidence and real trackers. Keep workflow edits minimal around open PR #139."}]}],"before_hash":"2e9d29b5341200b1eda7f7c964c6ba92df21685dc117fc0c7401ea869d0c35d6","after_hash":"235d98302d306b4446e87b545d0cf9e9d3f83cf6df49fa384d4bb5bd0461cd65","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":"f8e4b7a50cd7e98fdc79c23c47ab67a821e373f3152311d83fd5bafbcaa380f6"} +{"hash_algorithm":"sha256","ts":"2026-10-16T16:06:39.639Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e532c36ed40fc318cac03fae","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-16T16:06:39.639Z"},{"op":"add","path":"/metadata/files","value":[{"path":".github/workflows/pm-github-closeout.yml","scope":"project"},{"path":"closeout.ts","scope":"project"},{"path":"index.ts","scope":"project"},{"path":"test/closeout-workflow.test.ts","scope":"project"},{"path":"test/closeout.test.ts","scope":"project"}]}],"before_hash":"235d98302d306b4446e87b545d0cf9e9d3f83cf6df49fa384d4bb5bd0461cd65","after_hash":"99cb9bf8723f9be466c70da32b44d5b366b8c68d6e18d4d2027de3d2b100e6a9","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":"e47b8becbe313a63896142a6f5bccc8c458917fcd2fd3c754fe0a3fff4b847e1"} +{"hash_algorithm":"sha256","ts":"2026-10-16T16:06:45.909Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e532c36ed40fc318cac03fae","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-16T16:06:45.909Z"},{"op":"add","path":"/metadata/docs","value":[{"path":"docs/closeout-workflow-caller.yml","scope":"project"},{"path":"README.md","scope":"project"}]}],"before_hash":"99cb9bf8723f9be466c70da32b44d5b366b8c68d6e18d4d2027de3d2b100e6a9","after_hash":"4839048ee250e05ba87aaeccc0f7c515538d10f4c9b7c8dc814de470aa73eb5c","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":"eb5ff08804692cdb8cec05a7c5c3b614474c7c8ae36147ffe47ad26cc9c98d3c"} +{"hash_algorithm":"sha256","ts":"2026-10-16T16:06:50.000Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e532c36ed40fc318cac03fae","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-16T16:06:50.000Z"},{"op":"add","path":"/metadata/tests","value":[{"command":"npm run build && npm run build:test && node --test test/closeout.test.ts test/closeout-workflow.test.ts","scope":"project","timeout_seconds":1800,"provenance":{"author":"codex-agent","created_at":"2026-10-16T16:06:49.922Z","source_kind":"local_mutation","source_ref":"fix/closeout-required-checks-unverified"}}]}],"before_hash":"4839048ee250e05ba87aaeccc0f7c515538d10f4c9b7c8dc814de470aa73eb5c","after_hash":"1bcad5aedc7a47d71211ab0e5fac2728b705ff9a5e8e65547adb75c030da8e3e","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":"5b31f186680470ea2d4f66af24e4a2219cc3874bbbdb408fa8c3ba2542fd9e99"} +{"hash_algorithm":"sha256","ts":"2026-10-16T16:08:21.292Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e532c36ed40fc318cac03fae","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-16T16:08:21.292Z","author":"codex-agent","text":"Implementation distinguishes metadata gaps from a readable empty policy. required_checks_unverified blocks workflow publication; required-checks verifies declared contexts plus readable app-bound requirements. Regression coverage uses actual workflow bash, a packed extension, injected HTTP APIs, real SDK tracker mutations and an isolated Git remote. Caller write scopes now belong only to gated-closeout."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-16T16:08:21.292Z"}],"before_hash":"1bcad5aedc7a47d71211ab0e5fac2728b705ff9a5e8e65547adb75c030da8e3e","after_hash":"ec44bc6423b950a7bded523297563684d527d62813c5ec39110b31007c16f359","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":"2c9a9646564c03c1786159a2c975c677b003e5444621e33e632c65b18896d9cc"} +{"hash_algorithm":"sha256","ts":"2026-10-16T16:10:06.013Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e532c36ed40fc318cac03fae","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-16T16:10:06.013Z","author":"codex-agent","text":"Behavioural revert proof: restored only checkCloseoutHead to its previous implementation (retaining the added argument for compilation), rebuilt dist, and ran node --test --test-name-pattern='required-check metadata boundary (undeclared|missing) ' test/closeout-workflow.test.ts. Both tests failed: each observed closed=1 where closed=0 was required (0 passed, 2 failed). Fixed function restored and rebuilt. This exercises the packed command and real workflow rather than SDK mocks."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-16T16:10:06.013Z"}],"before_hash":"ec44bc6423b950a7bded523297563684d527d62813c5ec39110b31007c16f359","after_hash":"2aa864abc32438faae1ee0908dfbfb2f47baf1c4261888b0f319041a0c40826d","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":"c089383fe3ca6847fdd37f88883cc62d063e5588d04dd2fd9e19e9b035696065"} +{"hash_algorithm":"sha256","ts":"2026-10-16T16:10:32.730Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e532c36ed40fc318cac03fae","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"tests_remove","patch":[{"op":"remove","path":"/metadata/tests"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-16T16:10:32.730Z"}],"before_hash":"2aa864abc32438faae1ee0908dfbfb2f47baf1c4261888b0f319041a0c40826d","after_hash":"57ed5b667cbb2b9974ccaef871e887ed669a9f2acb1d6637395983ef84fc173e","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":"5778a77b9ec1b516d04171cbb701eaecab84fb70655fcf195f018d8f4c8f2c60"} +{"hash_algorithm":"sha256","ts":"2026-10-16T16:10:43.136Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e532c36ed40fc318cac03fae","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-16T16:10:43.136Z"},{"op":"add","path":"/metadata/tests","value":[{"command":"npm run build && npm run build:test && node --test --test-name-pattern=\"required-check metadata boundary|declared contexts|declared names\" test/closeout.test.ts test/closeout-workflow.test.ts","scope":"project","timeout_seconds":1800,"provenance":{"author":"codex-agent","created_at":"2026-10-16T16:10:42.723Z","source_kind":"local_mutation","source_ref":"fix/closeout-required-checks-unverified"}}]}],"before_hash":"57ed5b667cbb2b9974ccaef871e887ed669a9f2acb1d6637395983ef84fc173e","after_hash":"2af150774079ecb6bac9a74078816899b06324a48e08e55cf181d5e65636b4ee","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":"6b5628fb40967071c4585cae635231544d5a386fe53605777b685b094e4bd511"} +{"hash_algorithm":"sha256","ts":"2026-10-16T16:11:08.009Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e532c36ed40fc318cac03fae","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-16T16:11:08.008Z","author":"codex-agent","text":"Workflow revert proof: removed only required_checks_unverified from the real workflow refusal guard and ran node --test --test-name-pattern='required-check metadata boundary undeclared ' test/closeout-workflow.test.ts. The regression failed (0 passed, 1 failed): workflow exit was 0 instead of required refusal exit 1. Guard restored. Head-function revert separately proved unsafe closures for both absent and missing declarations."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-16T16:11:08.009Z"}],"before_hash":"2af150774079ecb6bac9a74078816899b06324a48e08e55cf181d5e65636b4ee","after_hash":"6594c78339011416a76b0f02f5f823904d25f402788a334c45eb67057f2ea7b6","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":"efba3c7e4e710044dd51be1a9a4d21ea347898d95f5995b89ed32c4034ac16b0"} +{"hash_algorithm":"sha256","ts":"2026-10-16T16:16:08.002Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e532c36ed40fc318cac03fae","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-16T16:16:08.002Z","author":"codex-agent","text":"The real workflow regression caught CLI normalization: the host passes requiredChecks while direct core callers use required-checks. Normalize both spellings before validating declarations. The initial linked run reported 4 passed and 5 failed; successful declarations had been ignored. The fix and refusal diagnostic compatibility are now being revalidated before final gates."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-16T16:16:08.002Z"}],"before_hash":"6594c78339011416a76b0f02f5f823904d25f402788a334c45eb67057f2ea7b6","after_hash":"85b103f6d93156f58be5327368a30c42f852b3454cff7082083770af1b3a2d23","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":"e249a52912115427b0524a1575d09ba967060a58d013a50ae2be852b6cbdecf3"} +{"hash_algorithm":"sha256","ts":"2026-10-16T16:20:35.758Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e532c36ed40fc318cac03fae","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-16T16:20:35.758Z","author":"codex-agent","text":"Pinned pm linked test run passed 9/9, zero failures, skips, cancellations or todos. Exact command: npm run build && npm run build:test && node --test --test-name-pattern=\"required-check metadata boundary|declared contexts|declared names\" test/closeout.test.ts test/closeout-workflow.test.ts. Covers default refusal/no publication, missing/neutral/skipped/pending declarations, successful declared closure, readable metadata precedence, exact context names, latest legacy statuses, optional failures and preserved app bindings. Both flag spellings now work through the actual command. Earlier pre-fix full runs were stopped and will not be used as gate evidence."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-16T16:20:35.758Z"}],"before_hash":"85b103f6d93156f58be5327368a30c42f852b3454cff7082083770af1b3a2d23","after_hash":"e2f5032fdea31a6464512c63a766f22685482aa52c38650da55396ad7f59b6f0","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":"806ed075544dc819b99092003c15ee7382afc29e3ceb072a0d8892d4ed6c3897"} +{"hash_algorithm":"sha256","ts":"2026-10-16T16:45:11.663Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e532c36ed40fc318cac03fae","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-16T16:45:11.663Z","author":"codex-agent","text":"Fresh npm run release:check passed in a clean HOME with GIT_CONFIG_GLOBAL=/dev/null and GIT_CONFIG_NOSYSTEM=1: 711/711 tests, zero failures/skips/cancellations/todos. Exact coverage across 13 authored modules: lines and statements 10082/10082, functions 256/256, branches 2858/2858, all four dimensions 100 percent at unchanged thresholds. Typecheck, build, docstrings, lint, zero duplication, privacy, production audit (zero vulnerabilities), package dry-run, changelog and publish-attestation gates passed. Clean-home pm health --strict-exit --require-merge-drivers also passed; normal host health reports an environment-specific extension version skew and existing stale-item warnings. Bun release gate is running next."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-16T16:45:11.663Z"}],"before_hash":"e2f5032fdea31a6464512c63a766f22685482aa52c38650da55396ad7f59b6f0","after_hash":"4fbb41a04bd6ca35bcd08cb7eb8885709029448b89a30d3db3a0ba01800f3c46","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":"90bb64225749e8865bbaddc656afd35a24966a0e4ae0ae52b02c404e1df33922"} +{"hash_algorithm":"sha256","ts":"2026-10-16T17:04:25.496Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e532c36ed40fc318cac03fae","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-16T17:04:25.496Z","author":"codex-agent","text":"bun run release:check passed in the same clean-home/Git identity environment: 711/711 tests, zero failures/skips/cancellations/todos, 13 authored modules with exact 100/100/100/100 coverage (10082 lines/statements, 256 functions, 2858 branches). All remaining release gates passed at unchanged thresholds. Both behavioral revert proofs and the passing 9/9 real-command boundary run are recorded above. Caller declarations must remain aligned with required contexts; no release or downstream adoption is part of this change. Preparing the review PR for issue #138; item remains in_progress and will not be closed."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-16T17:04:25.496Z"}],"before_hash":"4fbb41a04bd6ca35bcd08cb7eb8885709029448b89a30d3db3a0ba01800f3c46","after_hash":"202a7ffac6d16e8565c250b7e5a47a5610a82df47ee721bc1a8faa2a7a040b21","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":"3aa7da4c279dead9146dfad652a5b9ce1ca1c3246d8639c307fd5ed029679e7a"} +{"hash_algorithm":"sha256","ts":"2026-10-16T17:04:26.562Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e532c36ed40fc318cac03fae","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-16T17:04:26.562Z"}],"before_hash":"202a7ffac6d16e8565c250b7e5a47a5610a82df47ee721bc1a8faa2a7a040b21","after_hash":"5e22ce94e9f1512e2a1bdd25d3878cf2af1f3c6620c8e0d6539d1e2f64bceca6","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":"8592b0c0960a595d86481d150d967630ab98a869d998dc0d78950f2db989322e"} +{"hash_algorithm":"sha256","ts":"2026-10-16T17:05:39.029Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e532c36ed40fc318cac03fae","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-16T17:05:39.029Z"},{"op":"add","path":"/metadata/assignee","value":"codex-agent"},{"op":"add","path":"/metadata/claim_principal","value":"codex-agent"}],"before_hash":"5e22ce94e9f1512e2a1bdd25d3878cf2af1f3c6620c8e0d6539d1e2f64bceca6","after_hash":"7bed1df316447fac8d5b4aaa778e9e74c518db589c51329d1dfa7a9db1db8383","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":"a8d9cd74a58bd449552bbef50cda5e0782f5d60f044926eb0a8508d99e683f2c"} +{"hash_algorithm":"sha256","ts":"2026-10-16T17:05:41.112Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e532c36ed40fc318cac03fae","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-16T17:05:41.112Z","author":"codex-agent","text":"PR #139 merged during validation. Rebased onto main commit 6d9cc23 and resolved the caller-template conflict by preserving the self-versioned release SHA/tag comment plus this change's job-only permissions and required-checks input. No self-versioning implementation was changed. Previous 711/711 npm and Bun receipts predate this integration; rerunning the full gates on the rebased branch before delivery."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-16T17:05:41.112Z"}],"before_hash":"7bed1df316447fac8d5b4aaa778e9e74c518db589c51329d1dfa7a9db1db8383","after_hash":"f6d98341e2f3e8b7794a72bd2b2a4d964ad957884ffd8cb1455a71649bd8653a","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":"6d23d2c15e60ca3bccbad6da62aa4d25458e7652bce95f026b3b509d576c0a89"} +{"hash_algorithm":"sha256","ts":"2026-10-16T17:41:09.873Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e532c36ed40fc318cac03fae","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-16T17:41:09.873Z","author":"codex-agent","text":"Rebased acceptance on main 6d9cc23: npm run release:check passed 747/747 tests with zero failures/skips/cancellations/todos. Exact coverage remains 13 modules, 10082/10082 lines and statements, 256/256 functions, 2858/2858 branches (100/100/100/100; thresholds unchanged). All release gates passed. Dedicated integration checks also passed: node --test test/self-versioned-workflows.test.ts (36/36) and node --test --test-name-pattern='closeout contract' test/closeout-workflow.test.ts (1/1). The rebased Bun gate is now running."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-16T17:41:09.873Z"}],"before_hash":"f6d98341e2f3e8b7794a72bd2b2a4d964ad957884ffd8cb1455a71649bd8653a","after_hash":"3d91279c7db59105c0a18c8cca829ea0d85e7e78626f392452d01f00515c4a83","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":"9b10d8ca06009ddbe49ee6cb9b12827f87b98dabb45e35d9987cd3605bd75527"} +{"hash_algorithm":"sha256","ts":"2026-10-16T18:06:25.089Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e532c36ed40fc318cac03fae","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-16T18:06:25.089Z","author":"codex-agent","text":"Final rebased acceptance: npm run release:check and bun run release:check both passed 747/747 tests, zero failures/skips/cancellations/todos, exact 100/100/100/100 coverage across 13 authored modules (10082 lines/statements, 256 functions, 2858 branches). All release gates passed at unchanged thresholds on the implementation rebased over #139. Behavioral head-function and workflow-guard revert proofs remain recorded above; real-command boundary tests passed 9/9. Ready for review; no release, merge, downstream adoption or item close is performed. Caller-declared context lists must stay aligned with required checks."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-16T18:06:25.089Z"}],"before_hash":"3d91279c7db59105c0a18c8cca829ea0d85e7e78626f392452d01f00515c4a83","after_hash":"a30a356d83e28ea153b2339786a91336952cfcbade9446f834bee5e98f584fff","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":"e81721bfbfc62b985ed775994060faa16260233d84902a1b53e1d9ac11b07325"} +{"hash_algorithm":"sha256","ts":"2026-10-16T18:06:26.222Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e532c36ed40fc318cac03fae","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-16T18:06:26.222Z"}],"before_hash":"a30a356d83e28ea153b2339786a91336952cfcbade9446f834bee5e98f584fff","after_hash":"95a01ca6cd3e4bc3e6fcaec8f066166e9dd8fc809ad76ff49c536c9b22248393","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":"2277a5f3985a79d596dcc9aa2cae1e8d4291368307da1269f08ad01777e82515"} diff --git a/.agents/pm/issues/pm-github-f1ev.toon b/.agents/pm/issues/pm-github-f1ev.toon new file mode 100644 index 00000000..15bffe03 --- /dev/null +++ b/.agents/pm/issues/pm-github-f1ev.toon @@ -0,0 +1,35 @@ +id: pm-github-f1ev +title: Refuse closeout when required-check metadata is unverified +description: "Fix https://github.com/unbraind/pm-github/issues/138. Require complete protection metadata or explicit caller-declared contexts; harden caller permissions." +type: Issue +status: in_progress +priority: 1 +tags[2]: closeout,validation +created_at: "2026-10-16T16:04:00.890Z" +updated_at: "2026-10-16T18:06:26.222Z" +author: codex-agent +acceptance_criteria: Unavailable metadata without declarations never closes or publishes; declared contexts require success; metadata-backed behavior remains unchanged; npm and Bun release gates pass. +comments[11]{created_at,author,text}: + "2026-10-16T16:04:15.081Z",codex-agent,"Plan: distinguish unavailable protection metadata from a readable empty policy. Refuse with required_checks_unverified unless required-checks declares contexts; retain readable app-bound requirements. Exercise real command and workflow shell against injected HTTP evidence and real trackers. Keep workflow edits minimal around open PR #139." + "2026-10-16T16:08:21.292Z",codex-agent,"Implementation distinguishes metadata gaps from a readable empty policy. required_checks_unverified blocks workflow publication; required-checks verifies declared contexts plus readable app-bound requirements. Regression coverage uses actual workflow bash, a packed extension, injected HTTP APIs, real SDK tracker mutations and an isolated Git remote. Caller write scopes now belong only to gated-closeout." + "2026-10-16T16:10:06.013Z",codex-agent,"Behavioural revert proof: restored only checkCloseoutHead to its previous implementation (retaining the added argument for compilation), rebuilt dist, and ran node --test --test-name-pattern='required-check metadata boundary (undeclared|missing) ' test/closeout-workflow.test.ts. Both tests failed: each observed closed=1 where closed=0 was required (0 passed, 2 failed). Fixed function restored and rebuilt. This exercises the packed command and real workflow rather than SDK mocks." + "2026-10-16T16:11:08.008Z",codex-agent,"Workflow revert proof: removed only required_checks_unverified from the real workflow refusal guard and ran node --test --test-name-pattern='required-check metadata boundary undeclared ' test/closeout-workflow.test.ts. The regression failed (0 passed, 1 failed): workflow exit was 0 instead of required refusal exit 1. Guard restored. Head-function revert separately proved unsafe closures for both absent and missing declarations." + "2026-10-16T16:16:08.002Z",codex-agent,"The real workflow regression caught CLI normalization: the host passes requiredChecks while direct core callers use required-checks. Normalize both spellings before validating declarations. The initial linked run reported 4 passed and 5 failed; successful declarations had been ignored. The fix and refusal diagnostic compatibility are now being revalidated before final gates." + "2026-10-16T16:20:35.758Z",codex-agent,"Pinned pm linked test run passed 9/9, zero failures, skips, cancellations or todos. Exact command: npm run build && npm run build:test && node --test --test-name-pattern=\"required-check metadata boundary|declared contexts|declared names\" test/closeout.test.ts test/closeout-workflow.test.ts. Covers default refusal/no publication, missing/neutral/skipped/pending declarations, successful declared closure, readable metadata precedence, exact context names, latest legacy statuses, optional failures and preserved app bindings. Both flag spellings now work through the actual command. Earlier pre-fix full runs were stopped and will not be used as gate evidence." + "2026-10-16T16:45:11.663Z",codex-agent,"Fresh npm run release:check passed in a clean HOME with GIT_CONFIG_GLOBAL=/dev/null and GIT_CONFIG_NOSYSTEM=1: 711/711 tests, zero failures/skips/cancellations/todos. Exact coverage across 13 authored modules: lines and statements 10082/10082, functions 256/256, branches 2858/2858, all four dimensions 100 percent at unchanged thresholds. Typecheck, build, docstrings, lint, zero duplication, privacy, production audit (zero vulnerabilities), package dry-run, changelog and publish-attestation gates passed. Clean-home pm health --strict-exit --require-merge-drivers also passed; normal host health reports an environment-specific extension version skew and existing stale-item warnings. Bun release gate is running next." + "2026-10-16T17:04:25.496Z",codex-agent,"bun run release:check passed in the same clean-home/Git identity environment: 711/711 tests, zero failures/skips/cancellations/todos, 13 authored modules with exact 100/100/100/100 coverage (10082 lines/statements, 256 functions, 2858 branches). All remaining release gates passed at unchanged thresholds. Both behavioral revert proofs and the passing 9/9 real-command boundary run are recorded above. Caller declarations must remain aligned with required contexts; no release or downstream adoption is part of this change. Preparing the review PR for issue #138; item remains in_progress and will not be closed." + "2026-10-16T17:05:41.112Z",codex-agent,PR #139 merged during validation. Rebased onto main commit 6d9cc23 and resolved the caller-template conflict by preserving the self-versioned release SHA/tag comment plus this change's job-only permissions and required-checks input. No self-versioning implementation was changed. Previous 711/711 npm and Bun receipts predate this integration; rerunning the full gates on the rebased branch before delivery. + "2026-10-16T17:41:09.873Z",codex-agent,"Rebased acceptance on main 6d9cc23: npm run release:check passed 747/747 tests with zero failures/skips/cancellations/todos. Exact coverage remains 13 modules, 10082/10082 lines and statements, 256/256 functions, 2858/2858 branches (100/100/100/100; thresholds unchanged). All release gates passed. Dedicated integration checks also passed: node --test test/self-versioned-workflows.test.ts (36/36) and node --test --test-name-pattern='closeout contract' test/closeout-workflow.test.ts (1/1). The rebased Bun gate is now running." + "2026-10-16T18:06:25.089Z",codex-agent,"Final rebased acceptance: npm run release:check and bun run release:check both passed 747/747 tests, zero failures/skips/cancellations/todos, exact 100/100/100/100 coverage across 13 authored modules (10082 lines/statements, 256 functions, 2858 branches). All release gates passed at unchanged thresholds on the implementation rebased over #139. Behavioral head-function and workflow-guard revert proofs remain recorded above; real-command boundary tests passed 9/9. Ready for review; no release, merge, downstream adoption or item close is performed. Caller-declared context lists must stay aligned with required checks." +files[5]{path,scope}: + .github/workflows/pm-github-closeout.yml,project + closeout.ts,project + index.ts,project + test/closeout-workflow.test.ts,project + test/closeout.test.ts,project +tests[1]{command,scope,timeout_seconds,provenance{author,created_at,source_kind,source_ref}}: + "npm run build && npm run build:test && node --test --test-name-pattern=\"required-check metadata boundary|declared contexts|declared names\" test/closeout.test.ts test/closeout-workflow.test.ts",project,1800,codex-agent,"2026-10-16T16:10:42.723Z",local_mutation,fix/closeout-required-checks-unverified +docs[2]{path,scope}: + docs/closeout-workflow-caller.yml,project + README.md,project +body: "" diff --git a/.github/workflows/pm-github-closeout.yml b/.github/workflows/pm-github-closeout.yml index 0f0c646b..57d33a1d 100644 --- a/.github/workflows/pm-github-closeout.yml +++ b/.github/workflows/pm-github-closeout.yml @@ -31,6 +31,11 @@ on: type: string required: false default: "ci.yml" + required-checks: + description: "Whitespace-separated required contexts to verify when protection metadata is unavailable" + type: string + required: false + default: "" node-version: description: "Node.js version for the pm CLI" type: string @@ -59,6 +64,7 @@ jobs: REQUESTED_PM_GITHUB_VERSION: ${{ inputs.pm-github-version }} NPM_PROJECT_DIR: ${{ inputs.npm-project-dir }} POST_CLOSEOUT_SCRIPT: ${{ inputs.post-closeout-script }} + REQUIRED_CHECKS: ${{ inputs.required-checks }} CI_WORKFLOWS: ${{ inputs.ci-workflows }} CALLING_REPOSITORY: ${{ github.repository }} steps: @@ -283,7 +289,11 @@ jobs: set -euo pipefail # Full history catches up after missed schedules; the command skips terminal items. # Keep the report private until the proposed change has passed the privacy gate. - if ! pm github closeout --repo "${REPOSITORY}" --since 1970-01-01 --apply --json > "${RUNNER_TEMP}/pm-github-closeout.json" 2> "${RUNNER_TEMP}/pm-github-closeout.err"; then + required_args=() + if [[ -n "${REQUIRED_CHECKS:-}" ]]; then + required_args+=(--required-checks "${REQUIRED_CHECKS}") + fi + if ! pm github closeout --repo "${REPOSITORY}" --since 1970-01-01 --apply --json "${required_args[@]}" > "${RUNNER_TEMP}/pm-github-closeout.json" 2> "${RUNNER_TEMP}/pm-github-closeout.err"; then echo "::error::Closeout refused; check token read access to pull requests, checks and branch rules, and tracker close validation. No branch was published." exit 1 fi @@ -291,8 +301,8 @@ jobs: # Only a local tracker refusal makes missing head evidence actionable. # Foreign, terminal, excluded and unlinked PRs cannot block this run. jq -e 'all(.plan[]; (.code | type) == "string")' "${RUNNER_TEMP}/pm-github-closeout.json" >/dev/null - if jq -e 'any(.plan[]; .action == "skip" and .code == "head_evidence_unavailable")' "${RUNNER_TEMP}/pm-github-closeout.json" >/dev/null; then - echo "::error::Closeout refused; check token read access to head checks for linked local tracker items. No branch was published." + if jq -e 'any(.plan[]; .action == "skip" and (.code == "head_evidence_unavailable" or .code == "required_checks_unverified"))' "${RUNNER_TEMP}/pm-github-closeout.json" >/dev/null; then + echo "::error::Closeout refused; check token read access to head checks for linked local tracker items, and declare required-checks when protection metadata is unavailable. No branch was published." exit 1 fi # Merge facts are checked before head lookup and have a distinct decision. diff --git a/README.md b/README.md index 3d14490e..57f9fbb2 100644 --- a/README.md +++ b/README.md @@ -65,14 +65,21 @@ latest status. Check-run, status and ruleset pages are fully read. Rulesets are read through GitHub's [effective branch rules API](https://docs.github.com/en/rest/repos/rules#get-rules-for-a-branch). The check policy reflects the rules readable at invocation time. -When a protection source is unreadable (HTTP 403/404), or no required contexts -are configured, the report labels the **fallback** rule. Every readable required -context still must conclude success, including its app binding. The fallback -additionally requires at least one head check run, every run completed with -`success`, `neutral` or `skipped`, and successful latest legacy head statuses. -It never discards a known requirement because another source is unreadable. -Missing, malformed or incomplete head evidence, malformed metadata and other -metadata errors fail closed. The plan reports which rule was used. +When either protection source is unreadable (HTTP 403/404), the row is ineligible +with `required_checks_unverified`. Observed green runs cannot prove that every +required context ran. Supply `--required-checks "CI CodeQL"` to declare a +whitespace-separated set of required context names when metadata is unavailable. +Every declared context must conclude **success** on the exact head (or have a +successful latest legacy status); missing, pending, neutral or skipped contexts +refuse closure. Readable requirements remain enforced, including app bindings. +This **declared** policy does not require unrelated optional runs to succeed. +When both metadata sources are readable, declarations do not replace their policy. + +With fully readable metadata and no required contexts configured, the report +labels the **fallback** rule: at least one head check run must exist, every run +must complete with `success`, `neutral` or `skipped`, and latest legacy statuses +must succeed. Missing, malformed or incomplete evidence and other metadata errors +fail closed. The plan reports which rule was used. TOON lists actionable `close`/`refuse` rows individually and summarizes routine skips as counts by stable code, for example `skipped: {already_terminal: 30, @@ -105,8 +112,10 @@ structured close fields it supports, without overriding tracker policy: otherwise `Delivered by the linked pull request, merged after its head check gate passed.` - `actual_result`: `Merged at