From 1ffd202a2a1f95a339bc8e9e5cd67ec016953edd Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 6 Jul 2026 16:10:10 +0000 Subject: [PATCH 1/7] chore: standards v6 --- .oxfmtrc.json | 2 +- .repometa.json | 2 +- .standards/pending.json | 55 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 .standards/pending.json diff --git a/.oxfmtrc.json b/.oxfmtrc.json index 55c15df..b21f646 100644 --- a/.oxfmtrc.json +++ b/.oxfmtrc.json @@ -1,4 +1,4 @@ { "$schema": "./node_modules/oxfmt/configuration_schema.json", - "ignorePatterns": [] + "ignorePatterns": ["dist", "**/dist", "coverage", "pnpm-lock.yaml", "CHANGELOG.md", ".standards/"] } diff --git a/.repometa.json b/.repometa.json index dd5af18..419900c 100644 --- a/.repometa.json +++ b/.repometa.json @@ -1,5 +1,5 @@ { - "standards": 1, + "standards": 6, "visibility": "oss", "since": 2026 } diff --git a/.standards/pending.json b/.standards/pending.json new file mode 100644 index 0000000..f6806be --- /dev/null +++ b/.standards/pending.json @@ -0,0 +1,55 @@ +{ + "schemaVersion": 1, + "fromVersion": 1, + "toVersion": 6, + "scopes": [ + "common", + "node" + ], + "visibility": "oss", + "exceptions": [], + "prompt": "You are migrating this repository to the Sebastian Software org standards.\n\nContext:\n- Previous standards version: 1, target: 6\n- Detected scopes: common, node\n- visibility: oss, since: 2026\n- Documented exceptions: []\n- The mechanical part (`standards apply`) has already been executed; managed\n files, seeded files, branding section and the version stamp are up to date.\n\nYour job is the judgement part: carry out the migration steps of the changelog\nentries below that apply to this repository, following the instructions.\n\n# Sebastian Software repository standards — agent instructions\n\nYou are updating a repository of the `sebastian-software` GitHub org to match\nthe org-wide standards defined in this package. Work changelog-driven, prefer\nthe CLI for mechanics, use judgement only where the changelogs require it.\n\n## The model\n\n- Every managed repository carries a `.repometa.json`:\n `{ \"standards\": , \"visibility\": \"oss\" | \"private\", \"since\": , \"exceptions\": [...] }`\n- `manifest.json` (in this package) defines the current standards version and,\n per scope, which files are **managed** (byte-exact sync), **seeded** (created\n once, repos may adapt them) and which README **sections** (marker-delimited\n blocks) are owned by the standards.\n- `changes/NNNN-*.md` are migration changelogs. Each declares the scopes it\n applies to and describes intent, mechanical steps and judgement calls.\n- Scopes are detected from the working tree: `common` always applies, `node`\n if `package.json` exists, `rust` if `Cargo.toml` exists (not yet defined).\n\n## Workflow\n\nHuman maintainers onboarding a brand-new repo (not handled by the agent\nyet): see [`docs/runbooks/onboard-repo.md`](docs/runbooks/onboard-repo.md)\nfor the full step-by-step procedure.\n\n1. Run `pnpm --config.minimum-release-age=0 dlx @sebastian-software/standards check`.\n If it reports nothing, you are done. The `--config.minimum-release-age=0` is\n mandatory on every `pnpm dlx @sebastian-software/standards …` call: pnpm 11\n holds back versions younger than 24h, but the stamp is written by `apply`\n against the true latest, so without the bypass `check` resolves a stale CLI\n and reports false drift right after a standards release.\n2. Run `standards apply`. It writes managed files, seeds missing ones, updates\n branding sections and bumps the stamp. This covers the mechanical part only.\n3. Read every entry in `changes/` with a number greater than the repo's\n previous `standards` stamp, skipping entries whose scope does not apply.\n Carry out their migration steps — this is the part that needs judgement\n (merging configs the repo has customised, removing replaced tooling,\n adjusting package.json scripts).\n4. Verify with the repo's own gate: `pnpm agent:check` if present, otherwise\n lint + format check + typecheck + build + test individually.\n5. Open a PR titled `chore: standards v`. Never push to the default branch.\n\n## Rules\n\n- **Never reintroduce Prettier.** oxfmt is the org formatter. If a repo still\n uses Prettier, migrating away from it is part of the job (see change 0001).\n- **Seeded files are owned by the repo.** Do not overwrite local adaptations —\n merge the intent of the change into them instead.\n- **Every content change to a seeded file requires a judgement step in\n the changelog that describes the merge strategy.** Without an explicit\n step, the change does not propagate to existing repos because\n `standards apply` never re-writes seeded files after first creation\n (see `src/apply.ts` `applySeeded`). Examples of what to spell out: new\n CI step to merge in, new ESLint rule, additional `cspell.json`\n dictionary entry, extra `oxlint.config.ts` override. State which lines come\n from the reference file and which repo-specific lines stay untouched.\n- **Branding sections are owned by the standards.** Never hand-edit content\n between `` markers; never\n remove the markers. `visibility: private` repos get the plain copyright\n footer, no marketing.\n- **Respect `exceptions`.** Entries in `.repometa.json#exceptions` document\n deliberate deviations (e.g. `\"keeps-prettier\"`). Skip the matching steps and\n leave the exceptions in place.\n- **Respect `platform`.** Manifest entries without `platform` apply to every\n repo. Entries with `platform` apply only on a repo whose\n `.repometa.json#platform` matches. Legacy repos without `platform` skip\n every platform-scoped entry until the migration step from change 0003 is\n carried out (`standards init --force --platform

`, then re-run apply).\n- **Do not invent standards.** If something is unclear or a reference file is\n missing for the repo's stack, stop and report instead of improvising.\n\n## Renovate onboarding\n\nWhen Renovate opens its first onboarding PR (`Configure Renovate`) on a\nnew consumer repo, it may write `local>sebastian-software/renovate-config`\nfor the preset reference. The canonical form is `github>...` — on\nForgejo workers, `local>` resolves Forgejo-resident while `github>`\nresolves GitHub-resident, and the preset repo lives on GitHub.\n\nBefore merging the onboarding PR, edit the file so both preset entries\nuse the `github>` prefix:\n\n```json\n{\n \"extends\": [\n \"github>sebastian-software/renovate-config\",\n \"github>sebastian-software/renovate-config:standards\"\n ]\n}\n```\n\nThe seeded `renovate.json` in this package already uses the canonical\nform, so once a repo is past onboarding, `standards apply` keeps it\ncorrect.\n\n## Merge policy\n\nThe final merge of a `standards:` PR is **always a human step**. Automerge\nis explicitly disabled for this PR class.\n\nReason: every standards update can implicitly shift tool behavior broadly\n(linter, formatter, CI rules); a human eye at the end is the cheapest\ninsurance against hallucinations or training-data drift in the agent or\nreview LLM. The `standards` package rule in the org Renovate preset stays\nwithout `automerge: true` for exactly this reason; do not add it \"for\nconsistency\".\n\nPipeline order for a `standards:` PR:\n\n1. Renovate opens the bump PR.\n2. **Agent run 1 — mechanics:** the external pull-mode agent works\n through `.standards/pending.json`, runs the repo's gate in CI mode\n (`agent:check:ci`, else `agent:check`) and uses the output as fix\n hints, commits the judgement changes to the branch and **always\n pushes** regardless of the check outcome, deletes the marker, removes\n the `standards:needs-agent` label, and sets `standards:needs-review`.\n3. **Agent run 2 — semantic pre-check:** the same agent runs with a\n fresh context, reads the resulting diff plus the relevant SKILL/\n changelog material, and posts a PR comment summarizing changes,\n verifying SKILL.md rules, and recommending `merge` or `hold`.\n4. **Human review:** maintainer reads the comment plus the diff and\n merges manually.\n\nUntil agent run 2 is wired, the maintainer reviews the diff manually\nagainst the SKILL.md rules without an LLM pre-comment.\n\n## Branch protection setup\n\nWithout branch protection on `main`, the consumer-repo CI guard is soft:\na maintainer (or a misclick) could merge a red state, and the\n`pending.json` guard loses its effect. The merge policy above presupposes\nhard required status checks; otherwise the whole CI gate architecture is\ndecorative.\n\n### GitHub\n\nRequired settings on `main` for every consumer repo:\n\n- `main` is a protected branch.\n- \"Require status checks to pass before merging\" enabled, with the\n CI workflow name (e.g. `ci`) listed as a required check.\n- \"Require branches to be up to date before merging\" enabled.\n- Optional but recommended: \"Require linear history\", \"Allow force\n pushes: none\", \"Allow deletions: none\".\n\nIdempotent one-shot via `gh`:\n\n```bash\ngh api repos/$ORG/$REPO/branches/main/protection -X PUT \\\n -F required_status_checks.strict=true \\\n -F required_status_checks.contexts[]=ci \\\n -F enforce_admins=true \\\n -F required_pull_request_reviews= \\\n -F restrictions=\n```\n\n### Forgejo\n\nForgejo's branch-rules API differs (`POST /repos/{owner}/{repo}/branch_protections`)\nbut the required setting set is equivalent: protect `main`, require the\nnamed CI check to pass, require branches up to date, disallow force pushes\nand deletions. Verification on Forgejo is deferred until the first Forgejo\nconsumer repo opts in; once it does, mirror the GitHub setup above and\nrecord the exact API payload here.\n\n## Pull-mode agent wiring (open)\n\nThe contract from `changes/0002-renovate-pending.md` makes\n`.standards/pending.json` the only on-branch indicator for open\njudgement steps. As long as no external agent listens for the marker\n(or the `standards:needs-agent` label), every drift PR sits with red CI\nand the cycle stays formally open.\n\n> **Status:** as long as the agent is not wired, the final merge — until\n> `.standards/pending.json` is gone — is **manual**. A maintainer can\n> always work the changelog steps locally as a fallback (`standards sync`).\n\nThe agent configuration itself lives outside this repo. This section\nkeeps the gap visible and pins the contract so the wiring effort\n(OpenClaw / Claude Code / Codex installation) has a stable target.\n\n### Two runs, one external wiring\n\nThe external agent infrastructure (webhook receiver or poller) handles\ntwo routing paths, each a separate run with a fresh context — no memory\ntransfer between runs:\n\n1. **Agent run 1 — mechanics.** Triggered by label\n `standards:needs-agent` or by the presence of\n `.standards/pending.json` in the PR diff. Reads `pending.json` — its\n `prompt` field carries the instructions and references the `changes`\n array for the changelog bodies (which are not duplicated into the\n prompt). Performs the judgement steps, and runs the repo's own gate in CI mode\n — prefer `agent:check:ci`, fall back to `agent:check` — using the\n output as hints to improve the changes. The checks are **not** a merge\n gate: the agent does not abort on a failing check and always commits\n and pushes its best-effort result to the PR branch (never the default\n branch — see step 5 of the Workflow). It then deletes\n `pending.json`, removes the `standards:needs-agent` label, and sets\n `standards:needs-review` (and/or writes\n `.standards/review-pending.json`) as the last step.\n\n The pull request's own full CI run plus human review are the actual\n gate; the maintainer reviews and merges. Providing CI-appropriate\n environment variables — so deploy/connection checks do not fail for\n lack of a local dev environment — is the external wiring's\n responsibility. See\n [#37](https://github.com/sebastian-software/standards/issues/37).\n\n When the agent's own gate run produced any failed or incomplete checks,\n it posts **one separate information comment** on the PR listing each such\n check with its output and noting that the automated environment may lack\n prerequisites (e.g. environment variables), so its results can differ\n from the PR's own CI run and a human should verify. This comment is\n posted only when there are failures, in addition to run 2's summary\n comment, and does not change the always-finalize behavior; the failures\n stay recorded. See\n [#40](https://github.com/sebastian-software/standards/issues/40).\n\n2. **Agent run 2 — semantic pre-check.** Triggered by label\n `standards:needs-review` or by `.standards/review-pending.json`. Reads\n the PR diff, the relevant changelog entries, `SKILL.md`, and\n `.repometa.json` (including `exceptions`) — nothing else. Posts a PR\n comment per the output contract below. Removes the\n `standards:needs-review` label and the marker file at the end.\n\nOn Forgejo, label setting is unreliable; the marker file\n(`.standards/pending.json` for run 1, `.standards/review-pending.json`\nfor run 2) is the **primary** signal. The labels are an optimization\nfor GitHub.\n\n### Agent run 2 — output contract\n\nRun 2 posts exactly one PR comment with four sections, in order:\n\n- **(a) Summary of agent changes.** Which files were modified, mapped\n to the judgement steps from which changelog.\n- **(b) SKILL.md consistency check.** Tabular pass/fail per rule\n (Prettier reintroduction, branding markers intact, `exceptions`\n respected, seeded-vs-managed contract upheld).\n- **(c) Judgement step verdicts.** Each step from each changelog with\n one of `ok` / `check` (the latter for steps the agent could not\n definitively verify).\n- **(d) Recommendation.** Exactly one of `merge` or `hold`, with a\n one-sentence reason.\n\nThe prompt template for run 2 is checked in at\n`reference/agent/review-prompt.md` so the external wiring uses a\nversioned template (analog to how `SKILL.md` anchors the run-1 prompt\nindirectly via the `buildPrompt` output).\n\n### Fallback\n\nIf run 2 fails (agent system down, label stuck, marker file deleted by\na rebase), the maintainer does the review manually against the SKILL.md\nrules and merges without the LLM pre-comment. The merge policy stays\nthe same: humans always merge.\n\n### External wiring follow-up\n\nThe external wiring (webhook receiver, prompt routing, secrets) is\ntracked outside this repo. One follow-up ticket covers both runs — they\nshare the same infrastructure, only the trigger path and prompt mode\ndiffer. Link to the external ticket goes here once it exists.\n\n\n## Changelog entries to execute\n\nThe full body of each entry is in the `changes` array of\n`.standards/pending.json` (fields: file, version, scopes, content). Work\nthrough them in order:\n\n- 0002-renovate-pending.md (v2)\n- 0003-platform-aware-ci.md (v3)\n- 0004-ci-workflow-forgejo.md (v4)\n- 0005-oxfmt-ignore-patterns.md (v5)\n- 0006-pnpm-minimum-release-age-exclude.md (v6)\n\n## Validation\n\nAfter applying the judgement steps, run the repository's own quality gate to\nguide your changes. Prefer `pnpm agent:check:ci` and fall back to\n`pnpm agent:check` when the `:ci` script is absent; the gate runs in CI mode.\n\nTreat the check output as hints to improve your changes, not as a merge gate:\n\n- Do not stop at the first failing check. Run the gate to completion, collect\n every failure, and fix what you reasonably can while applying the changesets.\n- Your work is complete after these best-effort fixes, even if some checks still\n fail — never withhold or revert your commits because a check is red.\n- If any check still failed or could not complete, post one separate\n information comment on the pull request that lists each such check with its\n output and notes that this automated environment may lack prerequisites (for\n example environment variables) that the pull request's own CI run has, so a\n human can verify. Post it only when there are failed or incomplete checks; if\n everything passed, post no such comment. It is in addition to the summary\n comment and does not change the always-finalize behavior above.\n", + "changes": [ + { + "file": "0002-renovate-pending.md", + "version": 2, + "scopes": [ + "common" + ], + "content": "# 0002 — Renovate as the single driver for standards updates\n\n- **Scopes:** common\n- **Standards version:** 2\n\n## Intent\n\nFrom this version on, standards updates across all managed repos are\ninitiated uniformly by the self-hosted Renovate server. Renovate handles\nboth version detection (bump of `.repometa.json#standards`) and the\nmechanical sync (`standards apply`). The judgement part is performed by\nan externally triggered LLM agent (OpenClaw, local Claude Code, or\nCodex) in pull mode. The agent deliberately does not run inside the\nRenovate container.\n\n## Mechanical steps (covered by `standards apply`)\n\n- No file changes in target repos beyond what previous changelogs already\n required. The `.standards/pending.json` marker is written by Renovate's\n `postUpgradeTasks` step when `standards apply --emit-pending` is invoked\n with a non-zero set of pending judgement entries.\n\n## Judgement steps (agent work)\n\n- None at consumer level. This change is structural — it changes how\n future versions of `@sebastian-software/standards` are rolled out across\n the org, not what consumer repos contain.\n\n## Notes\n\n- **Per-repo bootstrap (manual, one-off):** the repo carries the existing\n `managed-deps` topic of the Renovate server. `.repometa.json` is\n created by running `pnpm dlx @sebastian-software/standards init`\n (interactive prompts for visibility and initial year; flags are\n available for CI). The onboarding PR (`Configure Renovate`) has to\n be merged once before standards drift becomes visible in the UI.\n- **Renovate server prerequisite:** a custom datasource reads\n `manifest.json#currentVersion` as an integer directly from the\n standards repo instead of using the published package's npm semver.\n This keeps the version model stack-agnostic (Rust, doc-only, or mixed\n repos never see a semver) and the PR title follows the\n `chore: standards v` convention from `SKILL.md`.\n- **Self-hosted Renovate is mandatory.** Mend Cloud (the GitHub App)\n does not support `postUpgradeTasks`. `allowedCommands` must permit\n the exact `apply` invocation; `executionMode: \"branch\"` avoids known\n problems with `executionMode: \"update\"`.\n- **Pull model:** Renovate opens the PR and writes\n `.standards/pending.json` when judgement steps exist. An external\n agent consumes the file, commits the judgement changes onto the same\n branch, and removes any `standards:needs-agent` label that was set.\n On Forgejo, label setting is less reliable than on GitHub — pull\n consumers should filter primarily on the presence of the file, not\n exclusively on the label.\n- **`apply` write contract:** `standards apply` writes only to\n (a) managed/seeded targets declared in `manifest.json`,\n (b) branding sections declared in `manifest.json`,\n (c) `.repometa.json#standards`, and (d) the path passed via\n `--emit-pending`. This makes broad `fileFilters: [\"**/*\"]` safe on the\n server side.\n- **Consumer tools outside the standards scopes** (Prettier leftovers\n during migration, Biome, gitleaks, dependabot configs,\n semantic-release pipelines, custom lint scripts) must ignore\n `.standards/` themselves when they would otherwise scan every repo\n path. The standards-scope ignore files (`oxfmtignore`, `cspell.json`)\n are maintained by the standards package; foreign tools are out of\n scope.\n" + }, + { + "file": "0003-platform-aware-ci.md", + "version": 3, + "scopes": [ + "common", + "node" + ], + "content": "# 0003 — Platform-aware manifest and GitHub Actions CI seed\n\n- **Scopes:** common, node\n- **Standards version:** 3\n\n## Intent\n\nEstablishes a `platform` field on `.repometa.json` (`\"github\" | \"forgejo\"`)\nand an optional `platform` field per manifest entry, so the CI\ninfrastructure ships platform-aware from the start. As the first\napplication, the GitHub Actions CI workflow ships as a seeded file on\nrepos with `platform: \"github\"`. Forgejo follows in a later changelog\n(see issue #10).\n\n## Mechanical steps (covered by `standards apply`)\n\n- Existing managed and seeded entries without `platform` continue to\n apply to every repo regardless of the `platform` stamp.\n- The new seeded entry `reference/node/github-workflows-ci.yml` →\n `.github/workflows/ci.yml` carries `platform: \"github\"` and lands\n only on GitHub repos. On Forgejo or legacy (no `platform`) repos,\n `standards apply` skips the entry — no write, no drift finding from\n `standards check`.\n\n## Judgement steps (agent work)\n\n1. **Set the repo platform (legacy migration).** If `.repometa.json`\n has no `platform` field, run\n `standards init --force --platform

` with `

` equal to\n `github` or `forgejo` depending on where the repo lives, then re-run\n `standards apply`. This step is a one-shot migration; new repos\n created via `standards init` write `platform` from the start.\n2. **GitHub repos (`platform: \"github\"`): merge the seeded CI\n workflow with any existing one.** `standards apply` seeds\n `.github/workflows/ci.yml` only when the file does not exist. If a\n repo already has a CI workflow, merge the reference content with\n the repo-specific steps (the seeded-files update rule from `#16`\n applies): take the pending guard, the `pnpm run --if-present` chain\n and the `pnpm dlx @sebastian-software/standards check` step from\n the reference; preserve repo-specific extras (custom matrix,\n deploy step, etc.) untouched.\n3. **Forgejo repos (`platform: \"forgejo\"`): no CI seed in this\n version.** A separate changelog will add a Forgejo CI workflow\n under `.forgejo/workflows/ci.yml` (issue #10). Until then, Forgejo\n repos keep their existing CI configuration unchanged.\n\n## Notes\n\n- The `platform` field becomes part of the `.repometa.json` schema\n going forward. `standards init` prompts for it (default derived\n from `git remote get-url origin`: `github.com` → `github`,\n everything else → `forgejo`), and accepts the `--platform` flag for\n non-interactive use.\n- Filter contract: an entry without `platform` applies everywhere; an\n entry with `platform` applies only on a repo whose\n `.repometa.json#platform` matches. Legacy repos without `platform`\n receive no platform-scoped entries — the migration step above\n closes the gap.\n- The seeded CI workflow's `pnpm/action-setup` version stays at\n whatever major is current at apply time (Renovate keeps it up to\n date once the file is in place).\n- The matching `.repometa.json` stamp in the fixture repo\n `standards-test-repo2` is tracked separately as\n `sebastian-software/standards-test-repo2#4`.\n" + }, + { + "file": "0004-ci-workflow-forgejo.md", + "version": 4, + "scopes": [ + "node" + ], + "content": "# 0004 — Forgejo Actions CI seed\n\n- **Scopes:** node\n- **Standards version:** 4\n\n## Intent\n\nExtends the platform-aware CI infrastructure from change 0003 with a\nForgejo Actions variant. A second reference file\n(`reference/node/forgejo-workflows-ci.yml`) ships the same pipeline\nshape as the GitHub Actions seed, but targets the Forgejo runner\n(`runs-on: docker`, explicit container image) and lands under\n`.forgejo/workflows/ci.yml`.\n\nThis is purely additive on the GitHub side: GitHub repos see no new\nseed entries. Forgejo repos at `platform: \"forgejo\"` get the new file\nthe next time `standards apply` runs.\n\n## Mechanical steps (covered by `standards apply`)\n\n- The new seeded entry `reference/node/forgejo-workflows-ci.yml` →\n `.forgejo/workflows/ci.yml` carries `platform: \"forgejo\"` and lands\n only on Forgejo repos. On GitHub or legacy (no `platform`) repos,\n `standards apply` skips the entry — no write, no drift finding.\n\n## Judgement steps (agent work)\n\n1. **GitHub repos (`platform: \"github\"`): no action.** This change is\n not visible. The `standards apply` stamp moves to 4 without any\n file write beyond the stamp itself.\n2. **Forgejo repos (`platform: \"forgejo\"`): merge the seeded CI\n workflow with any existing one.** `standards apply` seeds\n `.forgejo/workflows/ci.yml` only when the file does not exist. If\n a Forgejo repo already has a CI workflow under\n `.forgejo/workflows/`, merge the reference content with the\n repo-specific steps (the seeded-files update rule from issue #16\n applies): take the pending guard, the `pnpm run --if-present`\n chain, and the `pnpm dlx @sebastian-software/standards check`\n step from the reference; preserve repo-specific extras (custom\n matrix, deploy step, etc.) untouched.\n3. **Legacy repos (no `platform`): set the platform first.** The\n migration step from change 0003 still applies. Until\n `.repometa.json#platform` is set, the standards stamp does not\n bump (the legacy gate from change 0003 blocks it). Run\n `standards init --force --platform ` first, then\n re-run `standards apply`.\n\n## Notes\n\n- Forgejo Actions discovers workflows in `.forgejo/workflows/` first\n and falls back to `.github/workflows/` if the Forgejo path does\n not exist. Repos that already mirror a GitHub workflow into\n `.github/workflows/` keep working — the seed adds a Forgejo-native\n workflow that takes precedence once present.\n- `runs-on: docker` plus an explicit\n `container: code.forgejo.org/oci/node:24-bookworm` keeps the seed\n reproducible across Forgejo runner pools without depending on\n registered `ubuntu-latest` labels. The pipeline shape mirrors the\n GitHub seed (pnpm + Node 24, `pnpm run --if-present` chain, drift\n check).\n- The matching `.repometa.json` stamp in the fixture repo\n `standards-test-repo2` is tracked separately as\n `sebastian-software/standards-test-repo2#5`.\n" + }, + { + "file": "0005-oxfmt-ignore-patterns.md", + "version": 5, + "scopes": [ + "node" + ], + "content": "# 0005 — oxfmt ignores move into `.oxfmtrc.json` `ignorePatterns`\n\n- **Scopes:** node\n- **Standards version:** 5\n\n## Intent\n\nDrop the standalone `.oxfmtignore` file and express the formatter's ignore\nlist through the `ignorePatterns` key inside the managed `.oxfmtrc.json`.\n\n## Problem\n\noxfmt has no concept of an `.oxfmtignore` file. By default it only\nauto-discovers `.gitignore` and `.prettierignore`\n([oxc.rs ignore files](https://oxc.rs/docs/guide/usage/formatter/ignore-files.html)).\nThe previous baseline shipped a `.oxfmtignore` and made it work by passing\n`--ignore-path .oxfmtignore` in the `format` / `format:check` package\nscripts. That had two defects:\n\n- **Only the npm scripts honoured it.** Any other entry point — the editor\n LSP (`oxfmt --lsp`), a bare `oxfmt .`, or a downstream repo that does not\n replicate the exact flag — silently ignored the file and could reformat or\n flag `dist`, `pnpm-lock.yaml`, `CHANGELOG.md` and `.standards/`.\n- **The filename implied auto-discovery that does not exist**, so the\n indirection was easy to miss and easy to break.\n\nRenaming to `.prettierignore` would make auto-discovery work, but it directly\ncontradicts change 0001, which removes Prettier and deletes `.prettierignore`\nas part of the migration.\n\noxfmt's documented, recommended mechanism is `ignorePatterns` in the config\nfile: formatter-specific, uses `.gitignore` syntax, and applies globally —\nCLI, LSP and bare invocations alike — without any flag. Because `.oxfmtrc.json`\nis already a managed file, the patterns now propagate byte-exact to every repo.\n\n## Mechanical steps (covered by `standards apply`)\n\n- Rewrite the managed `.oxfmtrc.json` so `ignorePatterns` carries\n `dist`, `**/dist`, `coverage`, `pnpm-lock.yaml`, `CHANGELOG.md`,\n `.standards/`.\n\n## Judgement steps (agent work, node scope)\n\n`standards apply` writes managed files but never deletes files it no longer\nmanages, and it does not edit `package.json`. Spell these out per repo:\n\n1. **Delete the obsolete `.oxfmtignore`.** Its patterns now live in\n `.oxfmtrc.json#ignorePatterns`. Remove any repo-specific extra patterns by\n carrying them over into `ignorePatterns` first, then delete the file.\n2. **Drop the `--ignore-path` indirection from package scripts.** Change the\n `format` / `format:check` scripts to plain `oxfmt --write .` /\n `oxfmt --check .`. Remove any other `--ignore-path .oxfmtignore` usage.\n\n## Notes\n\n- `node_modules` and lock files such as `pnpm-lock.yaml` are ignored by oxfmt\n by default; keeping `pnpm-lock.yaml` in `ignorePatterns` is harmless and\n documents intent.\n- Future formatter ignores belong in `ignorePatterns`, not in a separate\n ignore file.\n" + }, + { + "file": "0006-pnpm-minimum-release-age-exclude.md", + "version": 6, + "scopes": [ + "node" + ], + "content": "# 0006 — pnpm: load own org packages without the release cooldown\n\n- **Scopes:** node\n- **Standards version:** 6\n\n## Intent\n\nExempt the organisation's own npm packages from pnpm's supply-chain release\ncooldown so internal releases install immediately instead of after a one-day\ndelay.\n\n## Problem\n\npnpm 11 enables supply-chain protection by default: `minimumReleaseAge`\ndefaults to `1440` minutes (24h), so a freshly published version is not\nresolved until it is at least a day old\n([pnpm 11 release notes](https://pnpm.io/blog/releases/11.0)). That cooldown\nis desirable for third-party dependencies, but it also stalls the org's own\npackages — a just-released `@sebastian-software/*` or `@sebastian-gmbh/*`\nversion is invisible to consumers for 24h, which blocks fast internal\niteration and back-to-back releases.\n\npnpm's `minimumReleaseAgeExclude` accepts name patterns\n([pnpm 10.17 notes](https://pnpm.io/blog/releases/10.17)), so the two org\nscopes can opt out of the cooldown while every other dependency keeps it.\n\n## Mechanical steps (covered by `standards apply`)\n\nNone. `pnpm-workspace.yaml` is not a managed or seeded file (its `allowBuilds`\nlist is repo-specific), so `standards apply` does not write it. The reference\n`reference/node/pnpm-workspace.yaml` documents the canonical shape; adoption is\na judgement step.\n\n## Judgement steps (agent work, node scope)\n\n1. **Add the org cooldown exclusion to `pnpm-workspace.yaml`.** Add (or extend)\n a top-level `minimumReleaseAgeExclude` list with the two org scopes:\n\n ```yaml\n minimumReleaseAgeExclude:\n - \"@sebastian-gmbh/*\"\n - \"@sebastian-software/*\"\n ```\n\n Keep any repo-specific existing entries and merge the two org globs in;\n do not touch `packages:` or `allowBuilds:`. If the repo has no\n `pnpm-workspace.yaml`, create one from `reference/node/pnpm-workspace.yaml`.\n\n## Notes\n\n- This only opts the two org scopes out of the cooldown; `minimumReleaseAge`\n keeps its secure default for all third-party dependencies.\n- Pattern syntax follows pnpm's `minimumReleaseAgeExclude`; exact versions or\n `||` disjunctions also work but are not needed here.\n" + } + ] +} From 34496082481650f0afe5169becb835eec64d66c1 Mon Sep 17 00:00:00 2001 From: OpenClaw Standards PR Agent Date: Mon, 6 Jul 2026 18:13:50 +0200 Subject: [PATCH 2/7] standards: apply changeset 2 From cbe241fd25bec93efe71ce4643bda03ffab84f19 Mon Sep 17 00:00:00 2001 From: OpenClaw Standards PR Agent Date: Mon, 6 Jul 2026 18:16:35 +0200 Subject: [PATCH 3/7] standards: apply changeset 3 --- .github/workflows/ci.yml | 11 ++++++++++- .repometa.json | 3 ++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 30a79f9..bd0dee7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,4 +18,13 @@ jobs: node-version: 24 cache: pnpm - run: pnpm install --frozen-lockfile - - run: pnpm agent:check + - name: "Guard: agent step not yet completed for this PR" + run: test ! -f .standards/pending.json + - run: pnpm run --if-present lint + - run: pnpm run --if-present format:check + - run: pnpm run --if-present typecheck + - run: pnpm run --if-present build + - run: pnpm run --if-present test + # minimum-release-age=0 mirrors the standards apply invocation: pnpm 11 + # otherwise holds back very fresh standards releases and can report false drift. + - run: pnpm --config.minimum-release-age=0 dlx @sebastian-software/standards check diff --git a/.repometa.json b/.repometa.json index 419900c..a099684 100644 --- a/.repometa.json +++ b/.repometa.json @@ -1,5 +1,6 @@ { "standards": 6, "visibility": "oss", - "since": 2026 + "since": 2026, + "platform": "github" } From 63ddc164797648cef389b8b22738f846c6ddf9ea Mon Sep 17 00:00:00 2001 From: OpenClaw Standards PR Agent Date: Mon, 6 Jul 2026 18:17:04 +0200 Subject: [PATCH 4/7] standards: apply changeset 4 From b5f1408ff96cb57192e991f3f908adb02b9dfa80 Mon Sep 17 00:00:00 2001 From: OpenClaw Standards PR Agent Date: Mon, 6 Jul 2026 18:17:47 +0200 Subject: [PATCH 5/7] standards: apply changeset 5 --- .oxfmtignore | 6 ------ package.json | 4 ++-- 2 files changed, 2 insertions(+), 8 deletions(-) delete mode 100644 .oxfmtignore diff --git a/.oxfmtignore b/.oxfmtignore deleted file mode 100644 index a4d523f..0000000 --- a/.oxfmtignore +++ /dev/null @@ -1,6 +0,0 @@ -dist -**/dist -coverage -node_modules -pnpm-lock.yaml -CHANGELOG.md diff --git a/package.json b/package.json index 6e8f261..3fa0c4e 100644 --- a/package.json +++ b/package.json @@ -24,8 +24,8 @@ "lint": "pnpm lint:oxlint && pnpm lint:eslint", "lint:eslint": "eslint . --max-warnings=0", "lint:oxlint": "oxlint -c oxlint.config.ts", - "format": "oxfmt --ignore-path .oxfmtignore --write .", - "format:check": "oxfmt --ignore-path .oxfmtignore --check .", + "format": "oxfmt --write .", + "format:check": "oxfmt --check .", "check:standards": "standards check", "agent:check": "pnpm lint && pnpm format:check && pnpm typecheck && pnpm build && pnpm test && pnpm check:standards" }, From c08172e4d35d2d98fd59c2b447f1cf7047ca7439 Mon Sep 17 00:00:00 2001 From: OpenClaw Standards PR Agent Date: Mon, 6 Jul 2026 18:18:33 +0200 Subject: [PATCH 6/7] standards: apply changeset 6 --- package.json | 2 +- pnpm-workspace.yaml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 3fa0c4e..5f841cb 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "lint:oxlint": "oxlint -c oxlint.config.ts", "format": "oxfmt --write .", "format:check": "oxfmt --check .", - "check:standards": "standards check", + "check:standards": "pnpm --config.minimum-release-age=0 dlx @sebastian-software/standards check", "agent:check": "pnpm lint && pnpm format:check && pnpm typecheck && pnpm build && pnpm test && pnpm check:standards" }, "devDependencies": { diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 9af53e8..644856b 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -23,4 +23,6 @@ allowBuilds: esbuild: true unrs-resolver: true minimumReleaseAgeExclude: + - "@sebastian-gmbh/*" + - "@sebastian-software/*" - "@sebastian-software/standards@0.2.0" From 76e8f3bb5efafce8c0481c773d4d6602623dee69 Mon Sep 17 00:00:00 2001 From: OpenClaw Standards PR Agent Date: Mon, 6 Jul 2026 18:19:55 +0200 Subject: [PATCH 7/7] standards: remove completed pending changes --- .standards/pending.json | 55 ----------------------------------------- 1 file changed, 55 deletions(-) delete mode 100644 .standards/pending.json diff --git a/.standards/pending.json b/.standards/pending.json deleted file mode 100644 index f6806be..0000000 --- a/.standards/pending.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "schemaVersion": 1, - "fromVersion": 1, - "toVersion": 6, - "scopes": [ - "common", - "node" - ], - "visibility": "oss", - "exceptions": [], - "prompt": "You are migrating this repository to the Sebastian Software org standards.\n\nContext:\n- Previous standards version: 1, target: 6\n- Detected scopes: common, node\n- visibility: oss, since: 2026\n- Documented exceptions: []\n- The mechanical part (`standards apply`) has already been executed; managed\n files, seeded files, branding section and the version stamp are up to date.\n\nYour job is the judgement part: carry out the migration steps of the changelog\nentries below that apply to this repository, following the instructions.\n\n# Sebastian Software repository standards — agent instructions\n\nYou are updating a repository of the `sebastian-software` GitHub org to match\nthe org-wide standards defined in this package. Work changelog-driven, prefer\nthe CLI for mechanics, use judgement only where the changelogs require it.\n\n## The model\n\n- Every managed repository carries a `.repometa.json`:\n `{ \"standards\": , \"visibility\": \"oss\" | \"private\", \"since\": , \"exceptions\": [...] }`\n- `manifest.json` (in this package) defines the current standards version and,\n per scope, which files are **managed** (byte-exact sync), **seeded** (created\n once, repos may adapt them) and which README **sections** (marker-delimited\n blocks) are owned by the standards.\n- `changes/NNNN-*.md` are migration changelogs. Each declares the scopes it\n applies to and describes intent, mechanical steps and judgement calls.\n- Scopes are detected from the working tree: `common` always applies, `node`\n if `package.json` exists, `rust` if `Cargo.toml` exists (not yet defined).\n\n## Workflow\n\nHuman maintainers onboarding a brand-new repo (not handled by the agent\nyet): see [`docs/runbooks/onboard-repo.md`](docs/runbooks/onboard-repo.md)\nfor the full step-by-step procedure.\n\n1. Run `pnpm --config.minimum-release-age=0 dlx @sebastian-software/standards check`.\n If it reports nothing, you are done. The `--config.minimum-release-age=0` is\n mandatory on every `pnpm dlx @sebastian-software/standards …` call: pnpm 11\n holds back versions younger than 24h, but the stamp is written by `apply`\n against the true latest, so without the bypass `check` resolves a stale CLI\n and reports false drift right after a standards release.\n2. Run `standards apply`. It writes managed files, seeds missing ones, updates\n branding sections and bumps the stamp. This covers the mechanical part only.\n3. Read every entry in `changes/` with a number greater than the repo's\n previous `standards` stamp, skipping entries whose scope does not apply.\n Carry out their migration steps — this is the part that needs judgement\n (merging configs the repo has customised, removing replaced tooling,\n adjusting package.json scripts).\n4. Verify with the repo's own gate: `pnpm agent:check` if present, otherwise\n lint + format check + typecheck + build + test individually.\n5. Open a PR titled `chore: standards v`. Never push to the default branch.\n\n## Rules\n\n- **Never reintroduce Prettier.** oxfmt is the org formatter. If a repo still\n uses Prettier, migrating away from it is part of the job (see change 0001).\n- **Seeded files are owned by the repo.** Do not overwrite local adaptations —\n merge the intent of the change into them instead.\n- **Every content change to a seeded file requires a judgement step in\n the changelog that describes the merge strategy.** Without an explicit\n step, the change does not propagate to existing repos because\n `standards apply` never re-writes seeded files after first creation\n (see `src/apply.ts` `applySeeded`). Examples of what to spell out: new\n CI step to merge in, new ESLint rule, additional `cspell.json`\n dictionary entry, extra `oxlint.config.ts` override. State which lines come\n from the reference file and which repo-specific lines stay untouched.\n- **Branding sections are owned by the standards.** Never hand-edit content\n between `` markers; never\n remove the markers. `visibility: private` repos get the plain copyright\n footer, no marketing.\n- **Respect `exceptions`.** Entries in `.repometa.json#exceptions` document\n deliberate deviations (e.g. `\"keeps-prettier\"`). Skip the matching steps and\n leave the exceptions in place.\n- **Respect `platform`.** Manifest entries without `platform` apply to every\n repo. Entries with `platform` apply only on a repo whose\n `.repometa.json#platform` matches. Legacy repos without `platform` skip\n every platform-scoped entry until the migration step from change 0003 is\n carried out (`standards init --force --platform

`, then re-run apply).\n- **Do not invent standards.** If something is unclear or a reference file is\n missing for the repo's stack, stop and report instead of improvising.\n\n## Renovate onboarding\n\nWhen Renovate opens its first onboarding PR (`Configure Renovate`) on a\nnew consumer repo, it may write `local>sebastian-software/renovate-config`\nfor the preset reference. The canonical form is `github>...` — on\nForgejo workers, `local>` resolves Forgejo-resident while `github>`\nresolves GitHub-resident, and the preset repo lives on GitHub.\n\nBefore merging the onboarding PR, edit the file so both preset entries\nuse the `github>` prefix:\n\n```json\n{\n \"extends\": [\n \"github>sebastian-software/renovate-config\",\n \"github>sebastian-software/renovate-config:standards\"\n ]\n}\n```\n\nThe seeded `renovate.json` in this package already uses the canonical\nform, so once a repo is past onboarding, `standards apply` keeps it\ncorrect.\n\n## Merge policy\n\nThe final merge of a `standards:` PR is **always a human step**. Automerge\nis explicitly disabled for this PR class.\n\nReason: every standards update can implicitly shift tool behavior broadly\n(linter, formatter, CI rules); a human eye at the end is the cheapest\ninsurance against hallucinations or training-data drift in the agent or\nreview LLM. The `standards` package rule in the org Renovate preset stays\nwithout `automerge: true` for exactly this reason; do not add it \"for\nconsistency\".\n\nPipeline order for a `standards:` PR:\n\n1. Renovate opens the bump PR.\n2. **Agent run 1 — mechanics:** the external pull-mode agent works\n through `.standards/pending.json`, runs the repo's gate in CI mode\n (`agent:check:ci`, else `agent:check`) and uses the output as fix\n hints, commits the judgement changes to the branch and **always\n pushes** regardless of the check outcome, deletes the marker, removes\n the `standards:needs-agent` label, and sets `standards:needs-review`.\n3. **Agent run 2 — semantic pre-check:** the same agent runs with a\n fresh context, reads the resulting diff plus the relevant SKILL/\n changelog material, and posts a PR comment summarizing changes,\n verifying SKILL.md rules, and recommending `merge` or `hold`.\n4. **Human review:** maintainer reads the comment plus the diff and\n merges manually.\n\nUntil agent run 2 is wired, the maintainer reviews the diff manually\nagainst the SKILL.md rules without an LLM pre-comment.\n\n## Branch protection setup\n\nWithout branch protection on `main`, the consumer-repo CI guard is soft:\na maintainer (or a misclick) could merge a red state, and the\n`pending.json` guard loses its effect. The merge policy above presupposes\nhard required status checks; otherwise the whole CI gate architecture is\ndecorative.\n\n### GitHub\n\nRequired settings on `main` for every consumer repo:\n\n- `main` is a protected branch.\n- \"Require status checks to pass before merging\" enabled, with the\n CI workflow name (e.g. `ci`) listed as a required check.\n- \"Require branches to be up to date before merging\" enabled.\n- Optional but recommended: \"Require linear history\", \"Allow force\n pushes: none\", \"Allow deletions: none\".\n\nIdempotent one-shot via `gh`:\n\n```bash\ngh api repos/$ORG/$REPO/branches/main/protection -X PUT \\\n -F required_status_checks.strict=true \\\n -F required_status_checks.contexts[]=ci \\\n -F enforce_admins=true \\\n -F required_pull_request_reviews= \\\n -F restrictions=\n```\n\n### Forgejo\n\nForgejo's branch-rules API differs (`POST /repos/{owner}/{repo}/branch_protections`)\nbut the required setting set is equivalent: protect `main`, require the\nnamed CI check to pass, require branches up to date, disallow force pushes\nand deletions. Verification on Forgejo is deferred until the first Forgejo\nconsumer repo opts in; once it does, mirror the GitHub setup above and\nrecord the exact API payload here.\n\n## Pull-mode agent wiring (open)\n\nThe contract from `changes/0002-renovate-pending.md` makes\n`.standards/pending.json` the only on-branch indicator for open\njudgement steps. As long as no external agent listens for the marker\n(or the `standards:needs-agent` label), every drift PR sits with red CI\nand the cycle stays formally open.\n\n> **Status:** as long as the agent is not wired, the final merge — until\n> `.standards/pending.json` is gone — is **manual**. A maintainer can\n> always work the changelog steps locally as a fallback (`standards sync`).\n\nThe agent configuration itself lives outside this repo. This section\nkeeps the gap visible and pins the contract so the wiring effort\n(OpenClaw / Claude Code / Codex installation) has a stable target.\n\n### Two runs, one external wiring\n\nThe external agent infrastructure (webhook receiver or poller) handles\ntwo routing paths, each a separate run with a fresh context — no memory\ntransfer between runs:\n\n1. **Agent run 1 — mechanics.** Triggered by label\n `standards:needs-agent` or by the presence of\n `.standards/pending.json` in the PR diff. Reads `pending.json` — its\n `prompt` field carries the instructions and references the `changes`\n array for the changelog bodies (which are not duplicated into the\n prompt). Performs the judgement steps, and runs the repo's own gate in CI mode\n — prefer `agent:check:ci`, fall back to `agent:check` — using the\n output as hints to improve the changes. The checks are **not** a merge\n gate: the agent does not abort on a failing check and always commits\n and pushes its best-effort result to the PR branch (never the default\n branch — see step 5 of the Workflow). It then deletes\n `pending.json`, removes the `standards:needs-agent` label, and sets\n `standards:needs-review` (and/or writes\n `.standards/review-pending.json`) as the last step.\n\n The pull request's own full CI run plus human review are the actual\n gate; the maintainer reviews and merges. Providing CI-appropriate\n environment variables — so deploy/connection checks do not fail for\n lack of a local dev environment — is the external wiring's\n responsibility. See\n [#37](https://github.com/sebastian-software/standards/issues/37).\n\n When the agent's own gate run produced any failed or incomplete checks,\n it posts **one separate information comment** on the PR listing each such\n check with its output and noting that the automated environment may lack\n prerequisites (e.g. environment variables), so its results can differ\n from the PR's own CI run and a human should verify. This comment is\n posted only when there are failures, in addition to run 2's summary\n comment, and does not change the always-finalize behavior; the failures\n stay recorded. See\n [#40](https://github.com/sebastian-software/standards/issues/40).\n\n2. **Agent run 2 — semantic pre-check.** Triggered by label\n `standards:needs-review` or by `.standards/review-pending.json`. Reads\n the PR diff, the relevant changelog entries, `SKILL.md`, and\n `.repometa.json` (including `exceptions`) — nothing else. Posts a PR\n comment per the output contract below. Removes the\n `standards:needs-review` label and the marker file at the end.\n\nOn Forgejo, label setting is unreliable; the marker file\n(`.standards/pending.json` for run 1, `.standards/review-pending.json`\nfor run 2) is the **primary** signal. The labels are an optimization\nfor GitHub.\n\n### Agent run 2 — output contract\n\nRun 2 posts exactly one PR comment with four sections, in order:\n\n- **(a) Summary of agent changes.** Which files were modified, mapped\n to the judgement steps from which changelog.\n- **(b) SKILL.md consistency check.** Tabular pass/fail per rule\n (Prettier reintroduction, branding markers intact, `exceptions`\n respected, seeded-vs-managed contract upheld).\n- **(c) Judgement step verdicts.** Each step from each changelog with\n one of `ok` / `check` (the latter for steps the agent could not\n definitively verify).\n- **(d) Recommendation.** Exactly one of `merge` or `hold`, with a\n one-sentence reason.\n\nThe prompt template for run 2 is checked in at\n`reference/agent/review-prompt.md` so the external wiring uses a\nversioned template (analog to how `SKILL.md` anchors the run-1 prompt\nindirectly via the `buildPrompt` output).\n\n### Fallback\n\nIf run 2 fails (agent system down, label stuck, marker file deleted by\na rebase), the maintainer does the review manually against the SKILL.md\nrules and merges without the LLM pre-comment. The merge policy stays\nthe same: humans always merge.\n\n### External wiring follow-up\n\nThe external wiring (webhook receiver, prompt routing, secrets) is\ntracked outside this repo. One follow-up ticket covers both runs — they\nshare the same infrastructure, only the trigger path and prompt mode\ndiffer. Link to the external ticket goes here once it exists.\n\n\n## Changelog entries to execute\n\nThe full body of each entry is in the `changes` array of\n`.standards/pending.json` (fields: file, version, scopes, content). Work\nthrough them in order:\n\n- 0002-renovate-pending.md (v2)\n- 0003-platform-aware-ci.md (v3)\n- 0004-ci-workflow-forgejo.md (v4)\n- 0005-oxfmt-ignore-patterns.md (v5)\n- 0006-pnpm-minimum-release-age-exclude.md (v6)\n\n## Validation\n\nAfter applying the judgement steps, run the repository's own quality gate to\nguide your changes. Prefer `pnpm agent:check:ci` and fall back to\n`pnpm agent:check` when the `:ci` script is absent; the gate runs in CI mode.\n\nTreat the check output as hints to improve your changes, not as a merge gate:\n\n- Do not stop at the first failing check. Run the gate to completion, collect\n every failure, and fix what you reasonably can while applying the changesets.\n- Your work is complete after these best-effort fixes, even if some checks still\n fail — never withhold or revert your commits because a check is red.\n- If any check still failed or could not complete, post one separate\n information comment on the pull request that lists each such check with its\n output and notes that this automated environment may lack prerequisites (for\n example environment variables) that the pull request's own CI run has, so a\n human can verify. Post it only when there are failed or incomplete checks; if\n everything passed, post no such comment. It is in addition to the summary\n comment and does not change the always-finalize behavior above.\n", - "changes": [ - { - "file": "0002-renovate-pending.md", - "version": 2, - "scopes": [ - "common" - ], - "content": "# 0002 — Renovate as the single driver for standards updates\n\n- **Scopes:** common\n- **Standards version:** 2\n\n## Intent\n\nFrom this version on, standards updates across all managed repos are\ninitiated uniformly by the self-hosted Renovate server. Renovate handles\nboth version detection (bump of `.repometa.json#standards`) and the\nmechanical sync (`standards apply`). The judgement part is performed by\nan externally triggered LLM agent (OpenClaw, local Claude Code, or\nCodex) in pull mode. The agent deliberately does not run inside the\nRenovate container.\n\n## Mechanical steps (covered by `standards apply`)\n\n- No file changes in target repos beyond what previous changelogs already\n required. The `.standards/pending.json` marker is written by Renovate's\n `postUpgradeTasks` step when `standards apply --emit-pending` is invoked\n with a non-zero set of pending judgement entries.\n\n## Judgement steps (agent work)\n\n- None at consumer level. This change is structural — it changes how\n future versions of `@sebastian-software/standards` are rolled out across\n the org, not what consumer repos contain.\n\n## Notes\n\n- **Per-repo bootstrap (manual, one-off):** the repo carries the existing\n `managed-deps` topic of the Renovate server. `.repometa.json` is\n created by running `pnpm dlx @sebastian-software/standards init`\n (interactive prompts for visibility and initial year; flags are\n available for CI). The onboarding PR (`Configure Renovate`) has to\n be merged once before standards drift becomes visible in the UI.\n- **Renovate server prerequisite:** a custom datasource reads\n `manifest.json#currentVersion` as an integer directly from the\n standards repo instead of using the published package's npm semver.\n This keeps the version model stack-agnostic (Rust, doc-only, or mixed\n repos never see a semver) and the PR title follows the\n `chore: standards v` convention from `SKILL.md`.\n- **Self-hosted Renovate is mandatory.** Mend Cloud (the GitHub App)\n does not support `postUpgradeTasks`. `allowedCommands` must permit\n the exact `apply` invocation; `executionMode: \"branch\"` avoids known\n problems with `executionMode: \"update\"`.\n- **Pull model:** Renovate opens the PR and writes\n `.standards/pending.json` when judgement steps exist. An external\n agent consumes the file, commits the judgement changes onto the same\n branch, and removes any `standards:needs-agent` label that was set.\n On Forgejo, label setting is less reliable than on GitHub — pull\n consumers should filter primarily on the presence of the file, not\n exclusively on the label.\n- **`apply` write contract:** `standards apply` writes only to\n (a) managed/seeded targets declared in `manifest.json`,\n (b) branding sections declared in `manifest.json`,\n (c) `.repometa.json#standards`, and (d) the path passed via\n `--emit-pending`. This makes broad `fileFilters: [\"**/*\"]` safe on the\n server side.\n- **Consumer tools outside the standards scopes** (Prettier leftovers\n during migration, Biome, gitleaks, dependabot configs,\n semantic-release pipelines, custom lint scripts) must ignore\n `.standards/` themselves when they would otherwise scan every repo\n path. The standards-scope ignore files (`oxfmtignore`, `cspell.json`)\n are maintained by the standards package; foreign tools are out of\n scope.\n" - }, - { - "file": "0003-platform-aware-ci.md", - "version": 3, - "scopes": [ - "common", - "node" - ], - "content": "# 0003 — Platform-aware manifest and GitHub Actions CI seed\n\n- **Scopes:** common, node\n- **Standards version:** 3\n\n## Intent\n\nEstablishes a `platform` field on `.repometa.json` (`\"github\" | \"forgejo\"`)\nand an optional `platform` field per manifest entry, so the CI\ninfrastructure ships platform-aware from the start. As the first\napplication, the GitHub Actions CI workflow ships as a seeded file on\nrepos with `platform: \"github\"`. Forgejo follows in a later changelog\n(see issue #10).\n\n## Mechanical steps (covered by `standards apply`)\n\n- Existing managed and seeded entries without `platform` continue to\n apply to every repo regardless of the `platform` stamp.\n- The new seeded entry `reference/node/github-workflows-ci.yml` →\n `.github/workflows/ci.yml` carries `platform: \"github\"` and lands\n only on GitHub repos. On Forgejo or legacy (no `platform`) repos,\n `standards apply` skips the entry — no write, no drift finding from\n `standards check`.\n\n## Judgement steps (agent work)\n\n1. **Set the repo platform (legacy migration).** If `.repometa.json`\n has no `platform` field, run\n `standards init --force --platform

` with `

` equal to\n `github` or `forgejo` depending on where the repo lives, then re-run\n `standards apply`. This step is a one-shot migration; new repos\n created via `standards init` write `platform` from the start.\n2. **GitHub repos (`platform: \"github\"`): merge the seeded CI\n workflow with any existing one.** `standards apply` seeds\n `.github/workflows/ci.yml` only when the file does not exist. If a\n repo already has a CI workflow, merge the reference content with\n the repo-specific steps (the seeded-files update rule from `#16`\n applies): take the pending guard, the `pnpm run --if-present` chain\n and the `pnpm dlx @sebastian-software/standards check` step from\n the reference; preserve repo-specific extras (custom matrix,\n deploy step, etc.) untouched.\n3. **Forgejo repos (`platform: \"forgejo\"`): no CI seed in this\n version.** A separate changelog will add a Forgejo CI workflow\n under `.forgejo/workflows/ci.yml` (issue #10). Until then, Forgejo\n repos keep their existing CI configuration unchanged.\n\n## Notes\n\n- The `platform` field becomes part of the `.repometa.json` schema\n going forward. `standards init` prompts for it (default derived\n from `git remote get-url origin`: `github.com` → `github`,\n everything else → `forgejo`), and accepts the `--platform` flag for\n non-interactive use.\n- Filter contract: an entry without `platform` applies everywhere; an\n entry with `platform` applies only on a repo whose\n `.repometa.json#platform` matches. Legacy repos without `platform`\n receive no platform-scoped entries — the migration step above\n closes the gap.\n- The seeded CI workflow's `pnpm/action-setup` version stays at\n whatever major is current at apply time (Renovate keeps it up to\n date once the file is in place).\n- The matching `.repometa.json` stamp in the fixture repo\n `standards-test-repo2` is tracked separately as\n `sebastian-software/standards-test-repo2#4`.\n" - }, - { - "file": "0004-ci-workflow-forgejo.md", - "version": 4, - "scopes": [ - "node" - ], - "content": "# 0004 — Forgejo Actions CI seed\n\n- **Scopes:** node\n- **Standards version:** 4\n\n## Intent\n\nExtends the platform-aware CI infrastructure from change 0003 with a\nForgejo Actions variant. A second reference file\n(`reference/node/forgejo-workflows-ci.yml`) ships the same pipeline\nshape as the GitHub Actions seed, but targets the Forgejo runner\n(`runs-on: docker`, explicit container image) and lands under\n`.forgejo/workflows/ci.yml`.\n\nThis is purely additive on the GitHub side: GitHub repos see no new\nseed entries. Forgejo repos at `platform: \"forgejo\"` get the new file\nthe next time `standards apply` runs.\n\n## Mechanical steps (covered by `standards apply`)\n\n- The new seeded entry `reference/node/forgejo-workflows-ci.yml` →\n `.forgejo/workflows/ci.yml` carries `platform: \"forgejo\"` and lands\n only on Forgejo repos. On GitHub or legacy (no `platform`) repos,\n `standards apply` skips the entry — no write, no drift finding.\n\n## Judgement steps (agent work)\n\n1. **GitHub repos (`platform: \"github\"`): no action.** This change is\n not visible. The `standards apply` stamp moves to 4 without any\n file write beyond the stamp itself.\n2. **Forgejo repos (`platform: \"forgejo\"`): merge the seeded CI\n workflow with any existing one.** `standards apply` seeds\n `.forgejo/workflows/ci.yml` only when the file does not exist. If\n a Forgejo repo already has a CI workflow under\n `.forgejo/workflows/`, merge the reference content with the\n repo-specific steps (the seeded-files update rule from issue #16\n applies): take the pending guard, the `pnpm run --if-present`\n chain, and the `pnpm dlx @sebastian-software/standards check`\n step from the reference; preserve repo-specific extras (custom\n matrix, deploy step, etc.) untouched.\n3. **Legacy repos (no `platform`): set the platform first.** The\n migration step from change 0003 still applies. Until\n `.repometa.json#platform` is set, the standards stamp does not\n bump (the legacy gate from change 0003 blocks it). Run\n `standards init --force --platform ` first, then\n re-run `standards apply`.\n\n## Notes\n\n- Forgejo Actions discovers workflows in `.forgejo/workflows/` first\n and falls back to `.github/workflows/` if the Forgejo path does\n not exist. Repos that already mirror a GitHub workflow into\n `.github/workflows/` keep working — the seed adds a Forgejo-native\n workflow that takes precedence once present.\n- `runs-on: docker` plus an explicit\n `container: code.forgejo.org/oci/node:24-bookworm` keeps the seed\n reproducible across Forgejo runner pools without depending on\n registered `ubuntu-latest` labels. The pipeline shape mirrors the\n GitHub seed (pnpm + Node 24, `pnpm run --if-present` chain, drift\n check).\n- The matching `.repometa.json` stamp in the fixture repo\n `standards-test-repo2` is tracked separately as\n `sebastian-software/standards-test-repo2#5`.\n" - }, - { - "file": "0005-oxfmt-ignore-patterns.md", - "version": 5, - "scopes": [ - "node" - ], - "content": "# 0005 — oxfmt ignores move into `.oxfmtrc.json` `ignorePatterns`\n\n- **Scopes:** node\n- **Standards version:** 5\n\n## Intent\n\nDrop the standalone `.oxfmtignore` file and express the formatter's ignore\nlist through the `ignorePatterns` key inside the managed `.oxfmtrc.json`.\n\n## Problem\n\noxfmt has no concept of an `.oxfmtignore` file. By default it only\nauto-discovers `.gitignore` and `.prettierignore`\n([oxc.rs ignore files](https://oxc.rs/docs/guide/usage/formatter/ignore-files.html)).\nThe previous baseline shipped a `.oxfmtignore` and made it work by passing\n`--ignore-path .oxfmtignore` in the `format` / `format:check` package\nscripts. That had two defects:\n\n- **Only the npm scripts honoured it.** Any other entry point — the editor\n LSP (`oxfmt --lsp`), a bare `oxfmt .`, or a downstream repo that does not\n replicate the exact flag — silently ignored the file and could reformat or\n flag `dist`, `pnpm-lock.yaml`, `CHANGELOG.md` and `.standards/`.\n- **The filename implied auto-discovery that does not exist**, so the\n indirection was easy to miss and easy to break.\n\nRenaming to `.prettierignore` would make auto-discovery work, but it directly\ncontradicts change 0001, which removes Prettier and deletes `.prettierignore`\nas part of the migration.\n\noxfmt's documented, recommended mechanism is `ignorePatterns` in the config\nfile: formatter-specific, uses `.gitignore` syntax, and applies globally —\nCLI, LSP and bare invocations alike — without any flag. Because `.oxfmtrc.json`\nis already a managed file, the patterns now propagate byte-exact to every repo.\n\n## Mechanical steps (covered by `standards apply`)\n\n- Rewrite the managed `.oxfmtrc.json` so `ignorePatterns` carries\n `dist`, `**/dist`, `coverage`, `pnpm-lock.yaml`, `CHANGELOG.md`,\n `.standards/`.\n\n## Judgement steps (agent work, node scope)\n\n`standards apply` writes managed files but never deletes files it no longer\nmanages, and it does not edit `package.json`. Spell these out per repo:\n\n1. **Delete the obsolete `.oxfmtignore`.** Its patterns now live in\n `.oxfmtrc.json#ignorePatterns`. Remove any repo-specific extra patterns by\n carrying them over into `ignorePatterns` first, then delete the file.\n2. **Drop the `--ignore-path` indirection from package scripts.** Change the\n `format` / `format:check` scripts to plain `oxfmt --write .` /\n `oxfmt --check .`. Remove any other `--ignore-path .oxfmtignore` usage.\n\n## Notes\n\n- `node_modules` and lock files such as `pnpm-lock.yaml` are ignored by oxfmt\n by default; keeping `pnpm-lock.yaml` in `ignorePatterns` is harmless and\n documents intent.\n- Future formatter ignores belong in `ignorePatterns`, not in a separate\n ignore file.\n" - }, - { - "file": "0006-pnpm-minimum-release-age-exclude.md", - "version": 6, - "scopes": [ - "node" - ], - "content": "# 0006 — pnpm: load own org packages without the release cooldown\n\n- **Scopes:** node\n- **Standards version:** 6\n\n## Intent\n\nExempt the organisation's own npm packages from pnpm's supply-chain release\ncooldown so internal releases install immediately instead of after a one-day\ndelay.\n\n## Problem\n\npnpm 11 enables supply-chain protection by default: `minimumReleaseAge`\ndefaults to `1440` minutes (24h), so a freshly published version is not\nresolved until it is at least a day old\n([pnpm 11 release notes](https://pnpm.io/blog/releases/11.0)). That cooldown\nis desirable for third-party dependencies, but it also stalls the org's own\npackages — a just-released `@sebastian-software/*` or `@sebastian-gmbh/*`\nversion is invisible to consumers for 24h, which blocks fast internal\niteration and back-to-back releases.\n\npnpm's `minimumReleaseAgeExclude` accepts name patterns\n([pnpm 10.17 notes](https://pnpm.io/blog/releases/10.17)), so the two org\nscopes can opt out of the cooldown while every other dependency keeps it.\n\n## Mechanical steps (covered by `standards apply`)\n\nNone. `pnpm-workspace.yaml` is not a managed or seeded file (its `allowBuilds`\nlist is repo-specific), so `standards apply` does not write it. The reference\n`reference/node/pnpm-workspace.yaml` documents the canonical shape; adoption is\na judgement step.\n\n## Judgement steps (agent work, node scope)\n\n1. **Add the org cooldown exclusion to `pnpm-workspace.yaml`.** Add (or extend)\n a top-level `minimumReleaseAgeExclude` list with the two org scopes:\n\n ```yaml\n minimumReleaseAgeExclude:\n - \"@sebastian-gmbh/*\"\n - \"@sebastian-software/*\"\n ```\n\n Keep any repo-specific existing entries and merge the two org globs in;\n do not touch `packages:` or `allowBuilds:`. If the repo has no\n `pnpm-workspace.yaml`, create one from `reference/node/pnpm-workspace.yaml`.\n\n## Notes\n\n- This only opts the two org scopes out of the cooldown; `minimumReleaseAge`\n keeps its secure default for all third-party dependencies.\n- Pattern syntax follows pnpm's `minimumReleaseAgeExclude`; exact versions or\n `||` disjunctions also work but are not needed here.\n" - } - ] -}