From 06bc6bc43ebce72c0e1936cd622afa01a29ad3ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Tue, 8 Sep 2026 14:07:33 +0200 Subject: [PATCH] :memo: Document GitHub Flow branching model and agent task workflow Adopt single-developer GitHub Flow: branches off main with a feature/fix/chore prefix, merged back via 'git merge --no-ff' (local PR). Releases are not triggered by pushes; only the maintainer runs 'npm run release', which tags and pushes; CI publishes to npm on the tag. Gitea is the lab; GitHub is reserved for later promotion. Replace the 'not yet settled' backlog note in AGENTS.md with concrete agent instructions: a task with subtasks gets a branch, a leaf task is worked on the current branch, and a fixed handover template (Implemented / Judgement calls / Known problems) frames the pre-merge review. Drop the stale 'MR' reference in 'Never do' (no MR workflow). Update the feedback-tier table and publishing workflow to gate CI on push to main, not PRs. Check off the branching-model backlog group and open a task for the release CI workflow (blocked on a missing Gitea runner). --- AGENTS.md | 20 ++++++++++++++++++-- CONTRIBUTING.md | 31 +++++++++++++++++-------------- backlog.tasks | 8 ++++---- 3 files changed, 39 insertions(+), 20 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index ef54937..3057b43 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,7 +26,7 @@ Don't silence the type system to force a green run. As an agent these are forbid - `// oxlint-disable` / `// oxlint-disable-next-line` - `as` casts used to push an expression through (type-aware oxlint already flags unsafe assertions) -Fix the root cause with the type system instead — narrowing, generics, `satisfies`, conditional / mapped types, utility types (`NonNullable`, `Exclude`, …). TypeScript can express it; that's the intended tool. The `oxlint-disable`-location rule in [CONTRIBUTING.md § Rules the tools don't enforce](./CONTRIBUTING.md#rules-the-tools-dont-enforce) is a **human** last-resort convention (so a reviewer can spot a deliberate suppression) — it is not permission for you to add one. If the types genuinely cannot express something, stop and surface the conflict (commit message / MR) rather than suppress it. +Fix the root cause with the type system instead — narrowing, generics, `satisfies`, conditional / mapped types, utility types (`NonNullable`, `Exclude`, …). TypeScript can express it; that's the intended tool. The `oxlint-disable`-location rule in [CONTRIBUTING.md § Rules the tools don't enforce](./CONTRIBUTING.md#rules-the-tools-dont-enforce) is a **human** last-resort convention (so a reviewer can spot a deliberate suppression) — it is not permission for you to add one. If the types genuinely cannot express something, stop and surface the conflict (commit message / handover) rather than suppress it. The same applies to the checks themselves: **never `git commit --no-verify`** (or otherwise skip a pre-commit / pre-push hook). The checks are fast and offline, so a redundant run is fine — bypassing a hook to get green is the identical anti-pattern. If a commit already skipped a hook, redo it through one: `git reset --soft HEAD~1 && git commit -C `. @@ -36,7 +36,23 @@ Never start a long-lived / blocking process such as `npm run watch`. It runs unt `backlog.tasks` uses the vscode-todotasks format (not Markdown). A line ending in `:` is a project; every other line is a task. Status glyphs: `☐` open, `✔` done, `✘` cancelled; subtasks nest by indentation. Inline `@tags` carry metadata — `@done` / `@cancelled` mark completion, `@critical` / `@high` / `@low` / `@today` set priority. The `(…)` timestamp after `@done` is editor-generated: omit it when checking off by hand. -How a backlog task maps onto branches, review and release is **not yet settled** — see the branching-model task-group in `backlog.tasks`. Until that is documented, take branch and merge instructions from the user rather than inferring them. +### Working on tasks + +- **Task with subtasks** (a task that has indented children): create a branch off `main` using the prefix inferred from the task content (`feature/…` / `fix/…` / `chore/…`), check it out, work on each subtask with commits, then present a concise handover for the user to review. Use this fixed shape: + + ```md + ## Handover — + + **Implemented:** + **Judgement calls:** + **Known problems:** + ``` + + Once the user has no further objections, merge back: `git checkout main && git merge --no-ff `. The branching model is documented in [CONTRIBUTING.md § Branching model](./CONTRIBUTING.md#branching-model). + +- **Leaf task** (no indented children): implement on the current branch and commit. + +In both cases, follow [CONTRIBUTING.md § Testing discipline (type-driven)](./CONTRIBUTING.md#testing-discipline-type-driven). Each subtask gets one or more commits. ## Read these diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f1adf68..88c919c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -46,8 +46,8 @@ The tools are organized into a feedback ladder. Each tier catches different thin | `npm run verify` | manual | Definition of done: `npm run check` + unit tests, one shot | ~6s | | `npm run fix` | manual | Auto-resolve fixable issues (lint, format) | ~3s | | `npm run maintain` | manual / CI (advisory) | `maintain:knip` + `maintain:outdated` (whole-project + network scans) | ~10s | -| CI build (auto) | on push/PR | `npm run check` + `npm run test:ci` | ~30s+ | -| CI maintain (auto, non-blocking) | on push/PR | `npm run maintain` — reports, never fails the build | ~10s | +| CI build (auto) | on push to `main` | `npm run check` + `npm run test:ci` | ~30s+ | +| CI maintain (auto, non-blocking) | on push to `main` | `npm run maintain` — reports, never fails the build | ~10s | | CI publish (auto) | on tag | `publish:publint` + `publish:attw`, then `npm publish` | ~10s | ### Why these splits? @@ -71,18 +71,21 @@ For this library the types _are_ the feature — narrowing, `exhaustive()` retur This is why every test in the suite pairs an `expectTypeOf(...)` with an `assert.*` — keep them together. Type-first is also enforced structurally: `npm test` runs `check:tsc` before the test runner, so a wrong type can never be papered over by a passing assertion. Per [AGENTS.md § Never do](./AGENTS.md#never-do), reach green honestly — fix the types so both the type check and the runtime assertion pass, never suppress the ones you can't make pass. +## Branching model + +**GitHub Flow (single-developer).** Every change — feature, fix, refactor — branches off `main` and is merged back via a local commit (no PR workflow on Gitea yet). Collaborative review via Gitea UI is not in place — Gitea is the lab; when something is tested and ready for production it will be promoted to GitHub. + +- **Base branch:** `main` +- **Branch naming:** `feature/` / `fix/` / `chore/` +- **Merging:** `git checkout main && git merge --no-ff ` (local PR — review the diff yourself before closing the branch). +- CI runs `npm run check` + `npm run test:ci` on every push to `main` — this is the authoritative gate. +- **Releases are NOT triggered by pushes.** Only the maintainer triggers a release (see [Publishing workflow](#publishing-workflow)). + ## Publishing workflow -Publishing is CI-only by policy. Local `npm publish` is not supported. +Publishing is CI-only by policy. Local `npm publish` is not supported. The maintainer triggers releases from `main`: -1. Develop and merge PRs to `main`. -2. CI runs `npm run check` + `npm run test:ci` on every push and PR — this is the authoritative gate. -3. After all intended changes are on `main`, bump the version locally: - ```sh - npm version - ``` -4. Push the tag to the forge (Gitea): - ```sh - git push --follow-tags origin main - ``` -5. The `publish` CI job runs on the tag: `build` → `publish:publint` → `publish:attw` → `npm publish --access public`. The publish-tier checks must pass before the artifact is published. +1. All intended changes are merged to `main` and passing CI. +2. The maintainer runs `npm run release` — an interactive prompt suggests a version (based on the latest CHANGELOG entry); the maintainer confirms or edits it. +3. `scripts/release.sh` creates a single release commit (changelog + package.json bump, amended into one commit), tags it, and pushes everything to Gitea. +4. CI runs on the push: `npm run check` + `npm run test:ci` on the commit; then the `publish` job fires on the tag: `build` → `publish:publint` → `publish:attw` → `npm publish --access public`. The publish-tier checks must pass before the artifact is published. diff --git a/backlog.tasks b/backlog.tasks index c65c759..7a13c2b 100644 --- a/backlog.tasks +++ b/backlog.tasks @@ -5,12 +5,12 @@ Backlog and tracking for tiny-pattern-ts. Managed in vscode-todotasks format. --- Setup: -☐ Add gitea actions, blocked by missing runner => pi --session 01a06e72-e61e-7327-b3e9-3e11749a523f @high +☐ Establish release CI workflow: post-tag checks + npm publish after release tag push => pi --session 01a06e72-e61e-7327-b3e9-3e11749a523f @high ☐ Add gitea release page in CI @high ☐ Split off template into separate package => pi --session 01a07dde-7050-7054-bb36-1606d7eb2bc3 @high -☐ Document branching model @critical - ☐ Describe branching model: when to branch, base branch, naming - ☐ Clarify release workflow: who pushes `main`, CI as post-merge gate, drop PR usage +✔ Document branching model @done + ✔ Describe branching model: when to branch, base branch, naming + ✔ Clarify release workflow: who pushes `main`, CI as post-merge gate, drop PR usage v1.0: ☐ API surface is stable and fully typed