Skip to content

feat(miner-manage): rejection state machine (closed/rejected → disengaged) #4278

Description

@JSONbored

packages/gittensory-miner/lib/rejection-templates.js (#2324) exists and is fully implemented — renderRejectionMessage (rejection-templates.js:57-71) deterministically renders one of three courtesy notes (REJECTION_REASONS: gate_close, maintainer_close_no_reason, superseded_by_duplicate, rejection-templates.js:9-19) for a closed PR. But it has zero callers anywhere outside its own test (test/unit/miner-rejection-templates.test.ts) — nothing in the codebase currently detects a rejection, classifies which of the three buckets applies, or invokes the renderer. This issue is that missing state machine, not the templates themselves.

Two concrete gaps to close along the way:

  1. Detecting the rejection at all. ci-poller.js's fetchHeadSha (ci-poller.js:155-163) already fetches the full GET /pulls/{number} payload but discards everything except head.sha — the PR's state/merged/merged_at/closed_at fields (exactly what's needed to detect "closed without merge") are right there in the response and currently thrown away. manage-poll.js's outcome field (mapPollConclusionToOutcome, manage-poll.js:33-42) is derived purely from CI conclusion (ready/needs-work/open) and has no "closed"/"rejected" value at all today.
  2. The target state "disengaged" doesn't exist yet. run-state.js's RUN_STATES is ["idle", "discovering", "planning", "preparing"] (run-state.js:6) — a per-repo lifecycle state, with no "disengaged" member. A rejected PR is a per-PR outcome, not a per-repo run state, so "closed/rejected → disengaged" most likely belongs as a new value in the per-PR outcome vocabulary manage-poll.js already writes (alongside ready/needs-work/open), not as a fifth RUN_STATES entry — but this is a real design decision the implementer needs to make and document, not assume.

Deliverables

  • Extend the PR-fetching path to surface state/merged/merged_at (either widen ci-poller.js's fetchHeadSha return shape, or add a sibling fetch) so a poller can detect "closed without merge" without a second unrelated API call
  • A pure classifier that maps a detected closed-without-merge PR (plus whatever gate-verdict signal is available from the sibling watcher issue) to one of REJECTION_REASONS — decide and document whether "no signal available" defaults to maintainer_close_no_reason or is left unclassified
  • Decide (and document in the module) whether "disengaged" is a new manage-poll.js outcome value or a new run-state.js state; implement whichever is chosen without silently expanding the other module's enum as a side effect
  • Wire the classifier's output into renderRejectionMessage (rejection-templates.js:57) so it finally has a real caller, and persist the rendered note + classification via the local event ledger (reusing the pr-outcome.js writer from the sibling issue if it lands first)
  • Unit tests for: each of the three reason buckets being selected correctly, the zero-signal fallback, and the "disengaged" transition itself
  • Still no GitHub write action — this stays a local classification + local note, matching the existing module's "posting anywhere is a separate write action, out of scope here" boundary (rejection-templates.js:2-4)

References

  • packages/gittensory-miner/lib/rejection-templates.js:1-20,57-71 (existing templates + renderer, zero current callers)
  • packages/gittensory-miner/lib/ci-poller.js:155-163 (fetchHeadSha — fetches the full PR payload but discards state/merged)
  • packages/gittensory-miner/lib/manage-poll.js:33-42 (mapPollConclusionToOutcome — current outcome vocabulary with no closed/rejected value)
  • packages/gittensory-miner/lib/run-state.js:6 (RUN_STATES — confirms "disengaged" is not a current per-repo state)
  • test/unit/miner-rejection-templates.test.ts (confirms the templates' only current caller is their own test)

Metadata

Metadata

Assignees

No one assigned

    Labels

    gittensor:featureGittensor-scored feature linked to a feature issue — scores a 0.25x multiplier.help wantedExtra attention is needed

    Projects

    Status
    Done

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions