wdi-method 0.6.30 → 0.6.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +68 -0
- package/README.md +3 -0
- package/bin/wdi-method.js +121 -14
- package/kit/.constitution/method/document/architecture-guide.md +217 -209
- package/kit/.constitution/method/document/corpus-guide.md +522 -517
- package/kit/.constitution/method/document/decision-guide.md +236 -216
- package/kit/.constitution/method/document/delivery-flow-guide.md +20 -0
- package/kit/.constitution/method/document/prd-guide.md +245 -245
- package/kit/.constitution/method/document/templates/design-system.md +96 -66
- package/kit/.constitution/method/document/templates/experience.md +62 -0
- package/kit/.constitution/method/document/templates/structure-codebase.md +131 -129
- package/kit/.constitution/method/document/templates/ux.md +78 -76
- package/kit/.constitution/method/document/ux-guide.md +161 -115
- package/kit/.constitution/method/method-glossary.md +3 -0
- package/kit/.constitution/method/scripts/validate.py +3310 -3200
- package/kit/.constitution/method/structure-guide.md +204 -202
- package/kit/.constitution/method/why/artifact-map.md +158 -157
- package/kit/skills/wdi-blueprint/SKILL.md +271 -264
- package/kit/skills/wdi-component/SKILL.md +179 -174
- package/kit/skills/wdi-decision/SKILL.md +206 -203
- package/kit/skills/wdi-help/SKILL.md +127 -125
- package/kit/skills/wdi-init/SKILL.md +9 -4
- package/kit/skills/wdi-problem/SKILL.md +114 -108
- package/kit/skills/wdi-product/SKILL.md +167 -162
- package/kit/skills/wdi-reconcile/SKILL.md +170 -169
- package/kit/skills/wdi-upgrade/SKILL.md +234 -215
- package/kit/skills/wdi-ux/SKILL.md +187 -169
- package/kit-overlay/AGENTS.md +3 -1
- package/package.json +1 -1
- package/scaffold/.control/registry/index.yaml +2 -1
|
@@ -1,169 +1,170 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: wdi-reconcile
|
|
3
|
-
description: Use before a gate, or after a batch of changes, to find drift between .what, .how, and .control — against each other and against the rules in .constitution. Scoped to what the gates already passed have actually produced, and to what each component's mode actually demands. Read-only — it reports, it never edits.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# WDI Reconcile
|
|
7
|
-
|
|
8
|
-
Documents drift apart quietly. An SRS gets amended while its SDD does not; a `DEC-` is accepted and never
|
|
9
|
-
applied; a ticket ships behaviour the use case never described. None of this shows up as an error, which is
|
|
10
|
-
why it needs a pass that looks for it on purpose.
|
|
11
|
-
|
|
12
|
-
This skill is **read-only**. It MUST NOT edit anything. Its output is a report, and every fix it
|
|
13
|
-
recommends is performed by another skill.
|
|
14
|
-
|
|
15
|
-
**It is not run after every change, and it MUST NOT be offered as one.** Its triggers are a gate, and a
|
|
16
|
-
batch of changes large enough that nobody can hold the delta in their head. Offering it after a two-file
|
|
17
|
-
edit is how a ten-minute change grows a twenty-minute tail — and the offer itself costs the reader
|
|
18
|
-
something, because declining it repeatedly teaches them to decline it always.
|
|
19
|
-
|
|
20
|
-
**Drift is reported only where it is load-bearing** — where a reader believing the stale sentence would
|
|
21
|
-
make the wrong repair. A document behind the code is in its expected state, not a defect;
|
|
22
|
-
`wdi-review` § Stale is not a finding owns the test, and it applies here unchanged.
|
|
23
|
-
|
|
24
|
-
## Step 1 — Scope by gate, then by `mode`
|
|
25
|
-
|
|
26
|
-
Two filters, and skipping either produces a report that is red where the plan says it should be.
|
|
27
|
-
|
|
28
|
-
**By gate.** The corpus is built gate by gate, so most of it is legitimately absent most of the time.
|
|
29
|
-
Establish which gate the work stands at — `wdi-help` answers that — and check only what has been passed.
|
|
30
|
-
|
|
31
|
-
| Gate passed | In scope |
|
|
32
|
-
|---|---|
|
|
33
|
-
| G1 | `brief.md` |
|
|
34
|
-
| G2 | + every `_prd/<initiative>/`, UX output wherever it currently sits, `product_components` with `mode` and `risk_accepted` |
|
|
35
|
-
| G3 | + every `.what/<pc>/` § UC Catalogue and § Actor Register, `domain-model.md`, `business-rules.md`, the spine, the C4 set, `containers`, the three inventories |
|
|
36
|
-
| G4 | + whatever each component's `mode` demands in `.what/<pc>/` and `.how/<pc>/` |
|
|
37
|
-
| G5 | + the contract, `specs.yaml`, ticket files, tests, `defects.yaml`, RTM rows |
|
|
38
|
-
|
|
39
|
-
**By `mode`.** An artifact a component's `mode` does not demand MUST NOT be reported as missing. A
|
|
40
|
-
component at `catalog` has an SDD skeleton and no depth, and that is a **finished** state — G4 is skipped
|
|
41
|
-
there. Reporting it as a gap is the failure that would make this pass unusable at the setting most
|
|
42
|
-
projects run.
|
|
43
|
-
|
|
44
|
-
An artifact a **later** gate produces MUST NOT be reported as missing either. That is not drift, it is
|
|
45
|
-
the plan. The corpus running ahead of the code is likewise normal and deliberate.
|
|
46
|
-
|
|
47
|
-
A narrower scope MAY be asked for — one Product Component, one initiative, one layer. State the scope
|
|
48
|
-
in one line before checking, and say what it excluded.
|
|
49
|
-
|
|
50
|
-
**A conflict the owner has already decided is not drift.** Where a document disagrees with the code
|
|
51
|
-
because the owner chose the code, the finding is that the **document has not been edited yet** — one
|
|
52
|
-
line, naming the edit — never a re-statement of the conflict and never a question. The decision is not
|
|
53
|
-
reopened here, and `delivery-flow-guide.md` says why: the survey behind that warning was spent when the
|
|
54
|
-
owner answered it.
|
|
55
|
-
|
|
56
|
-
## Step 2 — Run the validators first
|
|
57
|
-
|
|
58
|
-
Run `uv run .constitution/method/scripts/validate.py`, and `uv run .constitution/method/scripts/inventory.py` when code
|
|
59
|
-
exists. `goal-has-fr`–`cites-resolve` answer everything that can be **counted**, and you MUST NOT re-derive by reading what they
|
|
60
|
-
already report. Carry their findings as they came, then spend the reading on what no validator can see.
|
|
61
|
-
|
|
62
|
-
`.control/generated/` is their output and MUST NOT be read as an independent source. When it is
|
|
63
|
-
missing or stale, say so and name `validate.py --generate` rather than working around it.
|
|
64
|
-
|
|
65
|
-
## Step 3 — What only a reader can find
|
|
66
|
-
|
|
67
|
-
| Direction | Question |
|
|
68
|
-
|---|---|
|
|
69
|
-
| Top-down | Does every `applied` `DEC-` actually appear in the files its `touches` names? |
|
|
70
|
-
| Bottom-up | Does anything in `.how/<pc>/` describe behaviour that `.what/<pc>/` never promised? |
|
|
71
|
-
| Decisions | Is there an `accepted` `DEC-` that was never applied, or an `applied` one with an empty `touches`? The second is `applied-dec-touches`; the first no validator can see |
|
|
72
|
-
| Chain | `BG → CAP → FR/NFR → UC → ticket → test` — where does it break? |
|
|
73
|
-
| Depth | Does any document carry more than its component's `mode` demands? Over-writing is drift too, and it is the direction nobody looks for |
|
|
74
|
-
| Vocabulary | Does any document use a domain noun that `.control/product-glossary.md` does not define, or a synonym for one it does? Detect against the rule in `wdi-blueprint`; MUST NOT keep a second rule here |
|
|
75
|
-
| Registry | Does `components.yaml` still describe what the corpus contains — a `<pc>` folder with no entry, an `LC` with no prose in the slot its `type` names, a container in the C4 set but not in `containers`, `owns:` claiming an entity another component also claims |
|
|
76
|
-
| Inventory | Do the three inventories still match the code? `inventory.py` answers it; carry its findings rather than re-deriving them |
|
|
77
|
-
| **Constitution** | Does an artifact break the rule its own guide states? |
|
|
78
|
-
| **Homeless output** | Does anything in `_bmad-output/` have no row in the ownership table in `corpus-guide.md`, or a row whose named owner is not installed? |
|
|
79
|
-
| **
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
|
93
|
-
| A
|
|
94
|
-
| A
|
|
95
|
-
| A
|
|
96
|
-
| A
|
|
97
|
-
| `
|
|
98
|
-
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
|
124
|
-
| `
|
|
125
|
-
| `
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
|
148
|
-
|
|
|
149
|
-
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
1
|
+
---
|
|
2
|
+
name: wdi-reconcile
|
|
3
|
+
description: Use before a gate, or after a batch of changes, to find drift between .what, .how, and .control — against each other and against the rules in .constitution. Scoped to what the gates already passed have actually produced, and to what each component's mode actually demands. Read-only — it reports, it never edits.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# WDI Reconcile
|
|
7
|
+
|
|
8
|
+
Documents drift apart quietly. An SRS gets amended while its SDD does not; a `DEC-` is accepted and never
|
|
9
|
+
applied; a ticket ships behaviour the use case never described. None of this shows up as an error, which is
|
|
10
|
+
why it needs a pass that looks for it on purpose.
|
|
11
|
+
|
|
12
|
+
This skill is **read-only**. It MUST NOT edit anything. Its output is a report, and every fix it
|
|
13
|
+
recommends is performed by another skill.
|
|
14
|
+
|
|
15
|
+
**It is not run after every change, and it MUST NOT be offered as one.** Its triggers are a gate, and a
|
|
16
|
+
batch of changes large enough that nobody can hold the delta in their head. Offering it after a two-file
|
|
17
|
+
edit is how a ten-minute change grows a twenty-minute tail — and the offer itself costs the reader
|
|
18
|
+
something, because declining it repeatedly teaches them to decline it always.
|
|
19
|
+
|
|
20
|
+
**Drift is reported only where it is load-bearing** — where a reader believing the stale sentence would
|
|
21
|
+
make the wrong repair. A document behind the code is in its expected state, not a defect;
|
|
22
|
+
`wdi-review` § Stale is not a finding owns the test, and it applies here unchanged.
|
|
23
|
+
|
|
24
|
+
## Step 1 — Scope by gate, then by `mode`
|
|
25
|
+
|
|
26
|
+
Two filters, and skipping either produces a report that is red where the plan says it should be.
|
|
27
|
+
|
|
28
|
+
**By gate.** The corpus is built gate by gate, so most of it is legitimately absent most of the time.
|
|
29
|
+
Establish which gate the work stands at — `wdi-help` answers that — and check only what has been passed.
|
|
30
|
+
|
|
31
|
+
| Gate passed | In scope |
|
|
32
|
+
|---|---|
|
|
33
|
+
| G1 | `brief.md` |
|
|
34
|
+
| G2 | + every `_prd/<initiative>/`, UX output wherever it currently sits, `product_components` with `mode` and `risk_accepted` |
|
|
35
|
+
| G3 | + every `.what/<pc>/` § UC Catalogue and § Actor Register, `domain-model.md`, `business-rules.md`, the spine, the C4 set, `containers`, the three inventories |
|
|
36
|
+
| G4 | + whatever each component's `mode` demands in `.what/<pc>/` and `.how/<pc>/` |
|
|
37
|
+
| G5 | + the contract, `specs.yaml`, ticket files, tests, `defects.yaml`, RTM rows |
|
|
38
|
+
|
|
39
|
+
**By `mode`.** An artifact a component's `mode` does not demand MUST NOT be reported as missing. A
|
|
40
|
+
component at `catalog` has an SDD skeleton and no depth, and that is a **finished** state — G4 is skipped
|
|
41
|
+
there. Reporting it as a gap is the failure that would make this pass unusable at the setting most
|
|
42
|
+
projects run.
|
|
43
|
+
|
|
44
|
+
An artifact a **later** gate produces MUST NOT be reported as missing either. That is not drift, it is
|
|
45
|
+
the plan. The corpus running ahead of the code is likewise normal and deliberate.
|
|
46
|
+
|
|
47
|
+
A narrower scope MAY be asked for — one Product Component, one initiative, one layer. State the scope
|
|
48
|
+
in one line before checking, and say what it excluded.
|
|
49
|
+
|
|
50
|
+
**A conflict the owner has already decided is not drift.** Where a document disagrees with the code
|
|
51
|
+
because the owner chose the code, the finding is that the **document has not been edited yet** — one
|
|
52
|
+
line, naming the edit — never a re-statement of the conflict and never a question. The decision is not
|
|
53
|
+
reopened here, and `delivery-flow-guide.md` says why: the survey behind that warning was spent when the
|
|
54
|
+
owner answered it.
|
|
55
|
+
|
|
56
|
+
## Step 2 — Run the validators first
|
|
57
|
+
|
|
58
|
+
Run `uv run .constitution/method/scripts/validate.py`, and `uv run .constitution/method/scripts/inventory.py` when code
|
|
59
|
+
exists. `goal-has-fr`–`cites-resolve` answer everything that can be **counted**, and you MUST NOT re-derive by reading what they
|
|
60
|
+
already report. Carry their findings as they came, then spend the reading on what no validator can see.
|
|
61
|
+
|
|
62
|
+
`.control/generated/` is their output and MUST NOT be read as an independent source. When it is
|
|
63
|
+
missing or stale, say so and name `validate.py --generate` rather than working around it.
|
|
64
|
+
|
|
65
|
+
## Step 3 — What only a reader can find
|
|
66
|
+
|
|
67
|
+
| Direction | Question |
|
|
68
|
+
|---|---|
|
|
69
|
+
| Top-down | Does every `applied` `DEC-` actually appear in the files its `touches` names? |
|
|
70
|
+
| Bottom-up | Does anything in `.how/<pc>/` describe behaviour that `.what/<pc>/` never promised? |
|
|
71
|
+
| Decisions | Is there an `accepted` `DEC-` that was never applied, or an `applied` one with an empty `touches`? The second is `applied-dec-touches`; the first no validator can see |
|
|
72
|
+
| Chain | `BG → CAP → FR/NFR → UC → ticket → test` — where does it break? |
|
|
73
|
+
| Depth | Does any document carry more than its component's `mode` demands? Over-writing is drift too, and it is the direction nobody looks for |
|
|
74
|
+
| Vocabulary | Does any document use a domain noun that `.control/product-glossary.md` does not define, or a synonym for one it does? Detect against the rule in `wdi-blueprint`; MUST NOT keep a second rule here |
|
|
75
|
+
| Registry | Does `components.yaml` still describe what the corpus contains — a `<pc>` folder with no entry, an `LC` with no prose in the slot its `type` names, a container in the C4 set but not in `containers`, `owns:` claiming an entity another component also claims |
|
|
76
|
+
| Inventory | Do the three inventories still match the code? `inventory.py` answers it; carry its findings rather than re-deriving them |
|
|
77
|
+
| **Constitution** | Does an artifact break the rule its own guide states? |
|
|
78
|
+
| **Homeless output** | Does anything in `_bmad-output/` have no row in the ownership table in `corpus-guide.md`, or a row whose named owner is not installed? |
|
|
79
|
+
| **Gate record** | Does downstream work exist for a gate `gates_passed` does not list — components without `G2`, a spine without `G3`? Report it for the owner to answer; `delivery-flow-guide.md` § *Recording a gate that passed* |
|
|
80
|
+
| **Evidence** | `cites-resolve` answers the mechanical half — does every cited path still resolve. What is left for a reader: does the file still **contain** what is cited |
|
|
81
|
+
|
|
82
|
+
The chain check overlaps the validators on purpose. Validators answer what can be counted; this pass
|
|
83
|
+
answers what has to be read — a `UC` that exists and is wrong passes `fr-has-uc` and fails here.
|
|
84
|
+
|
|
85
|
+
### The Constitution check
|
|
86
|
+
|
|
87
|
+
The other checks compare documents with each other. This one compares a document with the rule that
|
|
88
|
+
governs it, and the four failures worth looking for are the ones no ID chain records:
|
|
89
|
+
|
|
90
|
+
| Looks like | Rule it breaks |
|
|
91
|
+
|---|---|
|
|
92
|
+
| Solution shape in `.what/` — a table, an endpoint, a framework | `corpus-guide.md`, and it is the most common one |
|
|
93
|
+
| A promise appearing first in `.how/` | The same rule, in the other direction |
|
|
94
|
+
| A file in the wrong slot | `.what/` numbers are reading order, `.how/` numbers are ABCE classification |
|
|
95
|
+
| A layer written by a skill that does not own it | The ownership table in `corpus-guide.md` |
|
|
96
|
+
| A rule stated in a `.constitution/method/` file | `status: Reference` — it explains, it MUST NOT bind |
|
|
97
|
+
| A `Reference` file contradicting a guide | The guide wins, and the contradiction is a defect to report |
|
|
98
|
+
| `CONTEXT.md` or `CONTEXT-MAP.md` outside `_bmad-output/` | A second home for the vocabulary and for where each context lives. The homes are `.control/product-glossary.md`, `components.yaml`, and the two structure maps |
|
|
99
|
+
| A `docs/` folder holding corpus or rules — `docs/adr/` above all | **Article 3**: this method has no `docs/` layer, and a leftover one is inventory to sort rather than a second home |
|
|
100
|
+
|
|
101
|
+
**The last two are hunted by artifact, not by author, and that is deliberate.** An engine invoked outside its
|
|
102
|
+
WDI wrapper still writes what it always writes — `wdi-blueprint` points `domain-modeling` at
|
|
103
|
+
`_bmad-output/`, but a skill that calls it directly does not. Policing who invoked what is impossible from
|
|
104
|
+
here; noticing the file that appeared is not. Inside `_bmad-output/` all three are legitimate working output
|
|
105
|
+
and MUST NOT be reported.
|
|
106
|
+
|
|
107
|
+
You MUST NOT invent a rule to fail an artifact against. Every finding here MUST quote the guide it comes
|
|
108
|
+
from. A file at `status: Draft` MAY be read as guidance but MUST NOT be used to reject anything — that
|
|
109
|
+
holds for all three `.constitution/project/codebase-*-guide.md` — and a file at `status: Reference` MUST NOT be cited to reject
|
|
110
|
+
anything at all.
|
|
111
|
+
|
|
112
|
+
### What the Evidence check is, and what it is not
|
|
113
|
+
|
|
114
|
+
It checks whether **citations still resolve**. It does **not** check whether the code implements the
|
|
115
|
+
corpus, and you MUST NOT widen it into that.
|
|
116
|
+
|
|
117
|
+
A general corpus-versus-code comparison would be red through the middle of every spec, and a check
|
|
118
|
+
that is always red is a check people learn to skip. What is already covered elsewhere MUST NOT be
|
|
119
|
+
re-reported here:
|
|
120
|
+
|
|
121
|
+
| Already answered by | Case |
|
|
122
|
+
|---|---|
|
|
123
|
+
| A red RTM row | Promised, not built yet |
|
|
124
|
+
| `fr-has-uc` · `uc-scheduled` | Documented, never scheduled |
|
|
125
|
+
| `ticket-has-test` | A ticket closed with no named test |
|
|
126
|
+
| `inventory.py` | The plan and the code disagreeing about a table, endpoint, or screen |
|
|
127
|
+
|
|
128
|
+
That leaves exactly one gap, and it is the one this check fills: **a descriptive claim about code
|
|
129
|
+
that already exists, which has quietly stopped being true.** A file renamed, a function removed, a
|
|
130
|
+
route unregistered — nothing in the ID chain moves, so no validator can see it.
|
|
131
|
+
|
|
132
|
+
Two properties keep the check healthy:
|
|
133
|
+
|
|
134
|
+
- It fires **only where a citation exists**. Prose with no cited source produces no finding, so there
|
|
135
|
+
is no flood.
|
|
136
|
+
- It is cheap: a path and symbol lookup, not a semantic judgement.
|
|
137
|
+
|
|
138
|
+
A claim the check proves absent MUST be labelled `[MISSING]` in the document rather than deleted —
|
|
139
|
+
see the evidence ladder in `sdd-guide.md`, which owns that rule.
|
|
140
|
+
|
|
141
|
+
## Output — an action matrix
|
|
142
|
+
|
|
143
|
+
Each finding gets four fields, and the last two are what make the report usable:
|
|
144
|
+
|
|
145
|
+
| Field | Content |
|
|
146
|
+
|---|---|
|
|
147
|
+
| What | The drift, stated concretely with both sides quoted |
|
|
148
|
+
| Where | File and section on each side |
|
|
149
|
+
| Which is right | Your reading, stated as a judgement, not hidden as a fact |
|
|
150
|
+
| Who fixes it | The skill that owns the layer needing the change — `wdi-problem` · `wdi-product` · `wdi-blueprint` · `wdi-component` · `wdi-ux` · `wdi-build` · `wdi-init` · `wdi-decision` · `wdi-question` · `wdi-log` · `wdi-systematic-debugging` · a human |
|
|
151
|
+
|
|
152
|
+
A finding you cannot assign to a fixer MUST be reported as an open question rather than left as an
|
|
153
|
+
observation.
|
|
154
|
+
|
|
155
|
+
Separate **drift** from **conflict** in the report, because they are answered differently: drift has
|
|
156
|
+
a right side and needs carrying across; a conflict has no clearly right side and needs deciding.
|
|
157
|
+
|
|
158
|
+
## Rules
|
|
159
|
+
|
|
160
|
+
- You MUST NOT edit. Not a typo, not a heading, not a link. The value of a read-only pass is that its
|
|
161
|
+
report can be trusted to describe the state before anything moved.
|
|
162
|
+
- You MUST NOT rank a finding as minor because it is small. Vocabulary drift is small and is the one
|
|
163
|
+
that compounds fastest.
|
|
164
|
+
- When two documents disagree and neither is clearly right, that is a decision, not a drift. Route it
|
|
165
|
+
to `wdi-decision` and say so.
|
|
166
|
+
- An output with no home MUST be reported as a gap in the method, and its home MUST NOT be guessed. The
|
|
167
|
+
gap has nowhere else to surface. Exploration output — research, brainstorming, forge, PRFAQ — is
|
|
168
|
+
homeless **by rule** and MUST NOT be reported.
|
|
169
|
+
- Run before every gate, and after any batch of edits that touched one layer without the other.
|
|
170
|
+
Running it only when something feels wrong defeats it — drift is silent by definition.
|