Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/PROJECT_FIELDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Every issue/PR attribute lives in **exactly one place**. Never dual-write the sa
| Priority | `priority:*` **labels** | `p0` critical / `p1` milestone-blocking / `p2` important / `p3` polish; Project views filter by label (`label:"priority:p0"`), no Priority field |
| Area | `area:*` **labels** | This library's own domains — see `.github/labels.yml` |
| Workflow status | **Project `Status` field** | `Backlog` / `Ready` / `In Progress` / `In Review` / `Blocked` / `Done`. Status is fully automatic: `Backlog`, `Ready` and `Blocked` are synced before 00:00 and 12:00 Taipei time every day from `Start` and blocked-by by the project-status-sync workflow in the shared board's home repository; `In Progress`, `In Review` and `Done` are set by PR and issue events. People only maintain the issues' blocked-by relations and `Start` on the board. |
| Target version | **Milestone** | `vX.Y.Z` releases, `gov-*` process phases; **no milestone = backlog** |
| Delivery checkpoint | **Milestone** | `M1`–`M4`, grouping work by delivery checkpoint; **no milestone = backlog**; release versions are git tags cut by release-please, not issue metadata |
Comment thread
TzuH-Hsu marked this conversation as resolved.
| Estimate | **Project `Estimate` field** (number) | Rough estimate in person-days. Replaces Effort (S/M/L); the Effort field is retired. An issue with a large estimate is split by its implementer into sub-issues before work starts; the parent stays as the progress roll-up (Sub-issues progress) Once an issue is split into sub-issues, its Estimate is cleared; the numbers stay on the sub-issues and the parent shows Sub-issues progress. |
| Schedule | **Project `Start` / `Target` fields** (date) | Scheduled start and finish. Target is the committed date; a milestone's due date is a different layer |
| Checkpoint | **Project `Checkpoint` field** (iteration) | Internal checkpoint, one level finer than a milestone; milestones are unchanged |
Expand Down Expand Up @@ -43,4 +43,4 @@ The issue form you pick still matters (it decides the body template); GitHub sim

- Labels: `.github/labels.yml` (declarative source of truth; sync = re-run `scripts/bootstrap.sh`)
- Project: the user-level board for the five implementation repositories, this one included. Issues are added by the board's built-in Auto-add workflow; `Status` is computed by a status-sync workflow outside this repository. Fields `Status`, `Estimate`, `Start`, `Target`, `Checkpoint` and the views are set up on the board — `docs/setup/project-views.md`
- Native issue types: set automatically by the issue forms in `.github/ISSUE_TEMPLATE/`
- Coarse type: the `type:bug` / `type:feature` labels (Type rows above); the issue forms in `.github/ISSUE_TEMPLATE/` cannot set a native issue type on a personal account
6 changes: 3 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Agent-specific entry files (`CLAUDE.md`, `GEMINI.md`, `.github/copilot-instructi

1. Work from an issue. If no issue exists for what you are about to do, create one first (see `skills/issue-writing/` — non-interactively, write the body in the form's shape so the labeler applies the labels).
2. Never commit directly to `main`. Branch as `<type>/<issue#>-<slug>` (e.g. `feat/42-label-sync`), open a PR.
3. Follow the metadata single-home contract in `.github/PROJECT_FIELDS.md` — every attribute (type, priority, area, status, version) lives in exactly one place. Never dual-write.
3. Follow the metadata single-home contract in `.github/PROJECT_FIELDS.md` — every attribute (type, priority, area, status, milestone) lives in exactly one place. Never dual-write.
4. Run `make verify` before opening or updating a PR.
5. Declare skipped validation levels in the PR body: `RISK: <level> not run — <reason>`. Never skip silently.
6. Never commit `*.local.md` files, secrets, or `.env*` files (only `.env.example` is allowed).
Expand Down Expand Up @@ -46,15 +46,15 @@ If this repository deliberately differs from the template it was created from, r

## Workflow

1. **Issue** — created via issue forms; native type (Bug/Feature/Task) is set by the form; labels for priority/area follow `.github/PROJECT_FIELDS.md`.
1. **Issue** — created via issue forms; the coarse type is carried by the `type:bug` / `type:feature` labels (neither means Task); labels for priority/area follow `.github/PROJECT_FIELDS.md`.
2. **Branch** — `<type>/<issue#>-<slug>`; the type is one of the Conventional Commit types this repository uses: `feat`, `fix`, `docs`, `chore`, `refactor`, `ci`, `test`, `perf` (this line is the one home for that list — `scripts/pr-lint.js` enforces it on the branch and its test asserts equality).
3. **Commits** — Conventional Commits, English, imperative (`feat: add label sync phase to bootstrap`).
4. **PR** — English title in Conventional Commit format; body follows the PR template: summary, linked issue (`Closes #N`), validation ladder checkboxes, `RISK:` lines, rollback notes. CI fails the PR when the branch name is malformed, or when GitHub reports that the PR does not close exactly one open issue in this repository — the branch's (`scripts/pr-lint.js` asks GitHub which issues the body closes, so a `Closes` line inside code does not count, and neither does an issue linked only in the sidebar — changing that link re-runs no check).
5. **Merge** — squash merge; the PR title becomes the commit message on `main`.

## AI agent conventions

- **Starting work**: an agent starts on an issue only when a human assigns it or names it in the conversation. There is no self-service queue; Project status `Ready` means "scoped", not "claimable by agents".
- **Starting work**: an agent starts on an issue only when a human assigns it or names it in the conversation. There is no self-service queue. Project status `Ready` is computed by the status sync from `Start` and blocked-by; it does not mean an issue can be picked up. `In Progress` is set automatically when a pull request is linked to the issue.
- **Provenance**: commit trailers (`Co-Authored-By`) and the PR author account are the audit trail. No provenance labels, no attribution line in the PR body.
- **Capability boundaries**: agents may manage issues, labels, milestones, and Project items via `gh`; agents must NOT perform destructive operations (deleting Project fields, force-pushing, rewriting history, changing repo settings) without explicit human approval in the conversation.
- **Session handoff**: long-running work may keep exactly one gitignored `HANDOVER.local.md` (hard cap ~150 lines, rewrite — don't append — at session end). Durable knowledge gets promoted to issues, ADRs, or skills, then deleted from the handoff file. See `skills/context-handoff/`.
Expand Down
14 changes: 7 additions & 7 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,12 @@ skipped level.

## Issues

- Always use the issue forms — never open a blank issue. The form you pick
sets the native GitHub issue type (Bug, Feature, Task).
- Always use the issue forms — never open a blank issue. The coarse type is
carried by the `type:bug` / `type:feature` labels; neither means Task. After
opening a Bug or Feature issue, add the matching label by hand.
- Priority and area labels are applied automatically by the labeler workflow;
you don't set them by hand.
- Every piece of issue/PR metadata (type, priority, area, status, version)
- Every piece of issue/PR metadata (type, priority, area, status, milestone)
lives in exactly one place. See `.github/PROJECT_FIELDS.md` for the
single-home contract — never dual-write the same attribute in two places.

Expand Down Expand Up @@ -80,10 +81,9 @@ Details: `skills/release-management/` (coming) and

## Milestones

- `vX.Y.Z` — a release commitment; issues attached to it are meant to ship in
that release.
- `gov-*` — process and governance work, not tied to a release.
- No milestone — backlog; not yet committed to a release.
- `M1`–`M4` — milestones group work by delivery checkpoint
(`.github/PROJECT_FIELDS.md`).
- No milestone — backlog.

## What not to commit

Expand Down
2 changes: 1 addition & 1 deletion docs/adr/ADR-0007-retire-agent-labels.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ADR-0007: Retire the `agent-ok` / `by-agent` label mechanism

- **Status**: Accepted
- **Status**: Accepted. Partially superseded 2026-10-08: `Ready` is computed by the status sync from `Start` and blocked-by, and `In Progress` is set when a pull request is linked (`docs/setup/project-views.md`)
- **Date**: 2026-09-07

## Context
Expand Down
6 changes: 3 additions & 3 deletions docs/adr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ Routine choices (a library patch bump, a wording tweak) do not get ADRs. When in

1. Copy `template.md` to `ADR-NNNN-short-slug.md` (next free number).
2. Write it in the PR that implements (or proposes) the decision.
3. Status lifecycle: `Proposed` → `Accepted` → (later) `Superseded by ADR-XXXX` or `Deprecated`. Never delete an ADR — supersede it.
3. Status lifecycle: `Proposed` → `Accepted` → (later) `Superseded by ADR-XXXX` or `Deprecated`. When a later change replaces only part of an accepted decision, the status reads `Accepted. Partially superseded <date>: <what changed and where it is now defined>`, and the body is left as it was. Never delete an ADR — supersede it.
Comment thread
TzuH-Hsu marked this conversation as resolved.

## Index

| ADR | Title | Status |
| --- | --- | --- |
| [ADR-0001](ADR-0001-adopt-adr.md) | Adopt Architecture Decision Records | Accepted |
| [ADR-0002](ADR-0002-release-flow.md) | Release flow: release-please with human-gated release PRs | Accepted |
| [ADR-0003](ADR-0003-metadata-single-home.md) | Metadata single-home policy | Accepted |
| [ADR-0007](ADR-0007-retire-agent-labels.md) | Retire the `agent-ok` / `by-agent` label mechanism | Accepted |
| [ADR-0003](ADR-0003-metadata-single-home.md) | Metadata single-home policy | Accepted. Partially superseded 2026-10-08 |
Comment thread
TzuH-Hsu marked this conversation as resolved.
| [ADR-0007](ADR-0007-retire-agent-labels.md) | Retire the `agent-ok` / `by-agent` label mechanism | Accepted. Partially superseded 2026-10-08 |
| [ADR-0008](ADR-0008-event-workflow-logic-in-scripts.md) | Event-driven workflow logic lives in `scripts/`, behind a thin `github-script` caller | Accepted |
2 changes: 1 addition & 1 deletion docs/adr/template.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ADR-NNNN: Title

- **Status**: Proposed | Accepted | Superseded by ADR-XXXX | Deprecated
- **Status**: Proposed | Accepted | Accepted. Partially superseded YYYY-MM-DD: … | Superseded by ADR-XXXX | Deprecated
- **Date**: YYYY-MM-DD
- **Issue**: #NNN (if any)

Expand Down
6 changes: 6 additions & 0 deletions docs/setup/bootstrap.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ On personal-account repos, see the "Personal accounts" note in

### 3. Milestone

> This repository's `scripts/bootstrap.sh` is not changed: phase 3 below still
> offers to create a `v0.1.0` milestone. This repository's milestones are
> `M1`–`M4`, so run bootstrap without `--yes` and answer no at the prompt:
> with `--yes` the phase creates `v0.1.0` without asking, and every such run
> creates it again. Close or delete a `v0.1.0` milestone the script created.
Comment thread
TzuH-Hsu marked this conversation as resolved.

Creates a `v0.1.0` milestone ("First release") if one doesn't already exist
(checked across both open and closed milestones, so a closed v0.1.0 is not
recreated).
Expand Down
5 changes: 4 additions & 1 deletion docs/template-drift.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ Files in the template that this repo does not have.
| .github/PROJECT_FIELDS.md | Coarse Type row points at `type:bug`/`type:feature` labels, not native issue type; adds Owner/assignee row and rule 5 | This repo is a personal-account repo, so native issue types are unavailable | #18 (personal-account rewrite), bootstrap (Owner/assignee row and rule 5) |
| docs/adr/ADR-0003-metadata-single-home.md (status line) | Marked partially superseded: the Project field set is defined in `.github/PROJECT_FIELDS.md` | The board's fields changed after the ADR was accepted | #67 |
| .github/PROJECT_FIELDS.md (authority map, rule 1, "Where things are defined"), docs/setup/project-views.md | Effort row replaced by Estimate (number); Schedule (`Start`/`Target`) and Checkpoint rows added; Priority row notes that views filter by label; rule 1 keeps the planning fields on the Project board only. project-views.md lists `Status`, `Estimate`, `Start`, `Target`, `Checkpoint` and the Now / Roadmap / Checkpoint / Milestone / By area / Blocked views. Both say `Status` is set automatically (a status-sync workflow outside this repository, and PR and issue events) and that issues reach the board through its built-in Auto-add workflow (`is:issue is:open`), so this repository holds no board workflow or secret. PROJECT_FIELDS.md also says a parent issue's `Estimate` is cleared once it is split into sub-issues, and that the status sync runs before 00:00 and 12:00 Taipei time every day; project-views.md says pull requests are not on the board and that the board's Pull request linked to issue workflow moves an issue to `In Progress` | The Project board's planning fields changed; Effort is retired; the board's Status and enrollment are automated on the board | #65 |
| CONTRIBUTING.md, .github/PROJECT_FIELDS.md ("Where things are defined", Delivery checkpoint row), docs/adr/README.md (lifecycle, index) | The coarse type is described as the `type:bug` / `type:feature` labels; milestones are `M1`–`M4`; the ADR lifecycle allows `Accepted. Partially superseded <date>: …`, and the index shows each ADR's status line | Native issue types cannot be applied on a personal account; the milestones group work by delivery checkpoint; part of an accepted decision can change without a new ADR | #69 |
| docs/setup/bootstrap.md (phase 3) | A note at the top of phase 3: the unchanged script still offers a `v0.1.0` milestone, so decline it | `scripts/bootstrap.sh` is not changed, while the milestones are `M1`–`M4` | #69 |
| AGENTS.md (Workflow, AI agent conventions), skills/agent-workflow/SKILL.md, skills/issue-writing/SKILL.md, skills/milestone-planning/SKILL.md, skills/release-management/SKILL.md (rule 5), skills/adr-writing/SKILL.md (rule 5), docs/adr/template.md (status line), docs/adr/ADR-0007-retire-agent-labels.md (status line) | The coarse type is carried by labels, added by hand for Bug and Feature; milestones are `M1`–`M4`, and release readiness is checked against open `priority:p0` / `priority:p1` issues instead of a milestone; `Ready` is computed by the status sync and does not mean an issue can be picked up; `In Progress` is set when a pull request is linked, not by hand; ADR-0007 is marked partially superseded; the ADR skill and template allow `Accepted. Partially superseded <date>: …` | The board's `Status` is automated | #69 |
Comment thread
TzuH-Hsu marked this conversation as resolved.
| docs/setup/bootstrap.md (phase 4) | A note at the top of phase 4: the unchanged script still creates a per-repository board with `Effort`, so skip the phase and check the shared board instead; and the script does not create Project views (instead of saying the API cannot) | `scripts/bootstrap.sh` is not changed for the shared board, and its phase 4 still creates a per-repository board, while the board is a shared user-level board | #67 |
| .github/labels.yml | `type:bug`/`type:feature` active (not commented out); `area:*` set is `update`/`health`/`docs`/`ci` instead of `docs`/`skills`/`ci`/`governance` | Personal-account Type fallback is in use; areas renamed to this library's own domains | #18 |
| .github/workflows/ci.yml | `runs-on` reads `vars.CI_RUNNER_LABELS`; header comments trimmed to the shorter public-repo runner-cost rationale; PR-lint step comment moved next to the step | Split the single `RUNNER_LABELS` into one variable per workflow so jobs can run on different runners | #20 |
Expand All @@ -84,7 +87,7 @@ Files in the template that this repo does not have.
| docs/setup/bootstrap.md | Reduced to the phases this repo's `scripts/bootstrap.sh` actually has | Only the phase-5 merge-settings block was ever synced from upstream after bootstrap; the rest are this repo's original, older phases (no phase 6/9, phase 2 misreports on personal accounts) | #13, bootstrap |
| docs/setup/licensing.md | Adds an "Adopter note" pointing out this repo predates upstream's phase 9 and has carried MIT attribution in `NOTICE` since bootstrap; "See also" links ADR-0004 by upstream URL | Explains why the phase-9 prompt described on the page never ran here | #18 |
| docs/setup/runners.md | Documents four variables (`CI_RUNNER_LABELS`, `AUTOMATION_RUNNER_LABELS`, `MAINTENANCE_RUNNER_LABELS`, `RELEASE_RUNNER_LABELS`) with a table, instead of the template's single `RUNNER_LABELS`; "measured" failure example is attributed to upstream, not this repo; ADR-0005 referenced by upstream URL | Per-workflow runner-variable split | #18 |
| scripts/bootstrap.sh | ~700 lines shorter than the template; only the phase-5 merge-settings block matches upstream | Same as docs/setup/bootstrap.md — this repo's copy predates most later upstream phases | #18, #13, #3, bootstrap |
| scripts/bootstrap.sh | Older than the template; only the phase-5 merge-settings block matches upstream | Same as docs/setup/bootstrap.md — this repo's copy predates most later upstream phases | #18, #13, #3, bootstrap |
| scripts/install-ci-tools.sh | `RUNNER_LABELS` comment updated to `*_RUNNER_LABELS`; markdownlint-cli2 pin kept current with upstream | Per-workflow runner-variable split | #20 |
| skills/anti-patterns/SKILL.md | Inherited-licence-leak row says this repo chose Apache-2.0 and has carried template attribution in `NOTICE` since bootstrap, instead of describing the generic bootstrap-phase-9 mechanism | Documents this repo's actual licence outcome | #18 |
| skills/github-actions-hygiene/SKILL.md | Rule 1 lists the four per-workflow runner variables instead of one `RUNNER_LABELS` | Per-workflow runner-variable split | #18 |
Expand Down
8 changes: 7 additions & 1 deletion skills/adr-writing/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Decisions made in a PR thread or a chat session evaporate — the context window
- **Decision** is written as a fact ("We use X for Y"), not a proposal or a debate summary.
- **Consequences** names what becomes easier *and* what becomes harder — an ADR that only lists benefits is marketing, not a decision record. Include accepted costs explicitly.
- **Alternatives considered** names each real alternative and *why not*, not just "we could have also...".
5. **Status lifecycle is one-directional**: `Proposed` → `Accepted` → (eventually) `Superseded by ADR-XXXX` or `Deprecated`. **Never edit an old ADR's Decision/Consequences to reflect a new choice, and never delete one.** Write a new ADR that supersedes it and update the old one's status line to point at the new number. History has to stay legible.
5. **Status lifecycle is one-directional**: `Proposed` → `Accepted` → (eventually) `Superseded by ADR-XXXX` or `Deprecated`. **Never edit an old ADR's Decision/Consequences to reflect a new choice, and never delete one.** When a new choice replaces the whole decision, write a new ADR that supersedes it and update the old one's status line to point at the new number. When a later change replaces only part of an accepted decision and that change is recorded elsewhere (a contract file or another ADR), set the status line to `Accepted. Partially superseded <date>: <what changed and where it is now defined>`, leave the body as it was, and update the index (`docs/adr/README.md`). History has to stay legible.
6. **Keep it under a page.** If the Context section needs subheadings, the decision is probably two decisions — split it.
7. **Update `docs/adr/README.md`'s index table in the same PR.** An ADR not in the index is functionally undiscoverable.
8. **Agents: read the relevant ADRs before changing behavior adjacent to one.** An ADR is a "do not helpfully fix this" fence — if a decision looks locally suboptimal but an ADR explains why it's that way on purpose, that's a signal to stop and ask, not to improve it silently.
Expand Down Expand Up @@ -48,6 +48,12 @@ Supersede an existing ADR instead of editing it:
- **Status**: Superseded by ADR-0007
```

Mark a partial supersession instead when only part of the decision changed:

```markdown
- **Status**: Accepted. Partially superseded YYYY-MM-DD: <what changed and where it is now defined>
```

Add the new ADR to the index in the same PR:

```markdown
Expand Down
Loading
Loading