* test(#2554): failing-first suite for path-scoped code review depth overrides Binds the not-yet-built code-review-depth module: segment-aware path-prefix matching of a changed-file set against ordered {paths,depth} rules, resolution order flag > strongest matching rule > global > standard, typed validation errors, and the large-scope downgrade boundary. Also proves behaviorally that workflow.code_review_depth_overrides is not yet a registered config key. Refs #2554 * feat(#2554): resolve code review depth from path-scoped override rules Adds workflow.code_review_depth_overrides — an ordered array of {paths, depth} rules matched against a review's changed-file set by segment-aware path-prefix comparison. Resolution order is --depth= flag, then the strongest matching rule, then workflow.code_review_depth, then standard; a matching rule replaces the global rather than being max'd with it, so quick and standard rules stay meaningful. Glob metacharacters are a hard configuration error rather than sugar for a prefix, and malformed rules halt the review instead of degrading to standard. The resolver is pure and reports its own provenance, so the workflow can print the resolved depth and the rule that matched. The pre-existing >50-file deep-to-standard downgrade moves into the module and now names the rule it overrode. The key is registered centrally rather than as a capability config slice: the federated slice channel admits only boolean/string/number/enum, so an array slice would be dropped as malformed. Closes #2554 * test(#2554): correct depth-provenance assertions and pin out-of-repo paths Two corrections to the failing-first suite. The source assertion for a non-matching rule with no global configured expected 'config'; with no global set the depth comes from the default, and a companion assertion tolerated either value, so both passed against an implementation that derived provenance from whether any rules existed rather than from where the depth came from. The out-of-repo absolute-path case used a home-directory path that matched neither implementation, so it never exercised the defect it named. It now pins the discriminating cases: an absolute path outside the repo root must not match a repo-relative rule, and one under the root must. * docs(#2554): document path-scoped code review depth overrides Reference rows for workflow.code_review_depth_overrides in the configuration, features and commands references plus the locale copies that carry those tables, and in the planning-config reference. Explanation of why escalation is whole-review rather than per-file and why v1 is prefix-only. New how-to for scoping review depth by path, carrying the configuration-error reason table and the distinction between nothing to report and could not look. CONTEXT.md glossary entry and the INVENTORY row for the new CLI module. ja-JP and ko-KR CONFIGURATION.md carry no code_review keys at all, and ko-KR and pt-BR FEATURES.md carry no code-review config table, so those files are deliberately untouched. * fix(#2554): make the depth-misconfiguration halt executable and reject control chars Three review findings, all in this change. The misconfiguration halt was prose rather than shell: the error-printing fence was followed by an unconditional extraction fence, so an ok:false result threw and left the depth empty instead of stopping the review. Prose is not a guard — the two fences are now one block with a real conditional, and anything that is not the literal string true fails closed. An interior control character in a rule path survived validation and reached the provenance string and the summary box; rule paths now reject control characters via a new PATH_CONTROL_CHAR reason, after the glob check so precedence is unchanged. That in turn makes the field record safe to delimit, so the seven node invocations that each re-parsed the same result to read one field collapse to one. Also corrects the glossary entry's illustrative paths, which the glossary-ref check read as real repository references. * fix(#2554): use the fast-check v4 string API and acknowledge workflow growth Two failures from the remote matrix on d3111f45, both this branch's. The property block built its segment arbitrary with fc.stringOf, removed in fast-check v4. Because the arbitrary is constructed in the describe body, the throw took out all four property tests rather than one — they had never executed. Rewritten to fc.string({unit, ...}), the form this repo already uses in emitted-attribution.test.cjs. Every other fast-check helper in the file was audited against the installed module. The emitted-attribution growth arm needed an acknowledgment for code-review.md, which grew 5376 bytes. The pre-existing 3503 fragment keying the same file is spent — its ripple was absorbed when #3503 merged, and the base file is exactly the 34435-byte baseline this growth is measured against — so it cannot clear anything, while the ack lint hard-fails on a duplicate key across two sources. Removed it in favor of the new fragment, which is exactly how #3503 itself replaced the spent 3191 fragment. * docs(#2554): backfill changeset PR number --------- Co-authored-by: sim <sim@local>
125 lines
7.4 KiB
Markdown
125 lines
7.4 KiB
Markdown
# How to scope code review depth by path
|
|
|
|
**Goal:** Escalate `/gsd-code-review` to `deep` for sensitive directories (auth, billing, payments) while the rest of the repository keeps reviewing at the project's normal default — without having to remember `--depth=deep` on every review that happens to touch one of those paths.
|
|
|
|
**Prerequisites:** A project with `/gsd-code-review` enabled (`workflow.code_review: true`). This guide assumes you already have a working default depth via `workflow.code_review_depth`; see [Configuration Reference](../CONFIGURATION.md#workflow-toggles) if you don't.
|
|
|
|
---
|
|
|
|
## The shortest working sequence
|
|
|
|
1. **Set the global default**, if you haven't already:
|
|
|
|
```bash
|
|
gsd config-set workflow.code_review_depth standard
|
|
```
|
|
|
|
2. **Add path-scoped rules** to `workflow.code_review_depth_overrides` in `.planning/config.json`. It's an ordered array of `{ "paths": [...], "depth": "quick" | "standard" | "deep" }` objects:
|
|
|
|
```json
|
|
{
|
|
"workflow": {
|
|
"code_review_depth": "standard",
|
|
"code_review_depth_overrides": [
|
|
{ "paths": ["src/auth"], "depth": "deep" },
|
|
{ "paths": ["src/billing"], "depth": "deep" }
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
3. **Run the review** as usual:
|
|
|
|
```bash
|
|
/gsd-code-review 12
|
|
```
|
|
|
|
4. **Read the provenance line** in the output to confirm which rule (if any) fired — see [Read the resolved-depth line](#read-the-resolved-depth-line) below.
|
|
|
|
---
|
|
|
|
## Worked example: escalate `src/auth` and `src/billing`, leave everything else at `standard`
|
|
|
|
With the config above, a phase that only touches `src/lib/formatter.ts` reviews at `standard` (no rule matches, falls through to the global default). A phase that touches `src/auth/token.ts` and nothing else in an escalated path reviews at `deep`, because a rule matched.
|
|
|
|
**Escalation is whole-review, not per-file.** If a phase touches both `src/lib/formatter.ts` and `src/auth/token.ts`, the *entire* review — including `formatter.ts` — runs at `deep`. Depth is a single scalar handed to the reviewer agent; one matched sensitive file is enough to raise the whole review, so a sensitive file is never reviewed shallowly because it shared a phase with unrelated code.
|
|
|
|
Matching is by **whole path segment**, not substring:
|
|
|
|
| Changed file | Matches rule `src/auth`? |
|
|
|---|---|
|
|
| `src/auth/token.ts` | Yes |
|
|
| `src/auth` (the file itself) | Yes |
|
|
| `src/authfoo/x.ts` | No — `authfoo` is a different segment |
|
|
| `docs/src/auth/x.ts` | No — prefix is anchored at the path root, not a substring search |
|
|
|
|
Matching is case-sensitive, following git: a rule written `Src/Auth` will not match `src/auth/x.ts`.
|
|
|
|
**Rules win over the global default, even when weaker.** If `workflow.code_review_depth` is `deep` but a matched rule says `quick`, the review runs at `quick` — the rule always replaces the global for files it matches. This is deliberate: if the strongest tier always won, a `quick` or `standard` rule could never actually take effect whenever the project default was `deep`, making it silently inert.
|
|
|
|
**Only globs are rejected — not the paths themselves.** `workflow.code_review_depth_overrides` supports directory-prefix strings only (`src/auth`, not `src/auth/**`). There is no glob engine in this project; write the prefix and let segment-aware matching do the rest.
|
|
|
|
---
|
|
|
|
## Read the resolved-depth line
|
|
|
|
Every review prints one line naming the resolved depth and why:
|
|
|
|
```
|
|
Review depth: deep (matched rule 0: src/auth)
|
|
```
|
|
|
|
The parenthetical names the source:
|
|
|
|
| Provenance text | Meaning |
|
|
|---|---|
|
|
| `from --depth flag` | The `--depth=` CLI flag was passed; it always wins over both rules and config. |
|
|
| `matched rule N: <path>` | Rule at index `N` (0-based, in declaration order) matched on prefix `<path>` and set the depth. |
|
|
| `from workflow.code_review_depth` | No rule matched this review's file set; the global config value was used. |
|
|
| `default` | Neither a rule, config value, nor flag applied; the built-in `standard` default was used. |
|
|
|
|
If the review scope exceeds 50 files and the resolved depth is `deep`, the existing large-scope downgrade still fires — but now it names the rule it overrode:
|
|
|
|
```
|
|
Switching from deep to standard depth for large file count (overrides matched rule 0: src/auth).
|
|
```
|
|
|
|
A configured sensitive-path policy is not exempt from this downgrade — the same guard that downgrades `--depth=deep` on a large scope also downgrades a rule-sourced `deep`.
|
|
|
|
---
|
|
|
|
## Nothing to report vs. could not look
|
|
|
|
Two outcomes look similar but mean opposite things:
|
|
|
|
- **A rule matches nothing.** This is not an error and is not reported specially — it means this review simply didn't touch any path the rule covers. The provenance line falls through to `from workflow.code_review_depth` or `default`, exactly as if the rule didn't exist. This is "nothing to report": the policy exists, was checked, and had nothing to say about this particular review.
|
|
- **The configuration is rejected.** This is "could not look": the review halts before doing any file-level work and prints every collected validation error. Never conflate the two — a validly-configured rule set with no match for this review is a healthy, silent no-op; a malformed rule set is a hard stop.
|
|
|
|
---
|
|
|
|
## Configuration error reasons
|
|
|
|
If `workflow.code_review_depth_overrides` is malformed, `/gsd-code-review` prints one error per defect (all of them, not just the first) and stops — it never silently falls back to a default depth. Errors report the reason as one of the following typed values (from `src/code-review-depth.cts`):
|
|
|
|
| Reason | Meaning | Fix |
|
|
|---|---|---|
|
|
| `not_an_array` | `workflow.code_review_depth_overrides` itself is not an array (object, string, number, `null`, etc.) | Set it to an array of rule objects, or `[]` to disable overrides. |
|
|
| `rule_not_object` | An entry in the array is not a plain object (a string, an array, `null`, `0`, etc.) | Each entry must be a `{ "paths": [...], "depth": "..." }` object. |
|
|
| `paths_malformed` | A rule's `paths` is missing, not an array, empty, or contains a non-string entry | Give `paths` a non-empty array of strings, e.g. `["src/auth"]`. |
|
|
| `invalid_depth` | A rule's `depth` is missing or not one of `quick`, `standard`, `deep` | Set `depth` to exactly one of `quick`, `standard`, or `deep`. |
|
|
| `glob_unsupported` | A rule path contains `*` or `?` (e.g. `src/auth/**`) | Use a directory prefix instead: `src/auth`, not `src/auth/**` or `src/auth/*.ts`. |
|
|
| `path_traversal` | A rule path contains a `..` segment | Remove the `..` segment; write a plain repo-relative prefix. |
|
|
| `path_absolute` | A rule path is absolute (`/src/auth`, `C:\src\auth`) | Use a path relative to the repo root: `src/auth`, not `/src/auth`. |
|
|
| `path_empty` | A rule path is empty, whitespace-only, or normalizes to empty or `.` | Give the path real content, e.g. `src/auth` rather than `""` or `"."`. |
|
|
|
|
Each printed error names the rule index (and, where applicable, the offending path or depth value) so you can find the exact entry to fix without guessing which rule in the array is broken.
|
|
|
|
---
|
|
|
|
## Related
|
|
|
|
- [Configuration Reference](../CONFIGURATION.md#workflow-toggles) — full schema for `workflow.code_review_depth_overrides` and `workflow.code_review_depth`
|
|
- [Feature Reference — Code Review Pipeline](../FEATURES.md#93-code-review-pipeline) — why escalation is whole-review and why v1 is prefix-only, not glob
|
|
- [`/gsd-code-review`](../COMMANDS.md#gsd-code-review) — command reference and the `--depth=` flag
|
|
- [docs index](../README.md)
|