From b5b566dc1d301ee95d651ce9983de772b0bca399 Mon Sep 17 00:00:00 2001 From: Jory Irving Date: Sun, 27 Sep 2026 13:15:47 -0600 Subject: [PATCH 1/2] docs(groomer): define close proof policy --- docs/groomer-close-policy.md | 48 ++++++++++++++++++++++++++++++++++++ docs/hosted-groomer.md | 4 ++- 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 docs/groomer-close-policy.md diff --git a/docs/groomer-close-policy.md b/docs/groomer-close-policy.md new file mode 100644 index 00000000..e4707d3d --- /dev/null +++ b/docs/groomer-close-policy.md @@ -0,0 +1,48 @@ +# Groomer Close Policy + +**Status:** issue #1069 v1 decision. This document does not authorize or implement additional automatic close classes. + +## V1 decision + +The hosted groomer may autonomously close only an issue it classifies as `already_done` under the existing deterministic policy: high confidence, no material uncertainty, and current repository evidence pinned to a SHA that grounds every acceptance criterion of that issue. Each criterion must be grounded in repository content read at that SHA. A related PR or issue may corroborate the conclusion, but cannot establish it by itself; a merged PR alone is not proof that this issue is resolved. + +`duplicate` and `superseded` remain recommendations for human review. No new autonomous close class, machine deduplication, implementation child, UI override, or force path is approved. The existing `already_done` policy is not broadened by this decision. There is no requirement or authorization for an operator override: a human who decides to close an issue must close it manually in GitHub, choosing the target and reason themselves. That manual action is not a groomer close and does not feed back as verified provenance to the groomer. + +The recommendations are persisted in `GroomingRun.mutationPlan` only; they are not surfaced in the UI. An operator can inspect an individual run through the authorized `GET /api/groomer/runs/[id]` detail API. The recommendation does not presently carry a verified human decision or a complete, independently verified target record, so it must not be treated as such. + +## Authority and evidence boundaries + +No GitHub comment, label, duplicate marker, or other timeline event is sufficient human authority for automatic closure in v1. This includes a human-authored comment: although an operator can manually close an issue in GitHub with a chosen target and reason, Dispatch does not currently verify the actor, intent, target, or provenance of that action for groomer policy. A bot or AI-authored comment, quoted text, or automation event is not human authority either. + +Do not infer duplicate identity from title or body similarity. Machine deduplication is not approved. Any future proposal must define a stable, machine-readable identity under a source-specific contract and demonstrate collision and regression safety; similar wording, shared labels, and related-work citations are insufficient. + +A superseded issue may be classified `already_done` only when current pinned repository evidence independently grounds that issue's own acceptance as satisfied. The existence of a newer issue, replacement PR, or merged PR does not suffice. Reopened issues and issues with regression evidence must go to human review, not an automatic close. This is a normative safety rule, not a description of current behavior: the existing `already_done` path does not check the issue's reopened/regression timeline. That implementation gap needs a separate guardrail follow-up; #1063 is already closed and does not supply it. Until that guardrail exists, do not interpret the current path as proving that an issue was never reopened or regressed. + +## Requirements for a separately approved extension + +Any future close class needs a separate design and explicit approval, and must establish all of the following before implementation or enablement: + +- **Bound authority:** specify the allowed actor and source, prove provenance, and distinguish authenticated human action from automation and quoted content. A timeline signal alone is not authority. +- **Bound target:** record the target repository, issue number, URL, and observed state; verify the target and state are current immediately before acting. A target being closed is not proof that this issue's symptom is resolved. +- **Bound evidence:** record checked-at time, immutable source/event ID, pinned repository SHA, and evidence bindings connecting this exact issue's acceptance to the decision. State explicitly what present recommendation data does not establish. +- **Versioned live check:** record the policy version and re-check required facts immediately before a close. Missing, stale, ambiguous, or changed facts fail closed. +- **Safe uncertainty handling:** preserve the recommendation and route uncertainty to human review; do not apply a done label or close. The existing pipeline can post a pre-close comment before a close attempt. Any future close class must not post an automatic resolution comment; that prohibition does not claim the current pipeline never posts a comment before closing. +- **No generic override:** human review means a human manually acts in GitHub. It is not a UI control, a generic `force` bypass, or permission for the groomer to close on the human's behalf. Any future machine override would need a separately specified and approved authority contract. +- **Regression proof:** pass the concrete fixture matrix below, expanded for the source-specific boundary cases, before enablement. + +### Minimum fixture matrix for a future extension + +| Case | Expected result | +| --- | --- | +| Authenticated human action with explicit target and reason, verified from the authoritative source | May proceed only if the separately approved policy accepts that exact source; record actor, target, reason, and immutable event ID. | +| Same-looking comment by bot/AI, or quoted human text | Reject as authority; recommendation only. | +| Human comment or label without a verified actor, target, or reason | Reject as authority; recommendation only. | +| GitHub duplicate marker from unknown/unverified actor | Reject as authority; recommendation only. | +| Correct target is open, wrong repository/number, deleted, inaccessible, or stale | Do not close; retain recommendation for human review. | +| Target is closed or merged PR exists, but current issue's acceptance is not independently proven | Do not close. | +| Current issue was reopened or has regression evidence | Do not close; route to human review. Include the current `already_done` implementation gap as a failing safety fixture until a separate guardrail closes it. | +| Similar titles/bodies, shared labels, or citation without a stable identity | Do not deduplicate or close. | +| Stable identity collision or contradictory evidence | Fail closed and retain recommendation. | +| Required source/event/state/evidence is missing, stale, ambiguous, or changes between planning and apply | Apply no close; record why and retain recommendation. | + +These are gates for a separately approved extension, not features promised by the current groomer. Until a class passes its applicable fixtures and receives an explicit policy decision, duplicate and superseded outcomes remain recommendations only. diff --git a/docs/hosted-groomer.md b/docs/hosted-groomer.md index 86c57b91..bdd412e8 100644 --- a/docs/hosted-groomer.md +++ b/docs/hosted-groomer.md @@ -150,6 +150,8 @@ Other rules the validator enforces: ### Close policy +The approved scope is limited to the existing deterministic `already_done` policy below. Duplicate and superseded outcomes remain recommendations for human review; issue #1069's design boundaries and the gate for any future extension are in [Groomer Close Policy](./groomer-close-policy.md). This link documents policy intent, not support for any hypothetical authority or target-verification feature. + An `already_done` plan closes the issue, the highest-impact write the groomer makes. It validates only when all of these hold: - the close has reason `already_done`, verdict confidence is `high`, and no material uncertainty remains; @@ -225,7 +227,7 @@ Every run captures its own snapshot, so the key only recurs when the issue, its Dry runs use the same preconditions, diff and policies without writing: `mutationPlan.applyOutcome` is `dry_run`, `stale` or `unverifiable` (with `preconditionFailures`), or `would_replay` when the key was already applied. A dry run never claims a key, so it never reports `busy`, and it never writes a backoff. -Out of scope here, and still to come: worker admission gating on these results (#1065), child issue creation (#1066), semantic duplicate/superseded closes (design gate #1069), and UI exposure of the new history fields (#1067). +The v1 decision for #1069 is made in [Groomer Close Policy](./groomer-close-policy.md): duplicate and superseded remain recommendations, and no new autonomous close class is approved. The decision does not mean an extension is implemented or fully designed; authority/target verification and the other extension gates remain open design work. Recommendations are persisted in `GroomingRun.mutationPlan`, not surfaced in the UI; operators can inspect an individual run through the authorized `GET /api/groomer/runs/[id]` detail API. Worker admission gating on these results (#1065), child issue creation (#1066), and UI exposure of history fields (#1067) remain out of scope here. ## History and Audit From 3fbbac43aa0d74d194c5ea9301c2cd6ed0f265c9 Mon Sep 17 00:00:00 2001 From: Jory Irving Date: Sun, 27 Sep 2026 13:22:27 -0600 Subject: [PATCH 2/2] docs(groomer): trim close policy decision --- docs/groomer-close-policy.md | 47 ++++++++++-------------------------- docs/hosted-groomer.md | 4 +-- 2 files changed, 15 insertions(+), 36 deletions(-) diff --git a/docs/groomer-close-policy.md b/docs/groomer-close-policy.md index e4707d3d..84f600d3 100644 --- a/docs/groomer-close-policy.md +++ b/docs/groomer-close-policy.md @@ -1,48 +1,27 @@ # Groomer Close Policy -**Status:** issue #1069 v1 decision. This document does not authorize or implement additional automatic close classes. +**Status:** issue #1069 v1 decision. No additional automatic close classes are approved. ## V1 decision -The hosted groomer may autonomously close only an issue it classifies as `already_done` under the existing deterministic policy: high confidence, no material uncertainty, and current repository evidence pinned to a SHA that grounds every acceptance criterion of that issue. Each criterion must be grounded in repository content read at that SHA. A related PR or issue may corroborate the conclusion, but cannot establish it by itself; a merged PR alone is not proof that this issue is resolved. +The hosted groomer may close an issue only as `already_done`, with high confidence, no material uncertainty, and current repository evidence pinned to a SHA that grounds every acceptance criterion of that issue. A related PR or issue may corroborate the conclusion, but cannot establish it by itself; a merged PR alone is insufficient. -`duplicate` and `superseded` remain recommendations for human review. No new autonomous close class, machine deduplication, implementation child, UI override, or force path is approved. The existing `already_done` policy is not broadened by this decision. There is no requirement or authorization for an operator override: a human who decides to close an issue must close it manually in GitHub, choosing the target and reason themselves. That manual action is not a groomer close and does not feed back as verified provenance to the groomer. +`duplicate` and `superseded` remain recommendations only. No machine deduplication, implementation child, UI override, or force path is approved. A human who decides to close an issue must close it manually in GitHub, choosing the target and reason; this is not a groomer close. -The recommendations are persisted in `GroomingRun.mutationPlan` only; they are not surfaced in the UI. An operator can inspect an individual run through the authorized `GET /api/groomer/runs/[id]` detail API. The recommendation does not presently carry a verified human decision or a complete, independently verified target record, so it must not be treated as such. +Recommendations are persisted in `GroomingRun.mutationPlan`, not surfaced in the UI. Operators can inspect an individual run through the authorized `GET /api/groomer/runs/[id]` detail API. -## Authority and evidence boundaries +## Boundaries -No GitHub comment, label, duplicate marker, or other timeline event is sufficient human authority for automatic closure in v1. This includes a human-authored comment: although an operator can manually close an issue in GitHub with a chosen target and reason, Dispatch does not currently verify the actor, intent, target, or provenance of that action for groomer policy. A bot or AI-authored comment, quoted text, or automation event is not human authority either. +No comment, label, duplicate marker, or other timeline event is sufficient human authority for automatic closure. Similar titles, bodies, labels, and citations do not establish duplicate identity. A superseded issue can qualify as `already_done` only when pinned repository evidence independently grounds that issue's own acceptance. -Do not infer duplicate identity from title or body similarity. Machine deduplication is not approved. Any future proposal must define a stable, machine-readable identity under a source-specific contract and demonstrate collision and regression safety; similar wording, shared labels, and related-work citations are insufficient. +A merged PR that references the issue is corroboration only, in part because an open issue may have been reopened after the merge. The remaining risk is a live regression while the cited code remains present: verbatim excerpts can still pass grounding even when behavior is broken. [#1113](https://github.com/misospace/dispatch/issues/1113) tracks that guardrail. -A superseded issue may be classified `already_done` only when current pinned repository evidence independently grounds that issue's own acceptance as satisfied. The existence of a newer issue, replacement PR, or merged PR does not suffice. Reopened issues and issues with regression evidence must go to human review, not an automatic close. This is a normative safety rule, not a description of current behavior: the existing `already_done` path does not check the issue's reopened/regression timeline. That implementation gap needs a separate guardrail follow-up; #1063 is already closed and does not supply it. Until that guardrail exists, do not interpret the current path as proving that an issue was never reopened or regressed. +## Gate for any separately approved extension -## Requirements for a separately approved extension +Any proposed extension needs a separate design and explicit approval, including: -Any future close class needs a separate design and explicit approval, and must establish all of the following before implementation or enablement: +- **Bound authority:** define and verify the permitted actor and source; distinguish human action from automation and quoted text. +- **Live target check:** bind the exact target and verify its identity and current state immediately before acting. +- **Regression fixtures:** cover the source-specific authority and target boundaries, including behavior regressing while the cited excerpt remains unchanged. -- **Bound authority:** specify the allowed actor and source, prove provenance, and distinguish authenticated human action from automation and quoted content. A timeline signal alone is not authority. -- **Bound target:** record the target repository, issue number, URL, and observed state; verify the target and state are current immediately before acting. A target being closed is not proof that this issue's symptom is resolved. -- **Bound evidence:** record checked-at time, immutable source/event ID, pinned repository SHA, and evidence bindings connecting this exact issue's acceptance to the decision. State explicitly what present recommendation data does not establish. -- **Versioned live check:** record the policy version and re-check required facts immediately before a close. Missing, stale, ambiguous, or changed facts fail closed. -- **Safe uncertainty handling:** preserve the recommendation and route uncertainty to human review; do not apply a done label or close. The existing pipeline can post a pre-close comment before a close attempt. Any future close class must not post an automatic resolution comment; that prohibition does not claim the current pipeline never posts a comment before closing. -- **No generic override:** human review means a human manually acts in GitHub. It is not a UI control, a generic `force` bypass, or permission for the groomer to close on the human's behalf. Any future machine override would need a separately specified and approved authority contract. -- **Regression proof:** pass the concrete fixture matrix below, expanded for the source-specific boundary cases, before enablement. - -### Minimum fixture matrix for a future extension - -| Case | Expected result | -| --- | --- | -| Authenticated human action with explicit target and reason, verified from the authoritative source | May proceed only if the separately approved policy accepts that exact source; record actor, target, reason, and immutable event ID. | -| Same-looking comment by bot/AI, or quoted human text | Reject as authority; recommendation only. | -| Human comment or label without a verified actor, target, or reason | Reject as authority; recommendation only. | -| GitHub duplicate marker from unknown/unverified actor | Reject as authority; recommendation only. | -| Correct target is open, wrong repository/number, deleted, inaccessible, or stale | Do not close; retain recommendation for human review. | -| Target is closed or merged PR exists, but current issue's acceptance is not independently proven | Do not close. | -| Current issue was reopened or has regression evidence | Do not close; route to human review. Include the current `already_done` implementation gap as a failing safety fixture until a separate guardrail closes it. | -| Similar titles/bodies, shared labels, or citation without a stable identity | Do not deduplicate or close. | -| Stable identity collision or contradictory evidence | Fail closed and retain recommendation. | -| Required source/event/state/evidence is missing, stale, ambiguous, or changes between planning and apply | Apply no close; record why and retain recommendation. | - -These are gates for a separately approved extension, not features promised by the current groomer. Until a class passes its applicable fixtures and receives an explicit policy decision, duplicate and superseded outcomes remain recommendations only. +Until such an extension is approved, duplicate and superseded outcomes remain recommendations only. diff --git a/docs/hosted-groomer.md b/docs/hosted-groomer.md index bdd412e8..067f09ec 100644 --- a/docs/hosted-groomer.md +++ b/docs/hosted-groomer.md @@ -150,7 +150,7 @@ Other rules the validator enforces: ### Close policy -The approved scope is limited to the existing deterministic `already_done` policy below. Duplicate and superseded outcomes remain recommendations for human review; issue #1069's design boundaries and the gate for any future extension are in [Groomer Close Policy](./groomer-close-policy.md). This link documents policy intent, not support for any hypothetical authority or target-verification feature. +The approved scope is limited to the existing deterministic `already_done` policy below. Duplicate and superseded outcomes remain recommendations for human review; see [Groomer Close Policy](./groomer-close-policy.md). An `already_done` plan closes the issue, the highest-impact write the groomer makes. It validates only when all of these hold: @@ -227,7 +227,7 @@ Every run captures its own snapshot, so the key only recurs when the issue, its Dry runs use the same preconditions, diff and policies without writing: `mutationPlan.applyOutcome` is `dry_run`, `stale` or `unverifiable` (with `preconditionFailures`), or `would_replay` when the key was already applied. A dry run never claims a key, so it never reports `busy`, and it never writes a backoff. -The v1 decision for #1069 is made in [Groomer Close Policy](./groomer-close-policy.md): duplicate and superseded remain recommendations, and no new autonomous close class is approved. The decision does not mean an extension is implemented or fully designed; authority/target verification and the other extension gates remain open design work. Recommendations are persisted in `GroomingRun.mutationPlan`, not surfaced in the UI; operators can inspect an individual run through the authorized `GET /api/groomer/runs/[id]` detail API. Worker admission gating on these results (#1065), child issue creation (#1066), and UI exposure of history fields (#1067) remain out of scope here. +Out of scope here, and still to come: worker admission gating on these results (#1065), child issue creation (#1066), and UI exposure of the new history fields (#1067). ## History and Audit