diff --git a/.config/README.md b/.config/README.md new file mode 100644 index 0000000..c34f113 --- /dev/null +++ b/.config/README.md @@ -0,0 +1,95 @@ +# `.config/` — Tool Configuration + +Every linter, formatter, and hook config lives here. One directory, one +purpose: if a tool needs a config file and it is not provisioning the +container, it goes in here. + +Policy and rationale: +[`docs/adr/0011-tooling-configuration-layout.md`](../docs/adr/0011-tooling-configuration-layout.md). +Enforcement: `task check:config` (CFG-01..CFG-08), implemented in +[`tools/src/config.ts`](../tools/src/config.ts). + +The convention is shared with `musher-dev/development-container` and +`musher-dev/platform`. Keeping the three aligned is the point: a contributor +moving between them should not have to re-learn where the linter configs are. + +## Index + +Every file, the tool that reads it, and how that tool is pointed at it. A file +missing from this table fails CFG-03; a file no caller names fails CFG-04. + +| File | Tool | How it is reached | +| --- | --- | --- | +| `lefthook.yml` | lefthook | **Auto-discovered.** Lefthook searches `.config/lefthook.*` natively | +| `lefthook-local.yml` | lefthook | Auto-discovered and merged. Gitignored; personal overrides only | +| `actions/actionlint.yaml` | actionlint | `-config-file .config/actions/actionlint.yaml` | +| `markdown/markdownlint.jsonc` | markdownlint-cli2 | `--config .config/markdown/markdownlint.jsonc` | +| `spelling/cspell.json` | cspell | `--config .config/spelling/cspell.json` | +| `spelling/musher.txt` | cspell | Resolved via `dictionaryDefinitions[].path` in `spelling/cspell.json` | + +Call sites are [`Taskfile.yml`](../Taskfile.yml), [`taskfiles/`](../taskfiles/), +`.config/lefthook.yml`, and [`.github/workflows/`](../.github/workflows/). +Tool versions are pinned in +[`tools/package.json`](../tools/package.json) for anything installed by Bun, +and in [`.devcontainer/mise.toml`](../.devcontainer/mise.toml) for the rest — +with the CI workflow mirroring the same version, because CI is not a mise host +and does not read that file. + +## Rules + +1. **Bucket by concern.** `.config//.`. A one-file bucket + is fine and collects siblings over time. The single exception is + `lefthook.yml`, which sits at the top level because lefthook's config search + does not descend past `.config/lefthook.*` — bucketing it would silently + stop every hook. +2. **No leading dot on filenames.** The directory is already dotted; a second + dot advertises a discovery mechanism that is deliberately not in use. +3. **Pass the path explicitly.** Except for lefthook, which finds this + directory on its own, every caller names its config with the tool's own + config flag. Never rely on default discovery — that is what put these files + at the repo root in the first place. +4. **Every file must have a caller.** A config nothing reads is dead weight + that still reads as authoritative. +5. **Every ignore needs a reason.** Suppressions, allowlists, and disabled + rules carry an inline comment explaining why the exception is acceptable. +6. **Configuration only.** No executables. A build asset belongs beside what + builds it; a repo-level runner belongs in `tools/src/`. + +Adding a config? Verify the flag is live: point the tool at a nonexistent path +and confirm it fails. A silently-ignored `--config` is the failure mode this +whole directory exists to prevent, and it is one command to rule out. + +## What does *not* live here + +| Thing | Where | Why | +| --- | --- | --- | +| `Taskfile.yml` | Repo root | Task only discovers `Taskfile.*` at the root; `--taskfile` would break bare `task ` | +| `.gitignore`, `.gitattributes` | Repo root | Git reads these from the root only | +| `.editorconfig` | Repo root | EditorConfig walks up from the file being edited; no config-path flag exists. It is the *single* source for whitespace, line endings and encoding — `devcontainer.json` deliberately does not restate them | +| `LICENSE`, `NOTICE` | Repo root | GitHub detects a licence at the root only, and Apache-2.0 expects NOTICE to travel with the work | +| `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`, `SECURITY.md` | `.github/` | Community health files, which GitHub resolves from there | +| `biome.json`, `tsconfig.json` | `tools/` | They belong to the `tools/` package and are resolved by it — a package's own config, not a repo-level one | +| `catalog.json`, `published.json` | Repo root | Published data artifacts, not tool configuration | +| `mise.toml`, `devcontainer.json` | `.devcontainer/` | They provision the environment rather than checking the code | +| `dependabot.yml`, `release-please/`, `rulesets/`, `workflows/` | `.github/` | GitHub reads these from fixed locations | + +## Deliberately config-less + +**shellcheck.** Its threshold is passed at the call site because `.shellcheckrc` +supports no `severity` key — the rcfile accepts only `disable`, `enable`, +`external-sources`, `source`, `source-path` and `shell`, and an unrecognised +key is silently ignored rather than rejected. A `.config/shell/shellcheckrc` +holding `severity=warning` would look like a gate and enforce nothing. If +shellcheck ever gains the key, the file becomes worth adding. + +## A trap worth knowing + +Lefthook's config search is **first-match-wins**, in this order: + +```text +lefthook.* → .lefthook.* → .config/lefthook.* +``` + +A stray `lefthook.yml` at the repo root therefore **silently shadows** this +directory's copy — no warning, no error, just a different set of hooks. +`task check:config` (CFG-06) fails the build if one appears. diff --git a/.config/actions/actionlint.yaml b/.config/actions/actionlint.yaml new file mode 100644 index 0000000..8bb5291 --- /dev/null +++ b/.config/actions/actionlint.yaml @@ -0,0 +1,18 @@ +# actionlint configuration. +# +# Read by actionlint via an explicit -config-file path. actionlint's own +# default is .github/actionlint.yaml; this repository overrides it so every +# tool config sits in one place. See .config/README.md. +# Docs: https://github.com/rhysd/actionlint/blob/main/docs/config.md + +# No self-hosted runners. GitHub-hosted labels are known to actionlint and need +# no declaration, so an empty list here is a statement rather than a stub: a +# workflow naming any other label is a typo, and actionlint will say so. +self-hosted-runner: + labels: [] + +# Every configuration variable this repository's workflows may read. The list +# is exhaustive on purpose -- with it, `vars.TYPO` is a build failure instead of +# an empty string that silently changes what a job does. Add a name here in the +# same change that adds the variable to the repository settings. +config-variables: [] diff --git a/lefthook.yml b/.config/lefthook.yml similarity index 75% rename from lefthook.yml rename to .config/lefthook.yml index 6fdaf77..cc4a4fd 100644 --- a/lefthook.yml +++ b/.config/lefthook.yml @@ -1,3 +1,12 @@ +# Git hooks. Lefthook discovers this file itself, by searching +# `lefthook.*` -> `.lefthook.*` -> `.config/lefthook.*` and stopping at the +# first match -- which is why it is the one config here not passed by path, +# and why a stray lefthook.yml at the repo root would silently shadow it. +# `task check:config` (CFG-06) fails the build if one appears. +# +# Keep min_version in step with .devcontainer/mise.toml. +min_version: 2.1.10 + pre-commit: parallel: true jobs: @@ -9,6 +18,14 @@ pre-commit: glob: 'specifications/**/*.schema.json' run: task check:schema + - name: config + glob: '.config/**' + run: task check:config + + - name: markdown + glob: '**/*.md' + run: task check:md + - name: drift glob: 'specifications/**/*.schema.json' run: task check:drift diff --git a/.config/markdown/markdownlint.jsonc b/.config/markdown/markdownlint.jsonc new file mode 100644 index 0000000..f8d63a8 --- /dev/null +++ b/.config/markdown/markdownlint.jsonc @@ -0,0 +1,48 @@ +// markdownlint rules for this repository's Markdown. +// +// Read by markdownlint-cli2 via an explicit --config path; never +// auto-discovered. The call sites are `check:md` and `fmt:md` in Taskfile.yml. +// Rule reference: https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md +// +// Every relaxation below carries its reason, per .config/README.md rule 5. +{ + "default": true, + + // Prose here wraps at 80, but the limit is 120 and applies to prose only. + // spec.md is largely wide reference tables, and reflowing one to fit costs + // more readability than the long line does. + "MD013": { + "line_length": 120, + "tables": false, + "code_blocks": false, + "headings": false + }, + + // The `` anchors in spec.md are load-bearing: conformance + // fixtures link back to them, so they are part of the contract rather than + // decoration. `
` is the only way to break a line inside a table cell. + // No other element is used, and none should be. + "MD033": { "allowed_elements": ["a", "br"] }, + + // The specifications and the ADRs deliberately reuse headings such as + // "Context" and "Known debt" under different parents. Siblings must still be + // unique, which is what this setting keeps enforcing. + "MD024": { "siblings_only": true }, + + // A bold lead-in opening a paragraph -- "**Positive.** ..." in every ADR, + // "**In scope**" in GOVERNANCE -- is this repository's house style, and it is + // a lead-in rather than a heading: the text continues on the same line and + // the sections it sits inside already have real headings. + "MD036": false, + + // A fenced block with no language is how this repository writes directory + // trees and terminal output, neither of which has a language to name. + "MD040": false, + + // Table pipe alignment is presentation with no effect on rendering, and the + // rule cannot fix it -- adopting it would mean hand-realigning 263 pipes + // across three normative spec.md files, in tables that carry anchors and + // in-cell line breaks. The cost is a large hand edit to normative prose; the + // benefit is nil. Revisit if markdownlint gains an autofix for it. + "MD060": false +} diff --git a/cspell.json b/.config/spelling/cspell.json similarity index 54% rename from cspell.json rename to .config/spelling/cspell.json index dc1e7e5..4a6db0b 100644 --- a/cspell.json +++ b/.config/spelling/cspell.json @@ -2,10 +2,17 @@ "$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json", "version": "0.2", "language": "en-GB", + + // This config sits two levels below the repo root, and cspell resolves the + // globs below against the config file's own directory. Without globRoot, + // every repo-relative ignorePath would be matched against .config/spelling/, + // match nothing, and the check would keep passing while covering less. + "globRoot": "../..", + "dictionaryDefinitions": [ { "name": "musher", - "path": "./.cspell/musher.txt", + "path": "./musher.txt", "addWords": true, "description": "Musher domain vocabulary and the tooling this repository uses. A word belongs here when it is a term of art, not when it is merely long." } @@ -19,13 +26,18 @@ "companies" ], "ignorePaths": [ + // Vendored or generated: not this repository's prose to correct. "**/node_modules/**", "site/**", "tools/bun.lock", - ".cspell/**", + "**/schemas/dist/**", + // The dictionary itself, and a licence whose wording is not ours to edit. + ".config/spelling/**", "LICENSE", + // Checksums and opaque identifiers, not words. "published.json", - "**/schemas/dist/**", + // Conformance vectors are chosen for their bytes, including deliberate + // misspellings and malformed input. Correcting them would destroy the test. "conformance/**/case.yaml", "conformance/**/tree/**" ], diff --git a/.cspell/musher.txt b/.config/spelling/musher.txt similarity index 96% rename from .cspell/musher.txt rename to .config/spelling/musher.txt index 2485ae3..4ffe50d 100644 --- a/.cspell/musher.txt +++ b/.config/spelling/musher.txt @@ -41,12 +41,16 @@ webp xlarge # --- Tooling and JSON Schema terms ------------------------------------------- +codespell commonmark gpgsign metaschema nojekyll +pycache subschema subschemas +Taskfile +taskfiles unindexed unioned vendored diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index c1671d2..b1be113 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -88,6 +88,9 @@ "customizations": { "vscode": { "extensions": [ + // VS Code does not read .editorconfig on its own, and .editorconfig is + // this repository's single source for whitespace and line endings. + "editorconfig.editorconfig", "redhat.vscode-yaml", "biomejs.biome", "timonwong.shellcheck", @@ -99,10 +102,12 @@ "settings": { "editor.formatOnSave": true, "editor.rulers": [80, 100], - "files.trimTrailingWhitespace": true, - "files.insertFinalNewline": true, - "files.eol": "\n", - "files.encoding": "utf8", + + // Whitespace, line endings and encoding are NOT set here. .editorconfig + // states them once, for every editor, and the EditorConfig extension + // above applies them inside the container. Restating them made VS Code + // trim trailing whitespace from Markdown, where two trailing spaces are + // a hard line break and .editorconfig deliberately preserves them. "terminal.integrated.defaultProfile.linux": "zsh", "terminal.integrated.profiles.linux": { diff --git a/.devcontainer/scripts/post-create.sh b/.devcontainer/scripts/post-create.sh index 9e2ed36..93faf24 100644 --- a/.devcontainer/scripts/post-create.sh +++ b/.devcontainer/scripts/post-create.sh @@ -30,13 +30,13 @@ on_error() { trap 'on_error ${LINENO} "${BASH_COMMAND}"' ERR # Installs lefthook git hooks for this repo. Best-effort: silently -# skips if lefthook isn't on PATH yet or no lefthook.yml exists. +# skips if lefthook isn't on PATH yet or no .config/lefthook.yml exists. # # Outputs: # Writes progress to stderr via log() install_lefthook_hooks() { command -v lefthook >/dev/null 2>&1 || return 0 - [[ -f "${SCRIPT_DIR}/../../lefthook.yml" ]] || return 0 + [[ -f "${SCRIPT_DIR}/../../.config/lefthook.yml" ]] || return 0 log "Installing lefthook git hooks..." (cd "${SCRIPT_DIR}/../.." && lefthook install >/dev/null 2>&1) || true } diff --git a/.editorconfig b/.editorconfig index 00f2c60..a184537 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,3 +1,14 @@ +# Editor behaviour, for every editor. +# +# This is the single source for whitespace, line endings and encoding. The +# devcontainer deliberately does NOT restate these in +# customizations.vscode.settings: two statements of the same rule drift, and +# these two already had -- VS Code trimmed trailing whitespace from Markdown +# while the rule below preserves it. +# +# It stays at the repo root because EditorConfig walks up from the file being +# edited and accepts no config-path flag, which is also why it is one of the +# few things not under .config/. See docs/adr/0011. root = true [*] @@ -8,8 +19,7 @@ trim_trailing_whitespace = true indent_style = space indent_size = 2 +# Two trailing spaces are a hard line break in Markdown, so trimming them +# silently changes how the prose renders. [*.md] trim_trailing_whitespace = false - -[Makefile] -indent_style = tab diff --git a/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md similarity index 96% rename from CODE_OF_CONDUCT.md rename to .github/CODE_OF_CONDUCT.md index 5aa18a8..86053f5 100644 --- a/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -32,7 +32,7 @@ Unacceptable behaviour: ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behaviour may be -reported to the maintainers at **conduct@musher.dev**. All complaints will be +reported to the maintainers at ****. All complaints will be reviewed and investigated promptly and fairly. Maintainers are obligated to respect the privacy and security of the reporter. diff --git a/CONTRIBUTING.md b/.github/CONTRIBUTING.md similarity index 88% rename from CONTRIBUTING.md rename to .github/CONTRIBUTING.md index 86d5e55..b4fa41f 100644 --- a/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -30,8 +30,8 @@ ordinary codebase. version, a change that would reject a previously valid document needs neither the `v` directory nor a migration note — there is no released version to have validated against, so there is nothing to migrate from. - [ADR 0005](docs/adr/0005-platform-divergence-reconciliation.md) §1 sets the - rule out and [GOVERNANCE.md](GOVERNANCE.md#compatibility-review) carries it. + [ADR 0005](../docs/adr/0005-platform-divergence-reconciliation.md) §1 sets the + rule out and [GOVERNANCE.md](../GOVERNANCE.md#compatibility-review) carries it. What the window does **not** remove is maintainer approval, or the obligation to declare the change as breaking in the commit trailer. It closes for a family @@ -51,6 +51,11 @@ task setup # install tool dependencies and git hooks task check # run everything CI runs ``` +The tasks live in [`taskfiles/`](../taskfiles/), included by the root +`Taskfile.yml`. Every linter, formatter and hook config lives in +[`.config/`](../.config/README.md), and every caller names its config with the +tool's own flag — adding one has a fixed shape, described there. + ## Making a change ```sh @@ -76,7 +81,7 @@ runs it on every pull request and writes it to the job summary. A fixture is a `case.yaml` when the rule is decided by reading one document, and a `tree/` when it is decided by reading the item the document sits in — a slug against its directory, a reference against a file. See -[conformance/README.md](conformance/README.md#case-trees). +[conformance/README.md](../conformance/README.md#case-trees). Adding a diagnostic code or a requirement ID to a `spec.md` obliges you to add a case for it. @@ -89,6 +94,7 @@ runner's `UNCOVERED` list saying why the code cannot be exercised. |---|---| | `check:format` | Biome formatting and lint of `tools/` | | `check:types` | TypeScript typecheck of `tools/` | +| `check:config` | The `.config/` layout: every file indexed, reachable, and a declaration (CFG-01..CFG-08) | | `check:schema` | Every `src/` module is valid JSON Schema 2020-12; `$id`s are unique and canonical; no remote `$ref` | | `check:drift` | The committed `dist/` bundle matches a fresh compile of `src/` | | `check:examples` | Every file in `examples/` validates against its family's bundle | @@ -100,6 +106,7 @@ runner's `UNCOVERED` list saying why the code cannot be exercised. | `check:test` | The tooling test suite, including the publication-immutability regressions | | `check:commits` | The Conventional Commits vocabulary agrees across its three copies | | `check:links` | Every internal Markdown link and anchor resolves | +| `check:md` | markdownlint over every Markdown file | | `check:spelling` | Prose, tooling, and schema descriptions spell-check clean | | `check:shell` | ShellCheck over `.devcontainer/scripts` | | `check:workflow` | actionlint over `.github/workflows` | @@ -137,8 +144,8 @@ git commit -s -m "feat(component): add restartPolicy" ## Proposing a structural change Changes to the repository architecture, the release model, or the family -taxonomy need an ADR in [`docs/adr/`](docs/adr/). Copy the format of -[ADR 0001](docs/adr/0001-canonical-repository-architecture.md), open it as a PR +taxonomy need an ADR in [`docs/adr/`](../docs/adr/). Copy the format of +[ADR 0001](../docs/adr/0001-canonical-repository-architecture.md), open it as a PR on its own, and get it accepted before writing the implementation. ## Reporting a problem in the specification diff --git a/SECURITY.md b/.github/SECURITY.md similarity index 98% rename from SECURITY.md rename to .github/SECURITY.md index 65d8e1b..f7ef819 100644 --- a/SECURITY.md +++ b/.github/SECURITY.md @@ -4,7 +4,7 @@ Report security issues privately through [GitHub Security Advisories](https://github.com/musher-dev/spec/security/advisories/new), -or by email to **security@musher.dev**. +or by email to ****. Please do not open a public issue for a security report. diff --git a/.github/workflows/dco.yml b/.github/workflows/dco.yml index 49ec0fb..dafe498 100644 --- a/.github/workflows/dco.yml +++ b/.github/workflows/dco.yml @@ -66,7 +66,7 @@ jobs: git rebase --signoff origin/main && git push --force-with-lease - See CONTRIBUTING.md → Sign your work. + See .github/CONTRIBUTING.md → Sign your work. MSG exit 1 fi diff --git a/.gitignore b/.gitignore index 1f88158..6084e22 100644 --- a/.gitignore +++ b/.gitignore @@ -15,5 +15,8 @@ Thumbs.db .idea/ *.swp +# Personal lefthook overrides, auto-merged beside .config/lefthook.yml. +/.config/lefthook-local.yml + # Task runner cache .task/ diff --git a/CLAUDE.md b/CLAUDE.md index 9fd289c..7eeec19 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -30,6 +30,10 @@ implementation of what is defined here. 7. **Exact-version URLs are rebuilt from tags, never from `main`.** `tools/src/site.ts` extracts each release from its own tag. Nothing in the working tree may feed a path that has already been published. +8. **Tool configuration lives in `.config//`, passed by path.** Never + add a config to the repo root when the tool accepts a config flag, and never + rely on default discovery — every caller names its config explicitly. + `task check:config` enforces this (CFG-01..CFG-08). See docs/adr/0011. ## Layout @@ -44,6 +48,8 @@ conformance//v/ // metadata.json + case.yaml + diagnostics.json tools/src/*.ts non-normative Bun scripts — the only language-bound code docs/adr/ architecture decision records +.config/ every linter, formatter, and hook config (see its README) +taskfiles/ Task modules included by the root Taskfile.yml ``` Three families, versioned independently: `component`, `blueprint`, `listing`. diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 7d74057..e32e4f7 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -29,7 +29,7 @@ specification disputes. Current maintainers are listed in [`.github/CODEOWNERS`](.github/CODEOWNERS). **Contributors** are anyone opening an issue or pull request. No agreement -beyond the [DCO](CONTRIBUTING.md#sign-your-work) is required. +beyond the [DCO](.github/CONTRIBUTING.md#sign-your-work) is required. ## Decision process @@ -183,4 +183,4 @@ passed. ## Security -See [SECURITY.md](SECURITY.md). +See [SECURITY.md](.github/SECURITY.md). diff --git a/README.md b/README.md index 2853d3d..49145b5 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ fails the build if your commit does not match. ## Contributing -See [CONTRIBUTING.md](CONTRIBUTING.md) and [GOVERNANCE.md](GOVERNANCE.md). +See [CONTRIBUTING.md](.github/CONTRIBUTING.md) and [GOVERNANCE.md](GOVERNANCE.md). ```sh task setup # install tooling and git hooks diff --git a/Taskfile.yml b/Taskfile.yml index 2bf7105..a5c6859 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -1,6 +1,12 @@ # Musher Specification Taskfile # Usage: task [task-name] # List tasks: task --list +# +# This file stays at the repo root because Task only discovers `Taskfile.*` +# there: `--taskfile` would work but would break bare `task `. It holds +# the entry points; the tasks themselves live in taskfiles/, included below. +# Everything else this repository configures lives in .config/ — see +# .config/README.md and docs/adr/0011-tooling-configuration-layout.md. version: '3' @@ -13,6 +19,19 @@ env: # already provisions, so a rebuild does not re-download the world. BUN_INSTALL_CACHE_DIR: '{{.BUN_INSTALL_CACHE_DIR | default "/home/vscode/.cache/bun"}}' +# Namespaced except for build, which is flattened so `task bundle` and +# `task site:build` keep the names CI, the hooks and CONTRIBUTING use. An +# included file's `default` becomes the bare namespace, so `task setup` and +# `task check` still work. +includes: + setup: taskfiles/setup.Taskfile.yml + build: + taskfile: taskfiles/build.Taskfile.yml + flatten: true + check: taskfiles/check.Taskfile.yml + ci: taskfiles/ci.Taskfile.yml + ledger: taskfiles/release.Taskfile.yml + tasks: default: desc: List available tasks @@ -21,7 +40,12 @@ tasks: silent: true # =========================================================================== - # Setup + # Entry points + # + # The two aggregates live here rather than as a `default` inside their + # modules: an included `default` is reachable as the bare namespace, but + # `task --list` shows it as `check:default`, which hides the command a + # newcomer most needs to find. # =========================================================================== setup: @@ -30,65 +54,11 @@ tasks: - task: setup:tools - task: setup:hooks - setup:tools: - desc: Install the specification tooling dependencies - dir: '{{.TOOLS_DIR}}' - cmds: - - bun install - - setup:hooks: - desc: Install repository git hooks - status: - - test -f .git/hooks/pre-commit - cmds: - - cmd: git config --unset core.hooksPath - ignore_error: true - - lefthook install - - # =========================================================================== - # Build - # =========================================================================== - - bundle: - desc: Compile schemas/src into the published schemas/dist bundles - dir: '{{.TOOLS_DIR}}' - cmds: - - bun run src/bundle.ts - - bun run src/catalog.ts - - bun run src/traceability.ts - - traceability: - desc: Regenerate the requirement traceability matrix - dir: '{{.TOOLS_DIR}}' - cmds: - - bun run src/traceability.ts - - changes: - desc: Report what this branch does to the published contract (BASE=origin/main) - dir: '{{.TOOLS_DIR}}' - cmds: - - bun run src/changes.ts {{.BASE | default "origin/main"}} - - catalog: - desc: Regenerate the SchemaStore catalog index - dir: '{{.TOOLS_DIR}}' - cmds: - - bun run src/catalog.ts - - site:build: - desc: Assemble the publication tree served at schemas.musher.dev - dir: '{{.TOOLS_DIR}}' - cmds: - - bun run src/site.ts - - # =========================================================================== - # Checks - # =========================================================================== - check: desc: Run every check CI runs cmds: - task: check:format + - task: check:config - task: check:types - task: check:schema - task: check:drift @@ -101,133 +71,11 @@ tasks: - task: check:test - task: check:commits - task: check:links + - task: check:md - task: check:spelling - task: check:shell - task: check:workflow - check:standards: - desc: Meta-validate schemas with an independent JSON Schema toolchain - dir: '{{.TOOLS_DIR}}' - cmds: - - bun run src/standards.ts - - check:parity: - desc: Require Ajv and Blaze to agree on every structural verdict - dir: '{{.TOOLS_DIR}}' - cmds: - - bun run src/parity.ts - - check:published: - desc: Verify every released version against the publication ledger - dir: '{{.TOOLS_DIR}}' - cmds: - - bun run src/published.ts - - check:ledger: - desc: Fail if published.json changed anything already recorded - dir: '{{.TOOLS_DIR}}' - cmds: - - bun run src/ledger.ts check - - check:compat: - desc: Replay every released version's accepted documents against the candidate schema - dir: '{{.TOOLS_DIR}}' - cmds: - - bun run src/compat.ts - - check:commits: - desc: Hold the Conventional Commits vocabulary in step across its three copies - dir: '{{.TOOLS_DIR}}' - cmds: - - bun run src/commits.ts - - check:links: - desc: Resolve every internal Markdown link and anchor - dir: '{{.TOOLS_DIR}}' - cmds: - - bun run src/links.ts - - check:spelling: - desc: Spell-check prose, tooling sources, and schema descriptions - cmds: - - >- - tools/node_modules/.bin/cspell --no-progress --no-summary --unique - --config cspell.json "**/*.md" "tools/src/**/*.ts" "**/*.schema.json" - - check:test: - desc: Run the tooling test suite - dir: '{{.TOOLS_DIR}}' - cmds: - - bun test - - ledger:record: - desc: Record the pending release from the release-please manifest - dir: '{{.TOOLS_DIR}}' - cmds: - - bun run src/ledger.ts record - - ledger:sync: - desc: Backfill ledger entries for tags that exist but were never recorded - dir: '{{.TOOLS_DIR}}' - cmds: - - bun run src/ledger.ts sync - - check:schema: - desc: Meta-validate schema modules, $id uniqueness, and $ref locality - dir: '{{.TOOLS_DIR}}' - cmds: - - bun run src/lint.ts - - check:drift: - desc: Fail if a committed bundle does not match a fresh compile - dir: '{{.TOOLS_DIR}}' - cmds: - - bun run src/drift.ts - - check:examples: - desc: Validate every example document against its family bundle - dir: '{{.TOOLS_DIR}}' - cmds: - - bun run src/examples.ts - - check:conformance: - desc: Execute the conformance corpus - dir: '{{.TOOLS_DIR}}' - cmds: - - bun run src/conformance.ts - - check:format: - desc: Check formatting and lint of the tooling sources - dir: '{{.TOOLS_DIR}}' - cmds: - - bun run biome ci . - - check:types: - desc: Typecheck the tooling sources - dir: '{{.TOOLS_DIR}}' - cmds: - - bun run tsc --noEmit - - check:shell: - desc: ShellCheck the dev container scripts - cmds: - - shellcheck -x --source-path=SCRIPTDIR .devcontainer/scripts/*.sh .devcontainer/scripts/lib/*.sh - - check:shell:files: - desc: ShellCheck specific files (used by the pre-commit hook) - cmds: - - shellcheck -x --source-path=SCRIPTDIR {{.CLI_ARGS}} - - check:workflow: - desc: Lint the GitHub Actions workflows - cmds: - - actionlint - - check:workflow:files: - desc: Lint specific workflow files (used by the pre-commit hook) - cmds: - - actionlint {{.CLI_ARGS}} - # =========================================================================== # Formatting # =========================================================================== @@ -238,6 +86,14 @@ tasks: cmds: - bun run biome format --write . + fmt:md: + desc: Apply the Markdown fixes markdownlint can make itself + cmds: + - >- + tools/node_modules/.bin/markdownlint-cli2 --fix + --config .config/markdown/markdownlint.jsonc + "**/*.md" "#tools/node_modules" "#site" "#.github/pull_request_template.md" + # =========================================================================== # Environment # =========================================================================== @@ -267,31 +123,9 @@ tasks: - task: check # =========================================================================== - # CI entry points + # Housekeeping # =========================================================================== - ci:lint: - desc: Lint suite as run in CI - cmds: - - task: check:format - - task: check:commits - - task: check:links - - task: check:spelling - - task: check:shell - - task: check:workflow - - ci:test: - desc: Schema and conformance suite as run in CI - cmds: - - task: check:schema - - task: check:drift - - task: check:examples - - task: check:conformance - - task: check:standards - - task: check:parity - - task: check:compat - - task: check:test - clean: desc: Remove generated output cmds: diff --git a/docs/adr/0002-conformance-case-trees.md b/docs/adr/0002-conformance-case-trees.md index 6a865cf..c183433 100644 --- a/docs/adr/0002-conformance-case-trees.md +++ b/docs/adr/0002-conformance-case-trees.md @@ -24,7 +24,7 @@ The `semantic` phase does not. Of the twenty-four diagnostic codes the three None is expressible as a single document, so none had a fixture. They reached `main` as prose with CI green, which is the failure mode -[CONTRIBUTING.md](../../CONTRIBUTING.md) ground rule 2 exists to prevent: "no +[CONTRIBUTING.md](../../.github/CONTRIBUTING.md) ground rule 2 exists to prevent: "no schema change without conformance fixtures." The rules themselves are not in doubt. Blueprint §3.1 defines the **item root** diff --git a/docs/adr/0004-listing-description-trust-boundary.md b/docs/adr/0004-listing-description-trust-boundary.md index 208cd2d..380ca47 100644 --- a/docs/adr/0004-listing-description-trust-boundary.md +++ b/docs/adr/0004-listing-description-trust-boundary.md @@ -13,7 +13,7 @@ origin — the classic shape of a stored cross-site scripting vulnerability. Nothing constrains it today. CommonMark permits raw HTML by design, so a conforming listing may contain `