✨ Add branch front-door command
The branching model assumed a clean, current `main` and a green baseline before any edit, but both were prose, and prose nobody checks silently becomes a suggestion. `npm run branch -- <prefix>/<desc>` asserts the precondition and only then creates the branch, so a later failure is attributable to the change that caused it. Checks run cheap-first (`--porcelain` deliberately catches untracked files, which would otherwise ride onto the new branch) and the suite runs last, so an ineligible tree never pays for it. `main`'s remote is derived from its upstream rather than hardcoded: this repo has both `origin` and `origin_https`, and `main` tracks the latter, so a `git fetch origin main` currency check would compare against a ref that is never updated here. The baseline runs after switching to `main`, so a red `main` restores the starting branch instead of stranding the caller on it. The prefix stays a judgment call: the script validates it against the documented vocabulary instead of inferring it. Prose kept as index only — AGENTS.md points agents at the command from the task workflow, CONTRIBUTING.md owns the model and the bare-script tier (dropping its stale hardcoded count of "two" conveniences).
This commit is contained in:
1 parent
93cb37441d
commit
8efd13b2f4
4 files changed
+140
-2
No files matched your search
+3
-1
@@ -10,6 +10,7 @@ CI and review will bounce these even though `npm run check` and the linters don'
|
||||
- **A new `npm run` script must reuse an existing prefix** (`check:` / `fix:` / `test:` / `watch:` / `maintain:` / `publish:`). If none fits, that's a signal the script doesn't belong in the pipeline — not a reason to invent a new prefix. (see [Script prefix convention](#script-prefix-convention))
|
||||
- **`oxlint-disable` directives live in source, not `.oxlintrc.json`.** The trade-off must sit next to the code it silences. This is a _human_ last-resort convention; agents must not add these — see [AGENTS.md § Never do](./AGENTS.md#never-do). (rationale: README § Tooling decisions)
|
||||
- **Don't put slow / network / whole-project scans in `check` or pre-commit.** Advisory scans are not correctness gates; they belong under `maintain:`. (see [Feedback tiers](#feedback-tiers) and [Script prefix convention](#script-prefix-convention))
|
||||
- **New work starts with `npm run branch`, never a hand-written `git switch -c`/`git checkout -b`.** The command carries the branch precondition; branching around it skips the clean-tree, current-`main` and green-baseline checks, and the skip is invisible until a failure can no longer be attributed. (see [Branching model](#branching-model))
|
||||
- **There is no local `npm run publish`, and `publish:publint` / `publish:attw` don't go in `check`.** (see [Publishing workflow](#publishing-workflow))
|
||||
|
||||
## Commit messages
|
||||
@@ -31,7 +32,7 @@ Script names in `package.json` use a prefix that signals _when_ the script is in
|
||||
|
||||
A new script should pick the prefix that matches its lifecycle, not invent a new one. If no existing prefix fits, that's a signal the script doesn't belong in the standard pipeline.
|
||||
|
||||
Separately, some top-level scripts are **bare** (no prefix): the entry points that either run a single tool (`build`, `clean`) or aggregate a `prefix:*` family (`check`, `fix`, `test`, `watch`, `maintain`), plus two conveniences that compose across tiers: `verify` — composing `check` + `test:unit` into one whole-project correctness gate (it deliberately uses `test:unit` rather than `test` because `check` already runs `check:tsc`, so the type checker runs exactly once); and `release` — the maintainer-only release front-door (see [Publishing workflow](#publishing-workflow)). Bare commands are how you invoke a tier; the `prefix:*` scripts are what those tiers are made of.
|
||||
Separately, some top-level scripts are **bare** (no prefix): the entry points that either run a single tool (`build`, `clean`) or aggregate a `prefix:*` family (`check`, `fix`, `test`, `watch`, `maintain`), plus conveniences that compose across tiers: `verify` — composing `check` + `test:unit` into one whole-project correctness gate (it deliberately uses `test:unit` rather than `test` because `check` already runs `check:tsc`, so the type checker runs exactly once); and the two scripts that bracket a unit of work and own its git state — `branch` — assert the precondition the branching model assumes, then create the branch (see [Branching model](#branching-model)); and `release` — the maintainer-only release front-door (see [Publishing workflow](#publishing-workflow)). Bare commands are how you invoke a tier; the `prefix:*` scripts are what those tiers are made of.
|
||||
|
||||
## Feedback tiers
|
||||
|
||||
@@ -77,6 +78,7 @@ This is why every test in the suite pairs an `expectTypeOf(...)` with an `assert
|
||||
|
||||
- **Base branch:** `main`
|
||||
- **Branch naming:** `feature/<desc>` / `fix/<desc>` / `chore/<desc>`
|
||||
- **Starting work:** `npm run branch -- <prefix>/<desc>`. It refuses, without changing anything, unless the working tree is clean (untracked files included), no merge/rebase/cherry-pick is in progress, `main` matches its upstream, and `npm run test` is green on `main` — so a later failure is always attributable to your edits. The prefix is still _your_ call, inferred from the task; the script validates it rather than guessing it.
|
||||
- **Merging:** `git checkout main && git merge --no-ff <branch>` (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)).
|
||||
|
||||
Reference in new issue
Block a user