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: 4 additions & 4 deletions .agents/skills/local-strict-review/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,10 @@ In the hub, `.husky/pre-push` checks the receipt at the push itself. So the mome
| The refusal says | What it means | What clears it |
| --- | --- | --- |
| No local review covers this branch's current content | The ordinary missing pass: no recorded receipt covers what this push delivers, either because none was recorded or because the content moved after one was | One pass over the branch's whole diff, recorded per "Recording the Pass" above |
| Tracked content differs from HEAD | A push delivers HEAD while a receipt covers the index and working tree, so the receipt does not describe this push. The hook prints the same headline for an unresolved merge and for a `git update-index --refresh` that exited above 1, naming each on its own line | Commit what is being pushed, then the pass, then the record. Where the change also carries the canonical ledger, record those passes before that commit, per "The Carried-Content Sweep" above, since a ledger written after it leaves the tree differing from HEAD again. Resolve the merge first where the hook names one, and run `git status` first where it names the refresh, since the content may not differ at all |
| The commit is not this worktree's HEAD | Any pushed branch ref carrying an object id that is neither this worktree's HEAD nor the all-zero id of a delete, which a push from a checkout sitting elsewhere reaches and so does a multi-ref push such as `git push --all` | Push one branch, the one this worktree holds. Where another branch is the one wanted, check it out in its own worktree first, per `repo-worktree` |
| Any wording saying the gate did not or could not run | An execution boundary rather than a verdict, which blocks because a gate that waves a push through when it could not run has stopped gating. The cause is named in that same message or in the engine error printed above it, and it is a missing Python interpreter, an unresolvable target, an unreadable receipt, a git command that failed, or any unexpected failure | Whatever the message names, most often installing an interpreter per `docs/host-setup.md` or fetching the target branch. Never another pass |
| The recorded pass was run against X and this check measured Y, printed under the missing-pass headline | The hook reads `develop` and nothing else, so a branch based elsewhere is measured against `develop` whatever the pass targeted, and the engine deliberately prints no record command, since the one it would print records a pass over a diff nobody read | One more pass against the branch this work actually targets, where it does target the measured one. Where it does not, the gate cannot judge the branch at all and the bypass is its answer |
| Tracked content differs from HEAD | A push delivers HEAD while a receipt covers the index and working tree, so the receipt does not describe this push. The hook prints the same headline for an unresolved merge and for a `git update-index --refresh` that exited above 1. It names each on its own line | Commit what is being pushed, then the pass, then the record. Where the change also carries the canonical ledger, record those passes before that commit, per "The Carried-Content Sweep" above. That is because a ledger written after the commit leaves the tree differing from HEAD again. Resolve the merge first where the hook names one. Run `git status` first where it names the refresh, since the content may not differ at all |
| The commit is not this worktree's HEAD | Any pushed branch ref carrying an object id that is neither this worktree's HEAD nor the all-zero id of a delete. A push from a checkout sitting elsewhere reaches such a ref, and so does a multi-ref push such as `git push --all` | Push one branch, the one this worktree holds. Where another branch is the one wanted, check it out in its own worktree first, per `repo-worktree` |
| Any wording saying the gate did not or could not run | An execution boundary rather than a verdict, which blocks because a gate that waves a push through when it could not run has stopped gating. The cause is named in that same message or in the engine error printed above it. The cause is a missing Python interpreter, an unresolvable target, an unreadable receipt, a git command that failed, or any unexpected failure | Whatever the message names, most often installing an interpreter per `docs/host-setup.md` or fetching the target branch. Never another pass |
| The recorded pass was run against X and this check measured Y, printed under the missing-pass headline | The hook reads `develop` and nothing else, so a branch based elsewhere is measured against `develop` whatever the pass targeted. The engine deliberately prints no record command, since the one it would print records a pass over a diff nobody read | One more pass against the branch this work actually targets, where it does target the measured one. Where it does not, the gate cannot judge the branch at all and the bypass is its answer |

This table is the fleet's one enumeration of these, and every other surface states the principle and routes here rather than listing the shapes. That is deliberate. Every review round that added a shape also left a restatement of it somewhere else. Keeping one table is what stops the next round doing the same.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,20 @@ For each *applicable* scenario, evaluate every job's `if:`/`needs:` against the

| # | Input | Expected output | Exercises |
| --- | --- | --- | --- |
| S1 | PR touching a build target | `changes` flags it; validation runs; that target's smoke build runs; no push, **no uploads**; validate-release **succeeds**, its check exiting early on smoke per D2.2; release **skipped**; aggregator **success**; version = prerelease; no release; no dangling artifacts | D1, D2.2, D3 |
| S1 | PR touching a build target | `changes` flags it, validation runs, and that target's smoke build runs. No push and **no uploads**. The validate-release job **succeeds**, its check exiting early on smoke per D2.2. Release **skipped**, aggregator **success**. The version is a prerelease. No release and no dangling artifacts | D1, D2.2, D3 |
| S2 | PR changing only docs | smoke-build **skipped**, validation runs, aggregator **success** | D1.1, D1.2, D1.5 |
| S3 | PR changing only `.github/workflows/**` | the filter marks no target -> smoke-build **skipped**, validation runs, aggregator **success** | D1.2, D1.4, D1.5 |
| S4 | PR base = default branch, carrying a build target | smoke versions as prerelease, validate-release **succeeds** with its check exited early per D2.2, so the default-branch arm does **not** fire, aggregator **success**, promotion not blocked | D1.5, D2.2, D3.2 |
| S5 | bot push to `main` not touching a release path (e.g. an Actions bump) | the paths filter excludes it, so nothing publishes | D4.1 |
| S6 | code-affecting **bot** push to `main` (a human push/promotion, or any develop push, does not) | the `plan` job gates it to the App/Dependabot actor, and `main` publishes a release | D3, D4 |
| S7 | publish run (schedule, a bot push to main, or a dispatch) | builds the **one** trigger branch: `main` -> `X.Y.Z`, `prerelease=false`, registry stable, readme run; `develop` -> `X.Y.Z-g<sha>`, `prerelease=true`, registry prerelease; `release-asset-*` consumed-then-deleted; each package build-artifact (`nuget-build-*`, `pypi-build-*`) deleted after its publish; **no dangling artifacts** | D3, D4, D5, D6, D7 |
| S7 | publish run (schedule, a bot push to main, or a dispatch) | builds the **one** trigger branch. `main` -> `X.Y.Z`, `prerelease=false`, registry stable, readme run. `develop` -> `X.Y.Z-g<sha>`, `prerelease=true`, registry prerelease. `release-asset-*` is consumed then deleted, and each package build-artifact (`nuget-build-*`, `pypi-build-*`) is deleted after its publish. **No dangling artifacts** | D3, D4, D5, D6, D7 |
| S8 | dispatch from a ref other than `main` or `develop` | **fails fast** | D2.3 |
| S9 | re-run publish on a schedule or push trigger, version unchanged (a dispatch re-run refreshes the release instead, per D4.4) | release-create **skipped**, `release-asset-*` delete **skipped**; NuGet/PyPI pushes no-op (server dedupe); **package build-artifacts still deleted** (their download succeeded); **Docker still re-pushes** the image; no duplicate release | D4.4, D5.2 |
| S10 | branch/version classification disagree | validate-release **fails loud**, build/publish skip | D2.2 |
| S11 | scheduled upstream-version bump (wrapper) | resolver detects a change -> commits the state file -> opens a per-branch bump PR -> the merge-bot auto-merges it, or leaves it for the maintainer where the tracker sets `auto-merge: false` (D8.3) -> the `main` pin publishes via the gate (a develop pin does not auto-publish, shipping instead via a develop dispatch or promotion) | D8.3, D3.5 |
| S12 | deploy dispatch naming an environment | the ref gate runs **first** (production from the default branch only, any ref to a non-production environment); validation runs; the callee re-asserts the environment name; a release installs under its own id; the pointer flips as a separate step; retention is bounded by whichever of the D5.6 shapes the repo uses, so a deploy whose credential can observe the destination asserts the count converged and one confined write-only leaves it to the host; the live check asserts the environment and the release id, waiting out the reload, then the URL contract; **no tag and no release are created** | D2.1, D4.6, D5.6 |
| S11 | scheduled upstream-version bump (wrapper) | resolver detects a change -> commits the state file -> opens a per-branch bump PR. The merge-bot auto-merges it, or leaves it for the maintainer where the tracker sets `auto-merge: false` (D8.3). The `main` pin then publishes via the gate. A develop pin does not auto-publish, shipping instead via a develop dispatch or promotion | D8.3, D3.5 |
| S12 | deploy dispatch naming an environment | the ref gate runs **first**, allowing production from the default branch only and any ref to a non-production environment. Validation runs, and the callee re-asserts the environment name. A release installs under its own id, and the pointer flips as a separate step. Retention is bounded by whichever of the D5.6 shapes the repo uses. So a deploy whose credential can observe the destination asserts the count converged, and one confined write-only leaves it to the host. The live check asserts the environment and the release id, waiting out the reload, then the URL contract. **No tag and no release are created** | D2.1, D4.6, D5.6 |
| S13 | deploy dispatch of a production environment from a non-default ref | **fails fast**, before anything is installed or written | D2.1 |
| S14 | publish whose branch head gained different `.github/workflows` content after the build began | every update since a release bot's push or merge: release-create **skipped**, the publisher is dispatched on the branch, this run ends **cancelled** with its package publish jobs skipped (a Docker image already pushed), and the dispatched run releases the head, except that a push landing after the branch is confirmed unmoved and before the dispatch is released as if it were one of them (an accepted, bounded limit per D4.7), and the dispatched run queues behind this run only where the head's publisher evaluates this run's group for a dispatch. Any other update: the step **fails**, nothing is released or dispatched | D4.1, D4.2, D4.5, D4.7 |
| S14 | publish whose branch head gained different `.github/workflows` content after the build began | every update since a release bot's push or merge: release-create **skipped** and the publisher dispatched on the branch. In that case this run ends **cancelled** with its package publish jobs skipped (a Docker image already pushed). The dispatched run releases the head, queueing behind this run only where the head's publisher evaluates this run's group for a dispatch. A push landing after the branch is confirmed unmoved and before the dispatch is released as if it were such an update. That is an accepted, bounded limit per D4.7. Any other update: the step **fails**, nothing is released or dispatched | D4.1, D4.2, D4.5, D4.7 |

### 5C. Live Probe (Where Warranted)

Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
8303ad535ff03847
13ea5003f124eba7
Original file line number Diff line number Diff line change
@@ -1 +1 @@
09ecd9fbe562f01b
7c52b74b24afbd27
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,10 @@ In the hub, `.husky/pre-push` checks the receipt at the push itself. So the mome
| The refusal says | What it means | What clears it |
| --- | --- | --- |
| No local review covers this branch's current content | The ordinary missing pass: no recorded receipt covers what this push delivers, either because none was recorded or because the content moved after one was | One pass over the branch's whole diff, recorded per "Recording the Pass" above |
| Tracked content differs from HEAD | A push delivers HEAD while a receipt covers the index and working tree, so the receipt does not describe this push. The hook prints the same headline for an unresolved merge and for a `git update-index --refresh` that exited above 1, naming each on its own line | Commit what is being pushed, then the pass, then the record. Where the change also carries the canonical ledger, record those passes before that commit, per "The Carried-Content Sweep" above, since a ledger written after it leaves the tree differing from HEAD again. Resolve the merge first where the hook names one, and run `git status` first where it names the refresh, since the content may not differ at all |
| The commit is not this worktree's HEAD | Any pushed branch ref carrying an object id that is neither this worktree's HEAD nor the all-zero id of a delete, which a push from a checkout sitting elsewhere reaches and so does a multi-ref push such as `git push --all` | Push one branch, the one this worktree holds. Where another branch is the one wanted, check it out in its own worktree first, per `repo-worktree` |
| Any wording saying the gate did not or could not run | An execution boundary rather than a verdict, which blocks because a gate that waves a push through when it could not run has stopped gating. The cause is named in that same message or in the engine error printed above it, and it is a missing Python interpreter, an unresolvable target, an unreadable receipt, a git command that failed, or any unexpected failure | Whatever the message names, most often installing an interpreter per `docs/host-setup.md` or fetching the target branch. Never another pass |
| The recorded pass was run against X and this check measured Y, printed under the missing-pass headline | The hook reads `develop` and nothing else, so a branch based elsewhere is measured against `develop` whatever the pass targeted, and the engine deliberately prints no record command, since the one it would print records a pass over a diff nobody read | One more pass against the branch this work actually targets, where it does target the measured one. Where it does not, the gate cannot judge the branch at all and the bypass is its answer |
| Tracked content differs from HEAD | A push delivers HEAD while a receipt covers the index and working tree, so the receipt does not describe this push. The hook prints the same headline for an unresolved merge and for a `git update-index --refresh` that exited above 1. It names each on its own line | Commit what is being pushed, then the pass, then the record. Where the change also carries the canonical ledger, record those passes before that commit, per "The Carried-Content Sweep" above. That is because a ledger written after the commit leaves the tree differing from HEAD again. Resolve the merge first where the hook names one. Run `git status` first where it names the refresh, since the content may not differ at all |
| The commit is not this worktree's HEAD | Any pushed branch ref carrying an object id that is neither this worktree's HEAD nor the all-zero id of a delete. A push from a checkout sitting elsewhere reaches such a ref, and so does a multi-ref push such as `git push --all` | Push one branch, the one this worktree holds. Where another branch is the one wanted, check it out in its own worktree first, per `repo-worktree` |
| Any wording saying the gate did not or could not run | An execution boundary rather than a verdict, which blocks because a gate that waves a push through when it could not run has stopped gating. The cause is named in that same message or in the engine error printed above it. The cause is a missing Python interpreter, an unresolvable target, an unreadable receipt, a git command that failed, or any unexpected failure | Whatever the message names, most often installing an interpreter per `docs/host-setup.md` or fetching the target branch. Never another pass |
| The recorded pass was run against X and this check measured Y, printed under the missing-pass headline | The hook reads `develop` and nothing else, so a branch based elsewhere is measured against `develop` whatever the pass targeted. The engine deliberately prints no record command, since the one it would print records a pass over a diff nobody read | One more pass against the branch this work actually targets, where it does target the measured one. Where it does not, the gate cannot judge the branch at all and the bypass is its answer |

This table is the fleet's one enumeration of these, and every other surface states the principle and routes here rather than listing the shapes. That is deliberate. Every review round that added a shape also left a restatement of it somewhere else. Keeping one table is what stops the next round doing the same.

Expand Down
Loading
Loading