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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,24 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Version
### Added

- **`Python Tooling 1.18` requires a `[project]` table for every adoption, including `build_backend = "none"`, and documents three adoption traps.** 1.17's adoption guide said a deliberately non-installable repository "needs no `[project]` table" two lines above the `PT-PROJECT-METADATA` block that exists because `uv lock` cannot read such a file, and the provider's `_metadata_findings` short-circuited to no findings whenever `build_backend = "none"` was selected ([#204](https://github.com/L3DigitalNet/project-standards/issues/204)). uv refuses any `pyproject.toml` with no `[project]` table whatever the backend, and classifies `[project]` without `[build-system]` as `source = { virtual = "." }`, so 1.18 removes the short-circuit and corrects the guide: the option omits `[build-system]`, never `[project]`. The adoption guide also now states that declared `source_layout = "explicit"` roots feed checker, Ruff, coverage, and pytest **scopes** without making a subproject package importable, and gives both fixes — `pythonpath` written directly into `[tool.pytest.ini_options]` for pytest alone, or a uv workspace member plus `additional_dev_dependencies` for every `uv run` ([#205](https://github.com/L3DigitalNet/project-standards/issues/205)) — and that any `[tool.pytest.ini_options]` key outside the package-owned `minversion`, `testpaths`, `addopts`, and `markers` is consumer-owned and may be written straight into the managed table, provided its plugin is installed first, because the owned `addopts` carries `--strict-config` ([#206](https://github.com/L3DigitalNet/project-standards/issues/206)). No option, unit identity, or rendered byte moves, so no migration edge is declared. Catalog 5 promotes `python-tooling@1.18` and retains 1.17.
- **`GitHub Workflow 1.9` gives the admission rule a branch vocabulary, an Agent Handoff exemption, and — for the first time — an executable check** ([#203](https://github.com/L3DigitalNet/project-standards/issues/203), [#218](https://github.com/L3DigitalNet/project-standards/issues/218); ADR 0031). Through 1.8 the rule attached to "the default branch", which in a repository whose work lands on a long-lived integration branch is reached only by fast-forward, so the obligation attached at no moment at all and the package shipped no enforcement of any kind; and T0's prose-repair condition left an Agent Handoff closeout with no lawful direct-commit route. 1.9 declares three branch classes — the default branch and an optional consumer-declared `integration_branch` are governed, everything else is a topic branch — and four admission classes, each carrying one `Workflow-Admission` trailer: `T0`, `PR #N`, `handoff`, and `release`. `merge --pr N` now writes `Workflow-Admission: PR #N` into the merge or squash commit it creates, which turns pull-request provenance into an offline-checkable fact instead of something an author must remember. A commit whose every path lies in `docs/handoff/**`, `docs/STATUS.md`, or `docs/TODO.md` is admitted directly as `handoff`; the set is fixed by the standard and cannot be widened by configuration, and a **mixed** commit takes the pull-request route. Four optional scalar options (`integration_branch`, `release_subject_prefix`, `admission_floor`, `handoff_admission`) all default to 1.8's behavior, so an upgrade that configures nothing changes nothing. Catalog 5 promotes `github-workflow@1.9` and retains 1.8.
- **`gh-workflow admission --branch B [--since REF] [--offline]`** is the eleventh subcommand: it classifies every commit in a range against those rules, exits `1` listing the commits no class admits with the trailer or route each needs, and exits `0` only when every commit is admitted. It verifies a `PR #N` trailer against the merged pull request when authenticated and falls back to the trailer alone under `--offline`; `admission_floor` records where enforcement begins, because adoption cannot rewrite history. **The package still contributes no workflow to `.github/`**, so a repository that has not wired the check into its own CI has the rule and no coverage — a managed CI contribution is deferred to a successor cut. The `gh-workflow` binary is rebuilt for 1.9 from changed Go source; `make go-check` verifies the committed bytes against a reproducible rebuild.

### Fixed

- **`Markdown Tooling 1.16` repairs a documented Prettier gate that failed on a clean tree.** The rendered local check handed Git's whole tracked selection to Prettier, which refuses an explicitly named symbolic link with `Explicitly specified pattern "..." is a symbolic link` and a non-zero status — while still reporting every real file correctly formatted. Any repository tracking symlinks that match the selected globs therefore failed its own gate with nothing wrong ([#209](https://github.com/L3DigitalNet/project-standards/issues/209)). The recipe now lists the corpus with `git ls-files -s` and drops index mode `120000` before `xargs`, which removes only paths Prettier was already refusing to read: the set of files actually checked is unchanged. Git stays the corpus authority, so nested `.gitignore` files, `.git/info/exclude`, and the child-repository boundary are still honored. The markdownlint recipe is unchanged — `markdownlint-cli2` follows a symlinked path and lints its target — and the no-Git Prettier fallback needs no filter, because Prettier skips a symlink reached by its own glob expansion.
- **`GitHub Workflow 1.9` closes a render/load asymmetry in the `organization` option.** The shipped binary's `ValidateLogin` rejects a login containing a doubled hyphen, but the payload's render provider did not, so `organization = "a--b"` rendered a `policy.toml` that reconcile reported as a success and every subcommand then refused to load. 1.9's `config.schema.json` carries the full login grammar as a pattern and the provider keeps the same rule as a second layer, so the configuration is refused where it is written rather than where it is read.

- **`GitHub Workflow 1.9`'s admission classifier no longer trusts a range or a path list it did not fully read.** Four defects found by a pre-release security read of the new subcommand are fixed in the same cut. The history was read with `git log --name-only`, which collapses a rename to its destination, so a commit declaring `Workflow-Admission: handoff` while moving _any_ file into `docs/handoff/` presented only exempt paths and was admitted — an arbitrary change leaving governance through the one exemption the standard grants; the read is now `-z --name-status --no-renames`, which reports both sides of every rename and, because `-z` suppresses git's C-quoting, also stops misreporting a legitimate handoff document whose name carries a non-ASCII byte. The revision argument now ends in a `--` pathspec terminator, so a `--branch` or floor value that names an existing directory is no longer silently reinterpreted as a path filter. A range that resolves to **zero commits** is refused with `GHW-ADMISSION-EMPTY-RANGE` instead of reported clean, and an `admission_floor` that is not an ancestor of the classified branch is refused with `GHW-ADMISSION-FLOOR-UNRELATED`; a run that does classify commits now states how many the floor excluded.
- **`GitHub Workflow 1.9` bounds the three free-text admission options.** `integration_branch`, `release_subject_prefix`, and `admission_floor` rejected a quote and a backslash but accepted a newline, a carriage return, and unbounded length — and a newline renders a `policy.toml` that the tool's bounded reader then refuses on every `Load`, which is the same render/load asymmetry 1.9 fixed for `organization`. The schema now pins `maxLength` on all three and a git ref-name `pattern` on the two that name revisions, and the provider accepts only bounded printable single-line text.
- **`GitHub Workflow 1.9` states plainly that the `release` class is not enforced.** Neither an explicit `release` trailer nor a subject matching `release_subject_prefix` is checked against the commit's paths, branch, or any version change, so the class records the release route rather than policing it. `pr-standard.md` and ADR 0031's consequences now say so; the release route's real guard remains the repository's own release tooling.

### Changed

- **`Markdown Tooling 1.16` makes the verify path tolerate an absent `runner_labels`, as the render path already did.** `_runner_label_reachability_warning` passed `config.get("runner_labels")` straight to its sequence coercion, so an effective configuration that omits the key raised a provider `ValueError` and aborted `verify-lint`, `verify-format`, and drift-check instead of returning findings — while the same configuration rendered cleanly. Absent and empty now behave alike on both paths. The shipped option schema declares `default: []`, so a configuration resolved through it always carries the key; this closes the gap for any caller that assembles the provider request itself.
- **`Markdown Tooling 1.16` advances `DavidAnson/markdownlint-cli2-action` to `v24.2.0`** (`21c1be1b93ad9ed58fa840aacc3f279cde2a72ff`, bundling `markdownlint-cli2` 0.23.2) in the managed lint workflow ([#211](https://github.com/L3DigitalNet/project-standards/issues/211)). The `uses:` line and its version comment are the only workflow bytes that move, and only a repository in `self-hosted` mode holds them. Because 1.16 re-renders both managed instruction blocks and the lint caller, every inbound migration edge is retargeted and now relocks all three contributions. Catalog 5 promotes `markdown-tooling@1.16` and retains 1.15, so the upgrade is a version bump; no option, unit identity, or managed config changes.
- **Upgrading to `Python Tooling 1.18` blocks a `build_backend = "none"` repository that has no `[project]` table**, where 1.17 reported nothing. Such a repository was already failing at the required `uv lock` step, so the cut surfaces an existing failure at `reconcile --check` rather than breaking a working adoption; adding the PEP 621 metadata and leaving `[build-system]` absent resolves it. The `PT-PROJECT-METADATA` hint no longer offers `build_backend = "none"` as a way out, because from 1.18 it is not one.
- ADR 0031 is ratified and moves to `active`. Two measurements in its draft are corrected in the same change: 33 of the 362 commits over `9c47907f..3bda3cf4` touch only handoff surfaces, not 66, and the derived unadmitted total was wrong with it; and the "4 PR-admitted" figure confused merged pull requests with admitted commits, since none of those commits carries a trailer that only 1.9's `merge` began to write.

## [5.27.0] — 2026-08-31

Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,9 @@ Repository-local project knowledge and bounded session continuity for coding age

GitHub work discipline for organization-owned repositories: typed issue contracts, field vocabulary, pull-request evidence, review expectations, and an attention-first operator summary. The package installs a mandatory repo-local `github-workflow` skill that keeps judgment with the agent, plus `gh-workflow` — a committed, reproducibly built static `linux/amd64` Go binary whose ten subcommands audit the organization schema, render operator summaries and creation receipts, apply validated issue mutations, and admit pull requests. Every subcommand reads the consumer's repository and writes none of it: 1.5 removed the `ledger` subcommand that generated `docs/GH-WORKFLOWS.md`, and a repository upgrading from 1.4 or earlier deletes that now-unowned file itself. The organization schema itself is skill-audited and human-applied; the tool never creates or retires an issue type, field, or value.

- **Standard:** [`standards/github-workflow/versions/1.8/README.md`](standards/github-workflow/versions/1.8/README.md)
- **Skill:** [`skills/github-workflow/`](standards/github-workflow/versions/1.8/skills/github-workflow/) — installed repo-local at `.agents/skills/github-workflow/` and `.claude/skills/github-workflow/`, with the tool at `bin/gh-workflow`.
- **Adopt:** [`adopt.md`](standards/github-workflow/versions/1.8/adopt.md)
- **Standard:** [`standards/github-workflow/versions/1.9/README.md`](standards/github-workflow/versions/1.9/README.md)
- **Skill:** [`skills/github-workflow/`](standards/github-workflow/versions/1.9/skills/github-workflow/) — installed repo-local at `.agents/skills/github-workflow/` and `.claude/skills/github-workflow/`, with the tool at `bin/gh-workflow`.
- **Adopt:** [`adopt.md`](standards/github-workflow/versions/1.9/adopt.md)

### Project Toolbox Standard

Expand Down Expand Up @@ -197,7 +197,7 @@ The path must be one exact repo-relative, non-glob path with exclusive whole-fil
| Project Specification | `1.11` | [`standards/project-spec/versions/1.11/adopt.md`](standards/project-spec/versions/1.11/adopt.md) |
| CLI Documentation | `1.6` | [`standards/cli-documentation/versions/1.6/adopt.md`](standards/cli-documentation/versions/1.6/adopt.md) |
| Agent Handoff | `1.16` | [`standards/agent-handoff/versions/1.16/adopt.md`](standards/agent-handoff/versions/1.16/adopt.md) |
| GitHub Workflow | `1.8` | [`standards/github-workflow/versions/1.8/adopt.md`](standards/github-workflow/versions/1.8/adopt.md) |
| GitHub Workflow | `1.9` | [`standards/github-workflow/versions/1.9/adopt.md`](standards/github-workflow/versions/1.9/adopt.md) |
| Project Toolbox | `1.1` | [`standards/project-toolbox/versions/1.1/adopt.md`](standards/project-toolbox/versions/1.1/adopt.md) |

For a V4 repository, do not create `.standards/` separately. Preview the complete migration, resolve every ambiguity, then apply the same command explicitly:
Expand Down
12 changes: 12 additions & 0 deletions UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,18 @@ An admitted change is now either a T0 direct commit — an unambiguous prose rep

Refreshing rewrites those two references, `SKILL.md`, the `gh-workflow` binary, and the `package_version` stamp inside the rendered `policy.toml` on the first reconcile. **No behavior changes:** the two configuration options, every `policy.toml` value outside that stamp, the ten subcommands, every exit code, and every gate outcome are 1.7's, so a PR body that passed the Ready gate under 1.7 still passes under 1.8. If you keep a PR template or house style that spells the value `R2`, correct it to `R2 Moderate` — that was always what the gate required.

### GitHub Workflow 1.8 → 1.9: four admission classes and a check that is yours to run

**The upgrade itself changes nothing you have to do.** All four options 1.9 adds are optional and defaulted, so a repository that configures none behaves exactly as it did under 1.8. Refreshing rewrites `references/pr-standard.md`, `SKILL.md`, the `gh-workflow` binary, the rendered `policy.toml` (which gains four keys at their defaults), and the managed `AGENTS.md`/`CLAUDE.md` block on the first reconcile.

**What changed is what the delivered guidance obliges.** A commit on a _governed_ branch — your default branch, plus the `integration_branch` if you declare one — is admitted by exactly one of four classes, each carrying one `Workflow-Admission` trailer: `T0`, `PR #N`, `handoff`, or `release`. `merge --pr N` writes the `PR #N` trailer itself, into the merge or squash commit body; GitHub applies a custom commit message to the merge and squash methods only, so a **rebase**-merged pull request carries no trailer and its commits need another class or a floor. If you scripted around the previous squash body, note that `merge` now supplies one.

**The handoff exemption, and its one knob.** A commit whose every path lies in `docs/handoff/**`, `docs/STATUS.md`, or `docs/TODO.md` is admitted directly with `Workflow-Admission: handoff`. The path set is fixed by the standard: `policy.toml` cannot widen it, because an exempt set a repository can extend is one an agent can extend to cover its own change. Set `handoff_admission = "none"` if you have **not** adopted `agent-handoff` and those paths are ordinary documents in your repository. A commit mixing a handoff path with any other path is not a handoff commit and takes the pull-request route.

**Set `admission_floor` before your first run.** `gh-workflow admission --branch <branch>` classifies history, and adoption cannot rewrite history — without a floor, every commit predating the rule is reported forever, and a permanently red control is an ignored one. Point it at the commit-ish where enforcement begins.

**Nothing runs the check for you.** The package contributes no workflow to `.github/`; wire `gh-workflow admission --branch <branch>` into your own CI on the branch where work lands, or the rule has no coverage. If you carry a hand-written admission or handoff carve-out in `AGENTS.md` or `CLAUDE.md`, delete it — the managed block now carries the classes and the exemption, and a hand-written middle ground beside it is the repository-configurable tier the standard refuses.

### Comments inside managed TOML regions

Consumer comments attached to a managed `pyproject.toml` unit survive a rewrite. When an apply re-renders a managed table, keyed-set entry, or key, comments found in the rewritten region — inside a multi-line array, trailing an owned line, or on their own line between owned lines — are re-emitted directly above the statement with the same key or table in the new rendering; a comment whose key no longer exists moves above the rewritten unit. Rewrites consume the old region completely, so they leave no stray blank lines, and a follow-up `reconcile --check` stays a no-op. The rendered layout of the managed unit itself (line breaks, indentation, entry order) belongs to the package, so annotate managed regions with comment lines rather than relying on a specific array layout.
Expand Down
6 changes: 6 additions & 0 deletions catalogs/5.toml
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,12 @@ role = "retained"
id = "github-workflow"
version = "1.8"
digest = "sha256:f98d80968f74cacec42711b82265f692368917130365097fe72c29ed0e6356a4"
role = "retained"

[[packages]]
id = "github-workflow"
version = "1.9"
digest = "sha256:2c9de8845e32bf93804b40867dc7f2bdb92ab17f596750e468befe663b40e5e3"
role = "default"

[[packages]]
Expand Down
1 change: 1 addition & 0 deletions cmd/gh-workflow/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

"github.com/L3DigitalNet/project-standards/internal/ghworkflow/cli"

_ "github.com/L3DigitalNet/project-standards/internal/ghworkflow/admission"
_ "github.com/L3DigitalNet/project-standards/internal/ghworkflow/audit"
_ "github.com/L3DigitalNet/project-standards/internal/ghworkflow/mutate"
_ "github.com/L3DigitalNet/project-standards/internal/ghworkflow/render"
Expand Down
Loading
Loading