diff --git a/README.md b/README.md index 7218902f..c9efe759 100644 --- a/README.md +++ b/README.md @@ -304,7 +304,7 @@ Then run the gate: ```bash scripts/verify.sh # fast gate: statics, ordinary suite, and compatibility as concurrent lanes -scripts/verify.sh --full # legacy serial battery (release prep and the last content change of a train) +scripts/verify.sh --full # same lanes, run one at a time (release prep and the last content change of a train) uv run project-standards validate # dogfood: schema, id, and references # Go gate. Install pinned repository-local tools once per checkout. @@ -312,7 +312,7 @@ make go-tools make go-check ``` -The fast Python gate is the everyday verification; the serial `--full` battery runs after a train's last content change and at release preparation, where it cross-checks the parallel configuration against the baseline it was proven against. The independent Go gate runs locally and in path-filtered CI when Go-owned files change. The release path uses `build/release-wheel` with `--clear` and replaces only the generated runtime extraction, so no stale wheel can satisfy the candidate-runtime gate. Require `sync-payload-projection --check` before `uv build`: the projection is what puts the catalog and payload bytes inside the distribution, and a wheel built without it serves `validate` but fails `init`/`reconcile` with `CP-INIT-STATE`. +The fast Python gate is the everyday verification; `--full` runs the same lane selections one at a time (ordinary suite included, still at `-n 16`) after a train's last content change and at release preparation, where it cross-checks the parallel configuration against the baseline it was proven against, and stops at the first red lane by default (`--fail-fast`; pass `--keep-going` to run every lane regardless). The independent Go gate runs locally and in path-filtered CI when Go-owned files change. The release path uses `build/release-wheel` with `--clear` and replaces only the generated runtime extraction, so no stale wheel can satisfy the candidate-runtime gate. Require `sync-payload-projection --check` before `uv build`: the projection is what puts the catalog and payload bytes inside the distribution, and a wheel built without it serves `validate` but fails `init`/`reconcile` with `CP-INIT-STATE`. The [release runbook](docs/reference/release-runbook.md) is the durable procedure for cutting a release from this checkout: every gate layer, its commands, its exit evidence, and its rollback. diff --git a/UPGRADING.md b/UPGRADING.md index 9ececf68..6c9195f3 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -369,6 +369,14 @@ An undeclared `[tool.pytest.ini_options]` leaf key, such as `asyncio_mode`, stay **Nothing runs the check for you.** The package contributes no workflow to `.github/`; wire `gh-workflow admission --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. +### What the 5.29.0 defaults rewrite on refresh: Agent Handoff 1.17 re-seals session start, GitHub Workflow 1.10 hardens `gh-workflow` + +**Agent Handoff 1.16 → 1.17 closes a startup command-execution path and fixes a stale-launcher diagnostic that answered the wrong version.** The `session-start` launcher ran its Git reads with the full inherited process environment, so a repository-local or ancestor `core.fsmonitor` setting could execute an arbitrary hook before the operator had seen anything the session context assembled (project-standards issue [#235](https://github.com/L3DigitalNet/project-standards/issues/235)). Every read now runs with an explicit minimal environment — `PATH` and `HOME` only, so no `GIT_DIR`, `GIT_WORK_TREE`, or `GIT_CONFIG_*` value from the harness can redirect it — plus `-c core.fsmonitor=`, which outranks every configuration file, and `--no-optional-locks`, so the read cannot race a concurrent write. Separately, 1.15 and 1.16 carried the 1.14 launcher binary forward byte for byte, so `session-start --version` kept answering `1.14` on both — the one question the stale-launcher diagnostic exists to answer, answered with a version the consumer never selected (project-standards issue [#229](https://github.com/L3DigitalNet/project-standards/issues/229)). 1.17 is re-linked from its own payload path and reports `agent-handoff session-start 1.17`; a contract test now pins that equality against the catalog's default version. The launcher is also linked with debug symbols stripped (project-standards issue [#228](https://github.com/L3DigitalNet/project-standards/issues/228)), taking it from 3,906,258 to 2,646,142 bytes (−32.3%); `.gopclntab` is retained, so panic traces still carry function names and line numbers. Injected session context is byte-identical to 1.16. Refreshing rewrites the installed hook because its digest moved; no option, template, provider, or contribution changes, so a repository that configures nothing behaves exactly as it did under 1.16 apart from the corrected `--version` answer. + +**GitHub Workflow 1.9 → 1.10 hardens `gh-workflow` against content it did not author and evidence it cannot re-check.** All eight fixes trace to one security read and ship together because payload bytes are immutable — every one of them changes the shipped binary (project-standards issue [#234](https://github.com/L3DigitalNet/project-standards/issues/234)). Disposition evidence — the `Final-Disposition:` pull-request comment `close --pr` and the read-only Post-merge gate both trust — is now accepted only when the authenticated actor authored it; through 1.9 any commenting account could pin a permanent disposition conflict or supply an outcome in place of the operator's own `--reason`. Untrusted text is now encoded where the envelope is written rather than in the `summary` and `receipt` renderers, so an issue body, a comment, or a hostile API error body can no longer repaint a terminal through a finding, a step message, or the JSON an agent pipes into a report; live organization-schema text printed by `audit` is encoded too. `merge --auto` now arms GitHub's auto-merge against the head SHA the gate validated, and `ready` re-observes the head immediately before marking a draft ready and refuses one that has moved since — printed as `GHW-PR-READY-HEAD-MOVED` — though GraphQL's ready mutation takes no `expectedHeadOid`, so that one compare-then-act round trip is documented as a residual race rather than claimed closed. `policy.toml` and `org-schema.yaml` are now resolved only up to the enclosing checkout root, never into an ancestor outside it, and a repository derived from `origin` is refused before any write when that remote's host is not the host the tool addresses. A 403 or 429 response carrying rate-limit headers is now waited out with `Retry-After` and retried within a bounded budget, and reported as `ErrRateLimited` — not a credential rejection — when the limit outlasts the retries. The `release` admission class stays declared but unenforced, exactly as in 1.9 (ADR 0031). No option, subcommand, or gate outcome changes, and the rendered `policy.toml` moves only its `package_version` stamp, so refreshing is a version bump: it rewrites the `gh-workflow` binary and that one line on the first reconcile. The binary is separately linked with `-s -w` from 1.10 forward, taking it from 10,432,350 to 7,307,390 bytes (−30%); `make go-check` verifies the stripped bytes against a reproducible rebuild. `gh-workflow land --pr N [--method M]` — a twelfth subcommand collapsing the hand-driven `ready` + `merge` sequence into one transaction with one receipt — is expected to ship in this same train; confirm against the installed package's `--help` before scripting to it **[VERIFY: gw110b]**. + +**A provider child process now receives an allowlisted environment, not the parent's whole one (project-standards issue [#230](https://github.com/L3DigitalNet/project-standards/issues/230)).** This is a producer-side control-plane change, not a package advance, so it applies the moment the tool release updates and is not gated by any catalog refresh. A Python-kind provider previously received a full copy of `os.environ`, so provider bytes that got past payload integrity verification could read `GITHUB_TOKEN`, `BAO_*`, or any other secret the parent process happened to hold. A child now receives exactly `PATH`, `PYTHONPATH` (recomputed from the parent's active `sys.path`, as before), `HOME`, `LANG`, `LC_ALL`, `LC_CTYPE`, `TMPDIR`, `PYTHONDONTWRITEBYTECODE`, and every `COVERAGE_*` variable. A consumer-authored provider that depends on an inherited variable outside that list must have it passed as typed provider input instead, or it now reads unset where it previously read the parent's value. + ### 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. diff --git a/scripts/README.md b/scripts/README.md index eb18f191..3c0d72e3 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -31,11 +31,11 @@ Exit `0` every applicable site declared, `1` at least one missing, `2` unknown f ## `verify.sh` — the repository release gate -Runs the repository's own verification as three concurrent lanes (statics through the non-uv path, the ordinary suite under coverage with pytest-xdist, and the compatibility matrix), then the timing-sensitive performance lane alone, then `coverage combine` and the report. `--full` reproduces the legacy serial battery for release-prep cross-checks. Expects the candidate wheel runtime on `PYTHONPATH` prerequisites and refuses to start without them; see the script header for the environment it establishes. +Runs the repository's own verification as three concurrent lanes (statics through the non-uv path, the ordinary suite under coverage with pytest-xdist, and the compatibility matrix), then the timing-sensitive performance lane alone, then `coverage combine` and the report. `--full` runs the same lane selections one at a time (ordinary suite still at `-n 16`) for the release-prep cross-check, and stops at the first red lane by default; `--keep-going` restores the fast gate's run-every-lane behavior for `--full`, and `--fail-fast` does the reverse for the fast gate. Expects the candidate wheel runtime on `PYTHONPATH` prerequisites and refuses to start without them; see the script header for the environment it establishes. ```bash scripts/verify.sh # fast gate (default) -scripts/verify.sh --full # legacy serial battery +scripts/verify.sh --full # same lanes, one at a time (release-prep cross-check) ``` ## `release_prep.py` — mechanical release preparation diff --git a/tests/README.md b/tests/README.md index fe3a7abe..59154a38 100644 --- a/tests/README.md +++ b/tests/README.md @@ -232,7 +232,7 @@ Run the gate locally before every commit that touches `src/project_standards/` o scripts/verify.sh ``` -Verification is trimmed to match the change: intermediate legs of a train run the fast gate above; the full serial battery runs only after the last content change and at release prep, where it doubles as the legacy cross-check against the coverage baseline the parallel lanes must reproduce. +Verification is trimmed to match the change: intermediate legs of a train run the fast gate above; `--full` runs only after the last content change and at release prep, where it doubles as the cross-check against the coverage baseline the parallel lanes must reproduce — the same lane selections as the fast gate, run one at a time instead of concurrently, with the ordinary suite still at `-n 16` and the run stopping at the first red lane by default. ```bash scripts/verify.sh --full