From 761474829c827b3410f3025d190d2d9a67562d9e Mon Sep 17 00:00:00 2001 From: Yue Ma Date: Wed, 9 Sep 2026 22:12:37 -0400 Subject: [PATCH 1/2] docs: tighten the standards reference 175 lines to 136, by cutting explanation that repeated itself rather than anything it discloses. Every limit stays: an absent tool is inconclusive, approve does not enforce who approves, trigger pr has no hook, and reading a file proves nothing about whether the agent followed it. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_016cCs5DdUjLDkP8V3Z5x6XH --- .../structured-coding/references/standards.md | 193 +++++++----------- .../structured-coding/references/standards.md | 193 +++++++----------- dist/structured-coding-claude-code.zip | Bin 112452 -> 111540 bytes dist/structured-coding-codex.zip | Bin 112796 -> 111884 bytes structured-coding/references/standards.md | 193 +++++++----------- 5 files changed, 231 insertions(+), 348 deletions(-) diff --git a/dist/claude-code/structured-coding/references/standards.md b/dist/claude-code/structured-coding/references/standards.md index 0f2a258..b0e895b 100644 --- a/dist/claude-code/structured-coding/references/standards.md +++ b/dist/claude-code/structured-coding/references/standards.md @@ -1,15 +1,13 @@ # Project standards configuration -Status: **REPORTS; NEVER BLOCKS.** This release reads the configuration and, -on the `run` command, executes the declared checks and classifies each result. -With the optional `standards` preset installed, it also reports after a direct -`git commit`. It blocks nothing: no commit, no merge, no tool call. Automatic triggering is later work; the target behaviour for a hook -remains in the [hook contract](hook-contract.md). +Status: **REPORTS; NEVER BLOCKS.** Checks run when you invoke `run`, and, with the +optional `standards` preset installed, after a direct `git commit`. Nothing is +blocked: no commit, no merge, no tool call. A clean report is not proof that a +check ran; read the outcomes. -A project uses this to state, once, what is true of its whole codebase, so the -operator does not restate it in every planning conversation. Requirements -specific to one PR stay in that PR's conversation and its frozen design; putting -them here would create a second authority over the same decision. +A project states here what is true of its whole codebase, so the operator does +not restate it in every planning conversation. Requirements specific to one PR +belong in that PR's conversation, not here. ## Files @@ -19,59 +17,36 @@ them here would create a second authority over the same decision. | `.structured-coding/standards.local.md` | overlay | Copy [the template](../standards-template.md) to the base path and edit its -single fenced `json` block. The surrounding prose is never read, which is the -point: it carries the options, the alternatives and the reasons that JSON cannot. -Exactly one top-level `json` block is read. A block nested inside another fence -is prose. Zero blocks, several, or unparsable content is refused, naming the file. +single fenced `json` block. The surrounding prose is never read. Exactly one +top-level `json` block is read; a block nested inside another fence is prose. +Zero blocks, several, or unparsable content is refused, naming the file. Both +files are optional, and with neither the shipped defaults apply. -Both files are optional. With neither present the shipped defaults apply and -behaviour is unchanged. +## Layer and trust are decided separately -## Two determinations, decided separately +Layer comes from the filename. Trust comes from `git ls-files`: a tracked file is +shared, so anything it declares outside the allowlist needs approval; an untracked +file is personal, absent from a fresh clone, so authoring it is the approval. -**Layer comes from the filename.** The base states the project's standard. The -overlay may add and tighten only. - -**Trust comes from `git ls-files`.** A tracked file is shared: any contributor can -change it in a pull request, so a tool it declares outside the allowlist needs -approval. An untracked file is personal: it is absent from a fresh clone and can -only have been written by the machine's owner, so authoring it is the approval. - -The two are independent, which removes the special cases. A tracked overlay is an -overlay that happens to be shared. An untracked base is the base and happens to -be personal. Filename must not decide trust, because `.gitignore` does not apply -to a file Git already tracks, so a repository could otherwise commit a file under -the personal name and have it treated as locally authored. +Filename must not decide trust, because `.gitignore` does not apply to a file Git +already tracks. A tracked overlay is simply a shared overlay; an untracked base is +simply a personal base. ## Resolution -Defaults, then base, then overlay. The base may relax the shipped defaults: those -defaults are a suggestion and the project's standard is the authority. Only the -overlay is restricted. +Defaults, then base, then overlay. The base may relax the defaults; they are a +suggestion and the project's standard is the authority. ```text trigger: off < pr < commit scope: changed < repository ``` -The overlay may raise a rank, enable a disabled tool, add a tool, and add a -convention. It may not lower a rank, disable a tool, or narrow a scope. Overlay -conventions are additions, so removing one has no representation at all. - -A relaxing overlay is **refused, naming the field**. Ignoring it would leave a -developer believing a local skip took effect. Applying it would let one machine -opt out of the team's standard while its report still read as compliant. A -genuine local skip belongs in the PR as a recorded deviation. This is the same -rule the workflow already states for documents: a child may not silently relax a -binding restriction. - -## Tool classification - -Grouped by whether the tool executes project code, not by whether the name is -recognized. `ruff` and `pyright` analyze without running anything. `pytest` -imports `conftest.py` by design and `mypy` imports configured plugins, so both are -recognized and still require approval. Anything else is unrecognized and also -requires approval. Recognition is not trust. +The overlay may only raise a rank, enable a disabled tool, add a tool, or add a +convention. A relaxing overlay is **refused, naming the field** — ignoring it +would leave a developer believing a local skip took effect, and applying it would +let one machine opt out while its report still read as compliant. Overlay +conventions are additions, so removing one has no representation. ## Commands @@ -81,95 +56,81 @@ python3 /scripts/standards.py run --project . --base main python3 /scripts/standards.py approve --project . ``` -`inspect` prints the files found with their trust, the effective values, the -layer every value came from, each tool's approval verdict, and a note. It runs -nothing. A refusal exits non-zero with an empty stdout, so a failure cannot be -mistaken for a defaults report. - -`run` adds the result of every enabled check. A base is required while any -enabled check uses changed-file scope, and there is no fallback to a guessed -default branch: guessing which branch a project treats as its base is how a check -silently examines the wrong range. +`inspect` prints the files found, their trust, the effective values, the layer +every value came from, and each tool's approval verdict. It runs nothing. -The base comes from `--base`, or from the session binding when `--host` and -`--session` identify one that recorded it. An explicit `--base` wins, because a -person naming a revision is more specific than a record made when the PR was -bound. With neither, a changed-scope check reports that it was not run. +`run` adds the result of every enabled check. A base is required while any enabled +check uses changed-file scope; it comes from `--base` or from a session binding +identified by `--host` and `--session`, with `--base` winning. There is no +fallback to a guessed default branch, because guessing which branch a project +treats as its base is how a check examines the wrong range and reports +confidently about it. ## Outcomes | Outcome | Meaning | | --- | --- | -| `PASS` | the tool ran and reported nothing | -| `FAIL` | the tool ran and reported findings | -| `INCONCLUSIVE` | it could not run or could not finish: absent, timed out, killed, or exited in its own error mode | -| `NOT RUN` | disabled, no files in scope, no command declared, or approval missing | +| `PASS` | ran, reported nothing | +| `FAIL` | ran, reported findings | +| `INCONCLUSIVE` | could not run or finish: absent, timed out, killed, or exited in its own error mode | +| `NOT RUN` | disabled, nothing in scope, no command, or approval missing | -An absent tool is `INCONCLUSIVE`, never `PASS`: nothing was examined, so nothing -was established. An empty changed-file selection is `NOT RUN` for the same -reason. +An absent tool is `INCONCLUSIVE`, never `PASS`, and so is an empty changed-file +selection: neither examined anything. -Exit codes are mapped per tool, because tools disagree. `ruff check` returns 2 -when it terminates abnormally; `pyright` returns 2 for a fatal error, 3 for a -config file it could not read and 4 for illegal parameters. Those mean the tool -could not run. A command the project supplied has no such map, so a non-zero exit -is reported as `FAIL` and the report says its exit codes are unmapped. +Exit codes are mapped per tool because tools disagree. `ruff check` returns 2 on +abnormal termination; `pyright` returns 2 fatal, 3 unreadable config, 4 illegal +parameters. A command the project supplied has no map, so a non-zero exit is +`FAIL` and the report says the codes are unmapped. -Runs are bounded: a per-tool timeout, a total budget, and captured output -truncated with the truncation marked. +Runs are bounded by a per-tool timeout, a total budget, and truncated output. A +timed-out tool has its process group ended, so nothing it spawned outlives it. -## The optional preset +## Tools the skill does not ship argv for + +`ruff` and `pyright` run with argv this skill supplies; declaring `command` for +them is refused. Anything else needs `command` as argv words, which requires +schema 2, and an approval: ```sh -./scripts/install codex --project /path/to/project --hooks standards +python3 /scripts/standards.py approve --project . ``` -It registers one hook: `PostToolUse` on a direct `git commit`. After the commit -exists, the checks whose trigger is `commit` run and the result is reported to -the agent as context. Nothing is blocked, and the commit has already happened. - -The registered budget is 60 seconds, larger than the 12 the other presets use, -because a check is slower than a notice. The host is blocked while it runs, so -keep the commit trigger to fast checks; a tool that outruns the budget is -`INCONCLUSIVE`, and its process group is ended rather than left running. - -**`trigger: "pr"` has no hook, deliberately.** Neither host has a PR-completed -event. `Stop` is the nearest moment, and it fires at the end of every agent turn, -which is not what `pr` means. Rather than map onto an event that means something -else and then suppress the noise, that granularity stays an explicit -`standards.py run` at review time, alongside the review discipline the workflow -already carries. +`approve` prints every command it authorizes and records the approval under the +Git directory, outside the working tree, so a pull request cannot carry approval +for the command it introduces. The record is bound to those commands alone: +changing one revokes it, an unrelated edit does not. Until an approval matches, +those tools are `NOT RUN`. -## Commands this skill does not ship +Tools are grouped by whether they execute project code, not by whether the name +is recognized. `pytest` imports `conftest.py` by design and `mypy` imports +configured plugins, so both are recognized and still need approval. -`ruff` and `pyright` run with argv this skill supplies, and a configuration -declaring `command` for them is refused: those names mean that argv, and letting -a project redefine them would make the allowlist meaningless. +**A disclosed limit:** `approve` records an operator decision; it does not enforce +who made it. Nothing here prevents an agent from running it, exactly as nothing +prevents an agent from running the installer. -Any other tool must supply `command` as argv words, which requires schema 2, and -must be approved before it runs: +## The optional preset ```sh -python3 /scripts/standards.py approve --project . +./scripts/install codex --project /path/to/project --hooks standards ``` -`approve` prints every command it authorizes and records the approval under the -Git directory, outside the working tree, so a pull request cannot carry approval -for the command it introduces. The record is bound to those commands alone: -changing one revokes it, while an unrelated edit to the configuration does not, -because a prompt that appears after harmless changes is one people learn to -accept without reading. Until an approval matches, those tools are `NOT RUN` and -nothing is executed. +One hook: `PostToolUse` on a direct `git commit`. The checks whose trigger is +`commit` run and the result is reported as context. The commit already exists; +nothing is blocked. The budget is 60 seconds rather than the 12 the other presets +use, and the host is blocked while it runs, so keep the commit trigger to fast +checks. -**A disclosed limit:** `approve` is an operator command, and nothing in this -skill prevents an agent from running it, exactly as nothing prevents an agent -from running the installer. It records a decision; it does not enforce who made -it. +**`trigger: "pr"` has no hook, deliberately.** Neither host has a PR-completed +event, and `Stop`, the nearest moment, fires at the end of every agent turn, +which is not what `pr` means. That granularity stays an explicit `run` at review +time. ## Limits Stdlib and Git only; Python 3.9 or newer. A declared command is argv and is never -passed to a shell. The configuration itself is read as data and never executed. A refusal names the file and the field and never carries the -file's contents. Symlinked, non-regular, oversized and non-UTF-8 configuration -files are refused rather than parsed. Reading a file proves nothing about whether -the agent followed it. +passed to a shell; the configuration itself is read as data and never executed. A +refusal names the file and field and never carries file contents. Symlinked, +non-regular, oversized and non-UTF-8 configuration files are refused. Reading a +file proves nothing about whether the agent followed it. diff --git a/dist/codex/structured-coding/references/standards.md b/dist/codex/structured-coding/references/standards.md index 0f2a258..b0e895b 100644 --- a/dist/codex/structured-coding/references/standards.md +++ b/dist/codex/structured-coding/references/standards.md @@ -1,15 +1,13 @@ # Project standards configuration -Status: **REPORTS; NEVER BLOCKS.** This release reads the configuration and, -on the `run` command, executes the declared checks and classifies each result. -With the optional `standards` preset installed, it also reports after a direct -`git commit`. It blocks nothing: no commit, no merge, no tool call. Automatic triggering is later work; the target behaviour for a hook -remains in the [hook contract](hook-contract.md). +Status: **REPORTS; NEVER BLOCKS.** Checks run when you invoke `run`, and, with the +optional `standards` preset installed, after a direct `git commit`. Nothing is +blocked: no commit, no merge, no tool call. A clean report is not proof that a +check ran; read the outcomes. -A project uses this to state, once, what is true of its whole codebase, so the -operator does not restate it in every planning conversation. Requirements -specific to one PR stay in that PR's conversation and its frozen design; putting -them here would create a second authority over the same decision. +A project states here what is true of its whole codebase, so the operator does +not restate it in every planning conversation. Requirements specific to one PR +belong in that PR's conversation, not here. ## Files @@ -19,59 +17,36 @@ them here would create a second authority over the same decision. | `.structured-coding/standards.local.md` | overlay | Copy [the template](../standards-template.md) to the base path and edit its -single fenced `json` block. The surrounding prose is never read, which is the -point: it carries the options, the alternatives and the reasons that JSON cannot. -Exactly one top-level `json` block is read. A block nested inside another fence -is prose. Zero blocks, several, or unparsable content is refused, naming the file. +single fenced `json` block. The surrounding prose is never read. Exactly one +top-level `json` block is read; a block nested inside another fence is prose. +Zero blocks, several, or unparsable content is refused, naming the file. Both +files are optional, and with neither the shipped defaults apply. -Both files are optional. With neither present the shipped defaults apply and -behaviour is unchanged. +## Layer and trust are decided separately -## Two determinations, decided separately +Layer comes from the filename. Trust comes from `git ls-files`: a tracked file is +shared, so anything it declares outside the allowlist needs approval; an untracked +file is personal, absent from a fresh clone, so authoring it is the approval. -**Layer comes from the filename.** The base states the project's standard. The -overlay may add and tighten only. - -**Trust comes from `git ls-files`.** A tracked file is shared: any contributor can -change it in a pull request, so a tool it declares outside the allowlist needs -approval. An untracked file is personal: it is absent from a fresh clone and can -only have been written by the machine's owner, so authoring it is the approval. - -The two are independent, which removes the special cases. A tracked overlay is an -overlay that happens to be shared. An untracked base is the base and happens to -be personal. Filename must not decide trust, because `.gitignore` does not apply -to a file Git already tracks, so a repository could otherwise commit a file under -the personal name and have it treated as locally authored. +Filename must not decide trust, because `.gitignore` does not apply to a file Git +already tracks. A tracked overlay is simply a shared overlay; an untracked base is +simply a personal base. ## Resolution -Defaults, then base, then overlay. The base may relax the shipped defaults: those -defaults are a suggestion and the project's standard is the authority. Only the -overlay is restricted. +Defaults, then base, then overlay. The base may relax the defaults; they are a +suggestion and the project's standard is the authority. ```text trigger: off < pr < commit scope: changed < repository ``` -The overlay may raise a rank, enable a disabled tool, add a tool, and add a -convention. It may not lower a rank, disable a tool, or narrow a scope. Overlay -conventions are additions, so removing one has no representation at all. - -A relaxing overlay is **refused, naming the field**. Ignoring it would leave a -developer believing a local skip took effect. Applying it would let one machine -opt out of the team's standard while its report still read as compliant. A -genuine local skip belongs in the PR as a recorded deviation. This is the same -rule the workflow already states for documents: a child may not silently relax a -binding restriction. - -## Tool classification - -Grouped by whether the tool executes project code, not by whether the name is -recognized. `ruff` and `pyright` analyze without running anything. `pytest` -imports `conftest.py` by design and `mypy` imports configured plugins, so both are -recognized and still require approval. Anything else is unrecognized and also -requires approval. Recognition is not trust. +The overlay may only raise a rank, enable a disabled tool, add a tool, or add a +convention. A relaxing overlay is **refused, naming the field** — ignoring it +would leave a developer believing a local skip took effect, and applying it would +let one machine opt out while its report still read as compliant. Overlay +conventions are additions, so removing one has no representation. ## Commands @@ -81,95 +56,81 @@ python3 /scripts/standards.py run --project . --base main python3 /scripts/standards.py approve --project . ``` -`inspect` prints the files found with their trust, the effective values, the -layer every value came from, each tool's approval verdict, and a note. It runs -nothing. A refusal exits non-zero with an empty stdout, so a failure cannot be -mistaken for a defaults report. - -`run` adds the result of every enabled check. A base is required while any -enabled check uses changed-file scope, and there is no fallback to a guessed -default branch: guessing which branch a project treats as its base is how a check -silently examines the wrong range. +`inspect` prints the files found, their trust, the effective values, the layer +every value came from, and each tool's approval verdict. It runs nothing. -The base comes from `--base`, or from the session binding when `--host` and -`--session` identify one that recorded it. An explicit `--base` wins, because a -person naming a revision is more specific than a record made when the PR was -bound. With neither, a changed-scope check reports that it was not run. +`run` adds the result of every enabled check. A base is required while any enabled +check uses changed-file scope; it comes from `--base` or from a session binding +identified by `--host` and `--session`, with `--base` winning. There is no +fallback to a guessed default branch, because guessing which branch a project +treats as its base is how a check examines the wrong range and reports +confidently about it. ## Outcomes | Outcome | Meaning | | --- | --- | -| `PASS` | the tool ran and reported nothing | -| `FAIL` | the tool ran and reported findings | -| `INCONCLUSIVE` | it could not run or could not finish: absent, timed out, killed, or exited in its own error mode | -| `NOT RUN` | disabled, no files in scope, no command declared, or approval missing | +| `PASS` | ran, reported nothing | +| `FAIL` | ran, reported findings | +| `INCONCLUSIVE` | could not run or finish: absent, timed out, killed, or exited in its own error mode | +| `NOT RUN` | disabled, nothing in scope, no command, or approval missing | -An absent tool is `INCONCLUSIVE`, never `PASS`: nothing was examined, so nothing -was established. An empty changed-file selection is `NOT RUN` for the same -reason. +An absent tool is `INCONCLUSIVE`, never `PASS`, and so is an empty changed-file +selection: neither examined anything. -Exit codes are mapped per tool, because tools disagree. `ruff check` returns 2 -when it terminates abnormally; `pyright` returns 2 for a fatal error, 3 for a -config file it could not read and 4 for illegal parameters. Those mean the tool -could not run. A command the project supplied has no such map, so a non-zero exit -is reported as `FAIL` and the report says its exit codes are unmapped. +Exit codes are mapped per tool because tools disagree. `ruff check` returns 2 on +abnormal termination; `pyright` returns 2 fatal, 3 unreadable config, 4 illegal +parameters. A command the project supplied has no map, so a non-zero exit is +`FAIL` and the report says the codes are unmapped. -Runs are bounded: a per-tool timeout, a total budget, and captured output -truncated with the truncation marked. +Runs are bounded by a per-tool timeout, a total budget, and truncated output. A +timed-out tool has its process group ended, so nothing it spawned outlives it. -## The optional preset +## Tools the skill does not ship argv for + +`ruff` and `pyright` run with argv this skill supplies; declaring `command` for +them is refused. Anything else needs `command` as argv words, which requires +schema 2, and an approval: ```sh -./scripts/install codex --project /path/to/project --hooks standards +python3 /scripts/standards.py approve --project . ``` -It registers one hook: `PostToolUse` on a direct `git commit`. After the commit -exists, the checks whose trigger is `commit` run and the result is reported to -the agent as context. Nothing is blocked, and the commit has already happened. - -The registered budget is 60 seconds, larger than the 12 the other presets use, -because a check is slower than a notice. The host is blocked while it runs, so -keep the commit trigger to fast checks; a tool that outruns the budget is -`INCONCLUSIVE`, and its process group is ended rather than left running. - -**`trigger: "pr"` has no hook, deliberately.** Neither host has a PR-completed -event. `Stop` is the nearest moment, and it fires at the end of every agent turn, -which is not what `pr` means. Rather than map onto an event that means something -else and then suppress the noise, that granularity stays an explicit -`standards.py run` at review time, alongside the review discipline the workflow -already carries. +`approve` prints every command it authorizes and records the approval under the +Git directory, outside the working tree, so a pull request cannot carry approval +for the command it introduces. The record is bound to those commands alone: +changing one revokes it, an unrelated edit does not. Until an approval matches, +those tools are `NOT RUN`. -## Commands this skill does not ship +Tools are grouped by whether they execute project code, not by whether the name +is recognized. `pytest` imports `conftest.py` by design and `mypy` imports +configured plugins, so both are recognized and still need approval. -`ruff` and `pyright` run with argv this skill supplies, and a configuration -declaring `command` for them is refused: those names mean that argv, and letting -a project redefine them would make the allowlist meaningless. +**A disclosed limit:** `approve` records an operator decision; it does not enforce +who made it. Nothing here prevents an agent from running it, exactly as nothing +prevents an agent from running the installer. -Any other tool must supply `command` as argv words, which requires schema 2, and -must be approved before it runs: +## The optional preset ```sh -python3 /scripts/standards.py approve --project . +./scripts/install codex --project /path/to/project --hooks standards ``` -`approve` prints every command it authorizes and records the approval under the -Git directory, outside the working tree, so a pull request cannot carry approval -for the command it introduces. The record is bound to those commands alone: -changing one revokes it, while an unrelated edit to the configuration does not, -because a prompt that appears after harmless changes is one people learn to -accept without reading. Until an approval matches, those tools are `NOT RUN` and -nothing is executed. +One hook: `PostToolUse` on a direct `git commit`. The checks whose trigger is +`commit` run and the result is reported as context. The commit already exists; +nothing is blocked. The budget is 60 seconds rather than the 12 the other presets +use, and the host is blocked while it runs, so keep the commit trigger to fast +checks. -**A disclosed limit:** `approve` is an operator command, and nothing in this -skill prevents an agent from running it, exactly as nothing prevents an agent -from running the installer. It records a decision; it does not enforce who made -it. +**`trigger: "pr"` has no hook, deliberately.** Neither host has a PR-completed +event, and `Stop`, the nearest moment, fires at the end of every agent turn, +which is not what `pr` means. That granularity stays an explicit `run` at review +time. ## Limits Stdlib and Git only; Python 3.9 or newer. A declared command is argv and is never -passed to a shell. The configuration itself is read as data and never executed. A refusal names the file and the field and never carries the -file's contents. Symlinked, non-regular, oversized and non-UTF-8 configuration -files are refused rather than parsed. Reading a file proves nothing about whether -the agent followed it. +passed to a shell; the configuration itself is read as data and never executed. A +refusal names the file and field and never carries file contents. Symlinked, +non-regular, oversized and non-UTF-8 configuration files are refused. Reading a +file proves nothing about whether the agent followed it. diff --git a/dist/structured-coding-claude-code.zip b/dist/structured-coding-claude-code.zip index d50d252e3443c6675e4ef24a152365ce8efcfb55..4ecc3c01649951a488979ba272649675c7531bf7 100644 GIT binary patch delta 2854 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 diff --git a/dist/structured-coding-codex.zip b/dist/structured-coding-codex.zip index 426fc1e2fb03ebdc8d2a42803deccc56058916a2..27d52cb03337cfe36d98e9b28ab28e0e1512d159 100644 GIT binary patch delta 2866 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 diff --git a/structured-coding/references/standards.md b/structured-coding/references/standards.md index 0f2a258..b0e895b 100644 --- a/structured-coding/references/standards.md +++ b/structured-coding/references/standards.md @@ -1,15 +1,13 @@ # Project standards configuration -Status: **REPORTS; NEVER BLOCKS.** This release reads the configuration and, -on the `run` command, executes the declared checks and classifies each result. -With the optional `standards` preset installed, it also reports after a direct -`git commit`. It blocks nothing: no commit, no merge, no tool call. Automatic triggering is later work; the target behaviour for a hook -remains in the [hook contract](hook-contract.md). +Status: **REPORTS; NEVER BLOCKS.** Checks run when you invoke `run`, and, with the +optional `standards` preset installed, after a direct `git commit`. Nothing is +blocked: no commit, no merge, no tool call. A clean report is not proof that a +check ran; read the outcomes. -A project uses this to state, once, what is true of its whole codebase, so the -operator does not restate it in every planning conversation. Requirements -specific to one PR stay in that PR's conversation and its frozen design; putting -them here would create a second authority over the same decision. +A project states here what is true of its whole codebase, so the operator does +not restate it in every planning conversation. Requirements specific to one PR +belong in that PR's conversation, not here. ## Files @@ -19,59 +17,36 @@ them here would create a second authority over the same decision. | `.structured-coding/standards.local.md` | overlay | Copy [the template](../standards-template.md) to the base path and edit its -single fenced `json` block. The surrounding prose is never read, which is the -point: it carries the options, the alternatives and the reasons that JSON cannot. -Exactly one top-level `json` block is read. A block nested inside another fence -is prose. Zero blocks, several, or unparsable content is refused, naming the file. +single fenced `json` block. The surrounding prose is never read. Exactly one +top-level `json` block is read; a block nested inside another fence is prose. +Zero blocks, several, or unparsable content is refused, naming the file. Both +files are optional, and with neither the shipped defaults apply. -Both files are optional. With neither present the shipped defaults apply and -behaviour is unchanged. +## Layer and trust are decided separately -## Two determinations, decided separately +Layer comes from the filename. Trust comes from `git ls-files`: a tracked file is +shared, so anything it declares outside the allowlist needs approval; an untracked +file is personal, absent from a fresh clone, so authoring it is the approval. -**Layer comes from the filename.** The base states the project's standard. The -overlay may add and tighten only. - -**Trust comes from `git ls-files`.** A tracked file is shared: any contributor can -change it in a pull request, so a tool it declares outside the allowlist needs -approval. An untracked file is personal: it is absent from a fresh clone and can -only have been written by the machine's owner, so authoring it is the approval. - -The two are independent, which removes the special cases. A tracked overlay is an -overlay that happens to be shared. An untracked base is the base and happens to -be personal. Filename must not decide trust, because `.gitignore` does not apply -to a file Git already tracks, so a repository could otherwise commit a file under -the personal name and have it treated as locally authored. +Filename must not decide trust, because `.gitignore` does not apply to a file Git +already tracks. A tracked overlay is simply a shared overlay; an untracked base is +simply a personal base. ## Resolution -Defaults, then base, then overlay. The base may relax the shipped defaults: those -defaults are a suggestion and the project's standard is the authority. Only the -overlay is restricted. +Defaults, then base, then overlay. The base may relax the defaults; they are a +suggestion and the project's standard is the authority. ```text trigger: off < pr < commit scope: changed < repository ``` -The overlay may raise a rank, enable a disabled tool, add a tool, and add a -convention. It may not lower a rank, disable a tool, or narrow a scope. Overlay -conventions are additions, so removing one has no representation at all. - -A relaxing overlay is **refused, naming the field**. Ignoring it would leave a -developer believing a local skip took effect. Applying it would let one machine -opt out of the team's standard while its report still read as compliant. A -genuine local skip belongs in the PR as a recorded deviation. This is the same -rule the workflow already states for documents: a child may not silently relax a -binding restriction. - -## Tool classification - -Grouped by whether the tool executes project code, not by whether the name is -recognized. `ruff` and `pyright` analyze without running anything. `pytest` -imports `conftest.py` by design and `mypy` imports configured plugins, so both are -recognized and still require approval. Anything else is unrecognized and also -requires approval. Recognition is not trust. +The overlay may only raise a rank, enable a disabled tool, add a tool, or add a +convention. A relaxing overlay is **refused, naming the field** — ignoring it +would leave a developer believing a local skip took effect, and applying it would +let one machine opt out while its report still read as compliant. Overlay +conventions are additions, so removing one has no representation. ## Commands @@ -81,95 +56,81 @@ python3 /scripts/standards.py run --project . --base main python3 /scripts/standards.py approve --project . ``` -`inspect` prints the files found with their trust, the effective values, the -layer every value came from, each tool's approval verdict, and a note. It runs -nothing. A refusal exits non-zero with an empty stdout, so a failure cannot be -mistaken for a defaults report. - -`run` adds the result of every enabled check. A base is required while any -enabled check uses changed-file scope, and there is no fallback to a guessed -default branch: guessing which branch a project treats as its base is how a check -silently examines the wrong range. +`inspect` prints the files found, their trust, the effective values, the layer +every value came from, and each tool's approval verdict. It runs nothing. -The base comes from `--base`, or from the session binding when `--host` and -`--session` identify one that recorded it. An explicit `--base` wins, because a -person naming a revision is more specific than a record made when the PR was -bound. With neither, a changed-scope check reports that it was not run. +`run` adds the result of every enabled check. A base is required while any enabled +check uses changed-file scope; it comes from `--base` or from a session binding +identified by `--host` and `--session`, with `--base` winning. There is no +fallback to a guessed default branch, because guessing which branch a project +treats as its base is how a check examines the wrong range and reports +confidently about it. ## Outcomes | Outcome | Meaning | | --- | --- | -| `PASS` | the tool ran and reported nothing | -| `FAIL` | the tool ran and reported findings | -| `INCONCLUSIVE` | it could not run or could not finish: absent, timed out, killed, or exited in its own error mode | -| `NOT RUN` | disabled, no files in scope, no command declared, or approval missing | +| `PASS` | ran, reported nothing | +| `FAIL` | ran, reported findings | +| `INCONCLUSIVE` | could not run or finish: absent, timed out, killed, or exited in its own error mode | +| `NOT RUN` | disabled, nothing in scope, no command, or approval missing | -An absent tool is `INCONCLUSIVE`, never `PASS`: nothing was examined, so nothing -was established. An empty changed-file selection is `NOT RUN` for the same -reason. +An absent tool is `INCONCLUSIVE`, never `PASS`, and so is an empty changed-file +selection: neither examined anything. -Exit codes are mapped per tool, because tools disagree. `ruff check` returns 2 -when it terminates abnormally; `pyright` returns 2 for a fatal error, 3 for a -config file it could not read and 4 for illegal parameters. Those mean the tool -could not run. A command the project supplied has no such map, so a non-zero exit -is reported as `FAIL` and the report says its exit codes are unmapped. +Exit codes are mapped per tool because tools disagree. `ruff check` returns 2 on +abnormal termination; `pyright` returns 2 fatal, 3 unreadable config, 4 illegal +parameters. A command the project supplied has no map, so a non-zero exit is +`FAIL` and the report says the codes are unmapped. -Runs are bounded: a per-tool timeout, a total budget, and captured output -truncated with the truncation marked. +Runs are bounded by a per-tool timeout, a total budget, and truncated output. A +timed-out tool has its process group ended, so nothing it spawned outlives it. -## The optional preset +## Tools the skill does not ship argv for + +`ruff` and `pyright` run with argv this skill supplies; declaring `command` for +them is refused. Anything else needs `command` as argv words, which requires +schema 2, and an approval: ```sh -./scripts/install codex --project /path/to/project --hooks standards +python3 /scripts/standards.py approve --project . ``` -It registers one hook: `PostToolUse` on a direct `git commit`. After the commit -exists, the checks whose trigger is `commit` run and the result is reported to -the agent as context. Nothing is blocked, and the commit has already happened. - -The registered budget is 60 seconds, larger than the 12 the other presets use, -because a check is slower than a notice. The host is blocked while it runs, so -keep the commit trigger to fast checks; a tool that outruns the budget is -`INCONCLUSIVE`, and its process group is ended rather than left running. - -**`trigger: "pr"` has no hook, deliberately.** Neither host has a PR-completed -event. `Stop` is the nearest moment, and it fires at the end of every agent turn, -which is not what `pr` means. Rather than map onto an event that means something -else and then suppress the noise, that granularity stays an explicit -`standards.py run` at review time, alongside the review discipline the workflow -already carries. +`approve` prints every command it authorizes and records the approval under the +Git directory, outside the working tree, so a pull request cannot carry approval +for the command it introduces. The record is bound to those commands alone: +changing one revokes it, an unrelated edit does not. Until an approval matches, +those tools are `NOT RUN`. -## Commands this skill does not ship +Tools are grouped by whether they execute project code, not by whether the name +is recognized. `pytest` imports `conftest.py` by design and `mypy` imports +configured plugins, so both are recognized and still need approval. -`ruff` and `pyright` run with argv this skill supplies, and a configuration -declaring `command` for them is refused: those names mean that argv, and letting -a project redefine them would make the allowlist meaningless. +**A disclosed limit:** `approve` records an operator decision; it does not enforce +who made it. Nothing here prevents an agent from running it, exactly as nothing +prevents an agent from running the installer. -Any other tool must supply `command` as argv words, which requires schema 2, and -must be approved before it runs: +## The optional preset ```sh -python3 /scripts/standards.py approve --project . +./scripts/install codex --project /path/to/project --hooks standards ``` -`approve` prints every command it authorizes and records the approval under the -Git directory, outside the working tree, so a pull request cannot carry approval -for the command it introduces. The record is bound to those commands alone: -changing one revokes it, while an unrelated edit to the configuration does not, -because a prompt that appears after harmless changes is one people learn to -accept without reading. Until an approval matches, those tools are `NOT RUN` and -nothing is executed. +One hook: `PostToolUse` on a direct `git commit`. The checks whose trigger is +`commit` run and the result is reported as context. The commit already exists; +nothing is blocked. The budget is 60 seconds rather than the 12 the other presets +use, and the host is blocked while it runs, so keep the commit trigger to fast +checks. -**A disclosed limit:** `approve` is an operator command, and nothing in this -skill prevents an agent from running it, exactly as nothing prevents an agent -from running the installer. It records a decision; it does not enforce who made -it. +**`trigger: "pr"` has no hook, deliberately.** Neither host has a PR-completed +event, and `Stop`, the nearest moment, fires at the end of every agent turn, +which is not what `pr` means. That granularity stays an explicit `run` at review +time. ## Limits Stdlib and Git only; Python 3.9 or newer. A declared command is argv and is never -passed to a shell. The configuration itself is read as data and never executed. A refusal names the file and the field and never carries the -file's contents. Symlinked, non-regular, oversized and non-UTF-8 configuration -files are refused rather than parsed. Reading a file proves nothing about whether -the agent followed it. +passed to a shell; the configuration itself is read as data and never executed. A +refusal names the file and field and never carries file contents. Symlinked, +non-regular, oversized and non-UTF-8 configuration files are refused. Reading a +file proves nothing about whether the agent followed it. From 988d59b17cf8814bec4190cbb47d4d55d2f062d9 Mon Sep 17 00:00:00 2001 From: Yue Ma Date: Wed, 9 Sep 2026 22:15:59 -0400 Subject: [PATCH 2/2] feat(standards): carry the declared review conventions in the commit report The last piece of the plan, in the smallest honest form. Whatever a project set to the commit trigger now rides along in one report: the checks run, and the declared review conventions are restated for the agent to apply to what it just committed. This needed no new delivery path. The hook already sends a message at that moment, so the conventions are ten lines of content added to it rather than a mechanism of their own, which is what the earlier plan would have built had the Stop handler survived. Conventions with the pr trigger are not orphaned: SKILL.md already routes the agent to the standards contract whenever a project declares one. The hook adds a commit-time reminder, it is not the only path, and the documentation says so. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_016cCs5DdUjLDkP8V3Z5x6XH --- .../structured-coding/references/standards.md | 11 +++++--- .../structured-coding/scripts/standards.py | 20 +++++++++------ .../structured-coding/references/standards.md | 11 +++++--- .../structured-coding/scripts/standards.py | 20 +++++++++------ dist/structured-coding-claude-code.zip | Bin 111540 -> 111709 bytes dist/structured-coding-codex.zip | Bin 111884 -> 112053 bytes scripts/test_standards.py | 24 +++++++++++++++++- structured-coding/references/standards.md | 11 +++++--- structured-coding/scripts/standards.py | 20 +++++++++------ 9 files changed, 80 insertions(+), 37 deletions(-) diff --git a/dist/claude-code/structured-coding/references/standards.md b/dist/claude-code/structured-coding/references/standards.md index b0e895b..872381d 100644 --- a/dist/claude-code/structured-coding/references/standards.md +++ b/dist/claude-code/structured-coding/references/standards.md @@ -116,16 +116,19 @@ prevents an agent from running the installer. ./scripts/install codex --project /path/to/project --hooks standards ``` -One hook: `PostToolUse` on a direct `git commit`. The checks whose trigger is -`commit` run and the result is reported as context. The commit already exists; -nothing is blocked. The budget is 60 seconds rather than the 12 the other presets +One hook: `PostToolUse` on a direct `git commit`. Whatever has `commit` as its +trigger rides along in one report: the checks run, and the declared review +conventions are restated for the agent to apply to what it just committed. +The commit already exists; nothing is blocked. The budget is 60 seconds rather than the 12 the other presets use, and the host is blocked while it runs, so keep the commit trigger to fast checks. **`trigger: "pr"` has no hook, deliberately.** Neither host has a PR-completed event, and `Stop`, the nearest moment, fires at the end of every agent turn, which is not what `pr` means. That granularity stays an explicit `run` at review -time. +time. Conventions with the `pr` trigger still reach the agent, because `SKILL.md` +routes it to this contract whenever a project declares one; the hook adds the +commit-time reminder, not the only path. ## Limits diff --git a/dist/claude-code/structured-coding/scripts/standards.py b/dist/claude-code/structured-coding/scripts/standards.py index 464c724..724052e 100644 --- a/dist/claude-code/structured-coding/scripts/standards.py +++ b/dist/claude-code/structured-coding/scripts/standards.py @@ -679,19 +679,23 @@ def event(project, host, mode, payload): if Path(cwd).resolve(strict=True) != repository.root: return {} effective, _ = resolved(project) - if effective["checks"]["trigger"] != "commit": + parts = [] + if effective["review"]["trigger"] == "commit": + for convention in effective["review"]["conventions"]: + parts.append(f"Convention: {convention}") + if effective["checks"]["trigger"] == "commit": + base = bound_base(project, host, payload.get("session_id")) + for result in outcomes(project, effective, base): + parts.append(f"{result['name']}: {result['outcome']} ({result['reason']})") + if not parts: return {} - base = bound_base(project, host, payload.get("session_id")) - results = outcomes(project, effective, base) - lines = [f"{r['name']}: {r['outcome']} ({r['reason']})" for r in results] return { "hookSpecificOutput": { "hookEventName": "PostToolUse", "additionalContext": bounded( - "Structured Coding standards, reported after this commit. " - "These results are advisory; nothing was blocked, and an " - "INCONCLUSIVE or NOT RUN check is not a pass. " - + "; ".join(lines) + "Structured Coding standards, after this commit. Advisory: nothing " + "was blocked, a convention is for you to apply, and an INCONCLUSIVE " + "or NOT RUN check is not a pass. " + "; ".join(parts) ), } } diff --git a/dist/codex/structured-coding/references/standards.md b/dist/codex/structured-coding/references/standards.md index b0e895b..872381d 100644 --- a/dist/codex/structured-coding/references/standards.md +++ b/dist/codex/structured-coding/references/standards.md @@ -116,16 +116,19 @@ prevents an agent from running the installer. ./scripts/install codex --project /path/to/project --hooks standards ``` -One hook: `PostToolUse` on a direct `git commit`. The checks whose trigger is -`commit` run and the result is reported as context. The commit already exists; -nothing is blocked. The budget is 60 seconds rather than the 12 the other presets +One hook: `PostToolUse` on a direct `git commit`. Whatever has `commit` as its +trigger rides along in one report: the checks run, and the declared review +conventions are restated for the agent to apply to what it just committed. +The commit already exists; nothing is blocked. The budget is 60 seconds rather than the 12 the other presets use, and the host is blocked while it runs, so keep the commit trigger to fast checks. **`trigger: "pr"` has no hook, deliberately.** Neither host has a PR-completed event, and `Stop`, the nearest moment, fires at the end of every agent turn, which is not what `pr` means. That granularity stays an explicit `run` at review -time. +time. Conventions with the `pr` trigger still reach the agent, because `SKILL.md` +routes it to this contract whenever a project declares one; the hook adds the +commit-time reminder, not the only path. ## Limits diff --git a/dist/codex/structured-coding/scripts/standards.py b/dist/codex/structured-coding/scripts/standards.py index 464c724..724052e 100644 --- a/dist/codex/structured-coding/scripts/standards.py +++ b/dist/codex/structured-coding/scripts/standards.py @@ -679,19 +679,23 @@ def event(project, host, mode, payload): if Path(cwd).resolve(strict=True) != repository.root: return {} effective, _ = resolved(project) - if effective["checks"]["trigger"] != "commit": + parts = [] + if effective["review"]["trigger"] == "commit": + for convention in effective["review"]["conventions"]: + parts.append(f"Convention: {convention}") + if effective["checks"]["trigger"] == "commit": + base = bound_base(project, host, payload.get("session_id")) + for result in outcomes(project, effective, base): + parts.append(f"{result['name']}: {result['outcome']} ({result['reason']})") + if not parts: return {} - base = bound_base(project, host, payload.get("session_id")) - results = outcomes(project, effective, base) - lines = [f"{r['name']}: {r['outcome']} ({r['reason']})" for r in results] return { "hookSpecificOutput": { "hookEventName": "PostToolUse", "additionalContext": bounded( - "Structured Coding standards, reported after this commit. " - "These results are advisory; nothing was blocked, and an " - "INCONCLUSIVE or NOT RUN check is not a pass. " - + "; ".join(lines) + "Structured Coding standards, after this commit. Advisory: nothing " + "was blocked, a convention is for you to apply, and an INCONCLUSIVE " + "or NOT RUN check is not a pass. " + "; ".join(parts) ), } } diff --git a/dist/structured-coding-claude-code.zip b/dist/structured-coding-claude-code.zip index 4ecc3c01649951a488979ba272649675c7531bf7..c6f736debff0305d780afc3020caa2d34498a0a2 100644 GIT binary patch delta 4018 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 delta 3862 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