89 lines
4.2 KiB
Markdown
89 lines
4.2 KiB
Markdown
# Structural diff and gates
|
|
|
|
Brick compares two canonical building models by stable node and edge ID. The
|
|
comparison is directional: `baseline -> current`. JSON remains the product
|
|
boundary; the renderer and layout are not involved.
|
|
|
|
## Compatibility
|
|
|
|
A diff is computed only after both buildings pass schema validation. By default,
|
|
`Building::ensure_compatible_with` requires the following to match exactly:
|
|
|
|
- Brick building schema version (currently 4);
|
|
- absolute scan root;
|
|
- stable repository node identity;
|
|
- complete scan scope, including exclusions and selected P4 frontend.
|
|
|
|
Duplicate node or edge IDs in either input fail closed. Brick does not guess
|
|
that two different roots, scopes, or frontend selections describe equivalent
|
|
repositories.
|
|
|
|
For saved models from corresponding checkout paths, opt in with
|
|
`--allow-root-relocation` (requires `--baseline`). This is a caller assertion of
|
|
root equivalence, not automatic Git verification. Only root equality is relaxed;
|
|
all other validation and compatibility checks remain. Source trees need not
|
|
still exist. The library exposes `diff::between_with_policy` with
|
|
`ComparisonPolicy::ExplicitRootEquivalence`; `diff::between` remains strict.
|
|
|
|
## Versioned JSON contract
|
|
|
|
Structural diff JSON has its own `diff_schema_version`, currently `2`, for both strict and relocated comparisons. It
|
|
records the source `building_schema_version`, `baseline_root`, `current_root`,
|
|
`comparison_policy` (`strict` or `explicit-root-equivalence`), repository graph
|
|
identity, and scope. `root` is retained as an alias of `current_root`. Consumers
|
|
must handle version 2 explicitly; the building schema remains 4. It contains exact
|
|
counts plus six deterministically ID-sorted collections:
|
|
|
|
- `added_nodes` and `removed_nodes`, containing complete node records;
|
|
- `changed_nodes`, containing the stable ID and complete `before`/`after` nodes;
|
|
- `added_edges` and `removed_edges`, containing complete edge records;
|
|
- `changed_edges`, containing the stable ID and complete `before`/`after` edges.
|
|
|
|
A shared ID is changed when any serialized node or edge field differs. Edge
|
|
comparison therefore retains evidence lane, backend, producer-local confidence,
|
|
attributes, and provenance. Evidence changes are not promoted, averaged, or
|
|
collapsed into a generic changed flag. Top-level node/edge array order does not
|
|
affect the result.
|
|
|
|
Checkout directory names remain repository display names, so different basenames
|
|
produce a changed repository node even for identical source. Absolute imported
|
|
report provenance also remains visible in changed edges. No input records or
|
|
IDs are rewritten, and these changes count toward the existing gates.
|
|
|
|
Metrics and diagnostics are not duplicated into the structural diff. Existing
|
|
`brick check` metric gates continue to evaluate the current building directly.
|
|
|
|
## CLI integration
|
|
|
|
The existing command accepts an optional compatible baseline:
|
|
|
|
```text
|
|
brick check current.json --min-score 70 \
|
|
--baseline baseline.json \
|
|
--diff-json structural-diff.json \
|
|
--max-added-nodes 20 \
|
|
--max-removed-nodes 0 \
|
|
--max-changed-edges 5
|
|
```
|
|
|
|
Available limits are `--max-{added,removed,changed}-{nodes,edges}`. Each defaults
|
|
to unlimited, so adding `--baseline` alone is inspection-only and does not alter
|
|
existing score/cycle/unresolved gate behavior. Diff output and diff limits
|
|
without `--baseline` are rejected. When requested, complete diff JSON is written
|
|
before a threshold failure is reported, so the failed gate remains inspectable.
|
|
|
|
The gate reports all current metric and diff-count failures together. Limits
|
|
are absolute counts, not percentages or architectural quality claims.
|
|
|
|
## Measurement and limitations
|
|
|
|
`tests/fixtures/diff/` contains a positive stable-ID change and an identical
|
|
clean negative. `tests/benchmarks/diff_runner.py` rebuilds baseline and current
|
|
models at one canonical root and at separate roots, runs all four cases at least
|
|
three times, checks strict rejection for relocated cases, and retains
|
|
raw models, diffs, command logs, latency, RSS, Git provenance, and the resolved
|
|
plan under `build/benchmarks/structural_diff/`.
|
|
|
|
This slice does not infer renames: a changed stable identity is one removal and
|
|
one addition. Declarative architectural policy is a later consumer of these
|
|
facts and must not retroactively change diff identity semantics.
|