mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 21:44:49 +00:00
f366f762cc
## Description:
# Issue Lifecycle Actions
Adds two GitHub Actions workflows that enforce OpenFront's
issue-lifecycle invariants. No LLM calls — only the default
`GITHUB_TOKEN`. Layer B (Claude-powered triage) will build on this
foundation.
## Summary
- **Stale closer** — daily cron. Unmilestoned issues get warned at 5
days of inactivity, auto-closed at 10. Exempt: milestoned or
`keep-open`. Bot comments don't reset the timer.
- **Assignment invariant** — event + cron backstop. You cannot assign
anyone to an unmilestoned issue. Violators are unassigned automatically
with an explanatory comment.
- **Approval label sync** — event + cron backstop. The `not-approved`
(red) and `approved` (green) labels are derived from milestone state.
These labels are *only* ever touched by this Action.
## Rollout
Both workflows ship gated by `vars.ISSUE_LIFECYCLE_DRY_RUN` (defaults to
`'true'`). They log decisions but do not mutate anything until the
maintainer flips that variable in **Settings → Variables**.
Suggested rollout:
1. Merge with dry-run on.
2. Watch the cron logs for ~1 week. Verify the action list matches
expectations.
3. Flip `ISSUE_LIFECYCLE_DRY_RUN=false` to go live.
## File layout
```
.github/workflows/
issue-lifecycle-cron.yml # daily 06:00 UTC + workflow_dispatch
issue-lifecycle-events.yml # issues: [opened, assigned, milestoned, demilestoned]
scripts/issue-lifecycle/
config.ts # labels, colors, thresholds, comment templates
github.ts # Octokit wrapper, Action applier, label idempotent-creation
rules/
approval-label-sync.ts # pure function — idempotent
assignment-invariant.ts # pure function
stale-closer.ts # async — reads comment history, filters bots
cron.ts # daily sweep orchestrator
events.ts # event-mode dispatcher
index.ts # entrypoint, CLI arg parser
README.md
```
Structure mirrors `scripts/pr-gate/` from Unit 2 — same Octokit/Action
patterns, same dry-run convention.
## Self-installing labels
On every run, the Action ensures the six labels exist (`not-approved`,
`approved`, `stale`, `keep-open`, `needs-info`, `auto-closed-stale`)
with the correct colors and descriptions. No manual setup required.
## Local testing
```bash
cd scripts/issue-lifecycle
npm install
export GITHUB_TOKEN=ghp_...
# Full cron sweep, dry-run (default for CLI):
npx tsx index.ts --mode cron
# Simulate an event:
EVENT_NAME=assigned npx tsx index.ts --mode event --issue 1234
```
CLI invocations are dry-run unless `--no-dry-run` is passed explicitly.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
## Please put your Discord username so you can be contacted if a bug or
regression is found:
evan
62 lines
2.9 KiB
Markdown
62 lines
2.9 KiB
Markdown
# Issue Lifecycle
|
|
|
|
Deterministic GitHub Actions that enforce OpenFront's issue lifecycle. No LLM calls — only the default `GITHUB_TOKEN`.
|
|
|
|
## Rules
|
|
|
|
1. **Stale closer** — daily cron. Unmilestoned issues get a warning at 7 days of inactivity, are closed at 14. Exempt: milestoned or `keep-open`-labelled issues. Assignees are not an exemption — if an unmilestoned issue somehow has assignees (event drift), Rule 2 unassigns them and Rule 1 still applies.
|
|
2. **Assignment invariant** — event + cron. You cannot assign a person to an unmilestoned issue. Violators are unassigned automatically.
|
|
3. **Approval label sync** — event + cron. `not-approved` and `approved` track milestone state (none = `not-approved`, any milestone = `approved`).
|
|
|
|
See the parent spec for the full contribution model rationale.
|
|
|
|
## Triggers
|
|
|
|
- [issue-lifecycle-cron.yml](../../.github/workflows/issue-lifecycle-cron.yml) — daily at 06:00 UTC, plus `workflow_dispatch`.
|
|
- [issue-lifecycle-events.yml](../../.github/workflows/issue-lifecycle-events.yml) — `issues: [opened, assigned, milestoned, demilestoned]`.
|
|
|
|
## Local testing
|
|
|
|
```bash
|
|
cd scripts/issue-lifecycle
|
|
npm install
|
|
export GITHUB_TOKEN=ghp_... # PAT with repo scope
|
|
|
|
# Full cron sweep against the real repo, dry-run (default for CLI):
|
|
npx tsx index.ts --mode cron
|
|
|
|
# Single-issue event-mode dry-run (simulates the assigned event):
|
|
EVENT_NAME=assigned npx tsx index.ts --mode event --issue 1234
|
|
|
|
# Force live mode locally (BE CAREFUL — this will mutate the repo):
|
|
npx tsx index.ts --mode cron --no-dry-run
|
|
```
|
|
|
|
CLI invocations are dry-run by default. Pass `--no-dry-run` to apply.
|
|
|
|
## Toggling dry-run in production
|
|
|
|
1. Go to repo **Settings → Secrets and variables → Actions → Variables**.
|
|
2. Edit `ISSUE_LIFECYCLE_DRY_RUN`.
|
|
3. Set to `false` to make the Actions act for real; any other value (or unset) keeps them in dry-run mode.
|
|
|
|
The default is `true` — both workflows log decisions but do not act until the maintainer flips the variable.
|
|
|
|
## File layout
|
|
|
|
- [config.ts](./config.ts) — constants, labels, comment templates
|
|
- [github.ts](./github.ts) — Octokit wrapper, action applier, label idempotent-creation
|
|
- [rules/approval-label-sync.ts](./rules/approval-label-sync.ts) — Rule 3 (pure function)
|
|
- [rules/assignment-invariant.ts](./rules/assignment-invariant.ts) — Rule 2 (pure function)
|
|
- [rules/stale-closer.ts](./rules/stale-closer.ts) — Rule 1 (async; reads comment history)
|
|
- [cron.ts](./cron.ts) — daily sweep orchestrator
|
|
- [events.ts](./events.ts) — event-mode dispatcher
|
|
- [index.ts](./index.ts) — entrypoint, arg parser
|
|
|
|
## Notes
|
|
|
|
- Labels are auto-created on every run (idempotent). The maintainer does not need to manually pre-create any of them.
|
|
- Rules act only on issues — PRs are filtered out (`pull_request` field check).
|
|
- API rate limit during the cron sweep causes the run to exit non-zero; the next day's cron retries.
|
|
- No Claude / LLM calls. Layer B will layer triage on top of this foundation.
|