Skip to content

Commit 3de1186

Browse files
thymikeeclaude
andcommitted
docs(adr): anchor ADR 0032 to the commit its tables were measured at
Status names d9959f5, where the harness produced the tables (its production tree is 7dda0c2's); the Measured section separates what the harness emits from the line counts of the deletion table; runtimes are re-measured there, and fallow's marginal cost is stated as within noise. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent d9959f5 commit 3de1186

1 file changed

Lines changed: 26 additions & 28 deletions

File tree

‎docs/adr/0032-layering-graph-engine.md‎

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,25 @@
22

33
## Status
44

5-
Accepted (2026-10-07). Decision spike for #3278 (umbrella #3276, workstream 2), measured at
6-
`7dda0c2bf`. No rule moves. The spike's harness is temporary; see [Deletion](#deletion).
5+
Accepted (2026-10-07). Spike for #3278 (umbrella #3276), measured by the harness at `d9959f510`
6+
(production tree of `7dda0c2bf`). No rule moves; the harness is temporary ([Deletion](#deletion)).
77

88
## Rules at a glance
99

1010
- R2, R4, R5, R6, R77, R78, R14 and R71 stay on the `resolveImportEdges` edge model. Neither
1111
dependency-cruiser 18.5 nor fallow 3.32 replaces them.
1212
- Every layering rule reads one edge model. Do not enforce a subset from a second import graph: both
1313
engines disagree with it (tables below), and about 20 AST and ownership rules keep it alive.
14-
- Do not upgrade fallow to 3.x to get `boundaries`.
1514
- Fix the custom gap at the shared parser: `parseImports` misses TypeScript `import('x').T` type
1615
positions (9 file pairs). The fix may move the R9 type-cycle ratchet.
17-
- Revisit when a [trigger](#revisit-triggers) fires, re-running the harness first.
16+
- Do not upgrade fallow to 3.x for `boundaries`; revisit on a [trigger](#revisit-triggers).
1817

1918
## Measured
2019

21-
`scripts/layering/boundary-engine-spike.ts` produces every number below (usage in its header). It
22-
generates both engine configs from `TARGET_DAG_RANK` and the rule tables, and refuses to run unless
23-
the production roots hold exactly the tracked tree (1,837 files, 41 zones). Recorded R6 and R78
24-
edges go to each engine's known-violations baseline.
20+
`scripts/layering/boundary-engine-spike.ts` produces the edge-set, parity, clean-tree and runtime
21+
numbers; the deletion table counts lines of the named code. The harness builds both engine configs
22+
from `TARGET_DAG_RANK` and the rule tables, runs only on exactly the tracked production tree (1,837
23+
files, 41 zones), and gives recorded R6 and R78 edges to each engine's known-violations baseline.
2524

2625
### Edge-set diff against `resolveImportEdges`
2726

@@ -36,19 +35,18 @@ edges go to each engine's known-violations baseline.
3635
The 9 extra pairs are `import('./x').T` type positions custom misses; both engines are right.
3736

3837
The 3.1.1 cross-check missed 88 dynamic and type-only edges; 18.5 misses none but mislabels 46 pairs
39-
(33 dynamic → type, 8 dynamic+type → type, 5 dynamic+value → value, with tsc or swc). It deduplicates
40-
a file's dependencies on `(specifier, moduleSystem, type-only flag)` (`getDependencyUniqueKey`, not
41-
configurable), so `typeof import('./x').f` and `await import('./x')` keep whichever comes first.
38+
(33 dynamic → type, 8 dynamic+type → type, 5 dynamic+value → value, with tsc or swc): its
39+
hardcoded `getDependencyUniqueKey` dedupes on `(specifier, moduleSystem, type-only flag)`, so
40+
`typeof import('./x').f` and `await import('./x')` keep whichever comes first.
4241

4342
fallow's value-versus-type split matches custom exactly (6,136 runtime pairs each), with no dynamic
4443
kind. Its boundary check follows `export type *` barrels to the declaring module, so
45-
`src/commands/cli-runner.ts → src/agent-device-client.ts` counts as `commands → client`: one more R6
46-
finding. Clean-tree findings: custom 0, depcruise 10 (3 R6 survivors, 7 recorded R78 edges), fallow 11.
44+
`src/commands/cli-runner.ts → src/agent-device-client.ts` counts as `commands → client`, one more R6
45+
finding. Clean tree: custom 0, depcruise 10 (3 R6 survivors, 7 recorded R78 edges), fallow 11.
4746

4847
### Planted-violation parity
4948

50-
`flag` rows must be reported; `pass` rows are the closest negative the custom rule admits. ✗ is a
51-
miss on `flag` or a false positive on `pass`.
49+
`pass` rows are the closest negative custom admits; ✗ is a missed `flag` or a flagged `pass`.
5250

5351
| Plant | Expect | custom | depcruise | fallow |
5452
| --- | --- | --- | --- | --- |
@@ -84,13 +82,13 @@ fallow's R77 rule pack carries an id, message and line. On the stale row, depcru
8482

8583
### Runtime
8684

87-
Medians of 5 runs after a warm-up, on a shared host (load average about 9):
85+
Medians of 5 runs after a warm-up, on a shared host (load average 9–17):
8886

8987
| | custom | depcruise | fallow |
9088
| --- | --- | --- | --- |
91-
| graph build | 0.75 s, shared and kept | 1.63 s (tsc), 1.66 s (swc) | 3.26 s standalone |
89+
| graph build | 0.74 s, shared and kept | 1.66 s (tsc), 1.56 s (swc) | 3.04 s standalone |
9290
| the eight rules | 0.10 s | included | included |
93-
| marginal CI cost | 0.10 s | +1.6 s, a second graph | +0.26 s in the repo's dead-code run (3.27 → 3.53 s) |
91+
| marginal CI cost | 0.10 s | +1.6 s, a second graph | none measurable in the repo's dead-code run (3.23 → 3.13 s) |
9492

9593
### Lines deletable
9694

@@ -108,9 +106,9 @@ Rule plus test lines a full migration would delete. The resolver and the R4/R5/R
108106
| R14, R71 | `retired-paths-policy.ts` | 218 | TS only (R14 gap) | TS only, generic |
109107
| shared | registry, imports, success line in `check.ts` | ≈25 | | |
110108

111-
About 1,224 lines, half of them tests; a migration adds about 110 lines of generated config. An
112-
exact-edge R6 baseline also changes policy: it rejects swapping one inversion for another within a
113-
pair, which the per-pair count ratchet admits.
109+
About 1,224 lines, half of them tests; a migration adds a generator like the harness's
110+
`depcruiseConfig` and helpers (122 lines). An exact-edge R6 baseline also changes policy: it rejects
111+
swapping one inversion for another within a pair, which the per-pair count ratchet admits.
114112

115113
## Decision
116114

@@ -122,10 +120,10 @@ the custom graph:
122120
fail on stale entries, and needs `typescript@<7` or `@swc/core` beside TypeScript 7 (+1.6 s).
123121
Adopting it for four rules would delete about 430 lines and put two disagreeing import graphs
124122
behind one gate.
125-
- **fallow** is already a dependency, adds about 0.3 s, and has the best stale gate. It has no
126-
dynamic kind, so R5's and R77's lazy-seam exemption becomes violations or misses. Its boundary
127-
findings carry no rule id or hint, its rule packs skip files no entry point reaches, and 3.x rejects
128-
the 73 `comment` fields in `.fallowrc.json`.
123+
- **fallow** is already a dependency, adds no measurable time (±0.3 s between runs), and has the
124+
best stale gate. With no dynamic kind, R5's and R77's lazy-seam exemption becomes violations or
125+
misses. Its boundary findings carry no rule id or hint, its rule packs skip files no entry point
126+
reaches, and 3.x rejects the 73 `comment` fields in `.fallowrc.json`.
129127

130128
## Revisit triggers
131129

@@ -136,6 +134,6 @@ the custom graph:
136134

137135
## Deletion
138136

139-
`scripts/layering/boundary-engine-spike.ts` is not a gate, and nothing keeps it compiling. The change
140-
that evaluates a revisit trigger re-runs it, updates the tables here, and deletes it together with
141-
any migrated rule's code from the deletion table.
137+
`scripts/layering/boundary-engine-spike.ts` is not a gate, and nothing keeps it compiling. The
138+
change that evaluates a revisit trigger re-runs it, updates the tables here, and deletes it together
139+
with any migrated rule's code from the deletion table.

0 commit comments

Comments
 (0)