Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,31 @@ End-of-work mutating cleanup at `scripts/hygiene/`. Never CI-invoked. See [`scri
## Legacy Packages

`legacy/` holds archived packages preserved for reference only — never installed, built, or published. `packages/*` and `e2e/*` MUST NOT import from `legacy/*` (see § One-Way Dependency Rule). For the catalog of legacy packages, workspace exclusion mechanics, and archived openspec path references, see [`legacy/AGENTS.md`](legacy/AGENTS.md).

### RepoWise File Loop

- Treat one file as the unit of work. Collect its dead-code, health, risk,
ownership, caller, and decision leads together before planning an edit.
- Verify every lead against live source, callers, exports, configuration,
dynamic loading, and tests. Analyzer findings are evidence, not authority.
- Form one cohesive in-place plan for the file: accepted fixes, rejected false
positives, protected behavior, and the mapped verification route.
- Implement compatible line-level and structural fixes in one pass. Split only
at a real behavior, ownership, or rollback boundary.
- Use focused tests while editing, then run the change-map verification once at
the file-bundle boundary. Request one review only at a material risk boundary.
- Close the file by rechecking the live diff and relevant RepoWise signals.
Record one terse row in the ignored `.repowise/cockpit.md`; do not create an
increment packet, journal entry, or proposal for ordinary remediation.
- Escalate to OpenSpec only for a durable public contract, architectural or
dependency decision, migration, or genuinely cross-cutting behavior change.

<!-- REPOWISE_DISTILL:START — Do not edit below this line. Auto-generated by Repowise. -->

### Output Distillation

- Prefer `repowise distill <cmd>` for noisy commands — test runs, builds, `git status`/`log`/`diff`, searches, file listings. It runs the command unchanged (exit code preserved) and prints a compact, errors-first rendering; every error line survives.
- Output may contain a marker like `[repowise#a1b2c3d4e5f6: 230 lines omitted (~6.1k tokens); restore: repowise expand a1b2c3d4e5f6]`. The omitted content is fully preserved — run `repowise expand <ref>` to retrieve it, or `repowise expand <ref> -q <regex>` for just the matching lines.
- Never re-run a command to see omitted output; expand the marker instead.
- For structure-level questions about a large indexed file ("what's in here", "which function handles X"), `get_context(["path"], include=["skeleton"])` returns the file with bodies elided — every signature plus the bodies of the most central symbols — at a fraction of the cost of a full Read.
<!-- REPOWISE_DISTILL:END -->
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
schema: ooda
created: 2026-07-19
goal: Reject conflicting overlapping system prop definitions without changing
valid overlap behavior
120 changes: 120 additions & 0 deletions openspec/changes/enforce-system-prop-overlap-equality/brainstorm.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<!--
brainstorm.md — the STRATEGIC exploration record for the whole change.

HISTORICAL RECORD: immutable once design.md exists; design.md supersedes this
file on any conflict. An append-only exploration log is valuable; a second
live copy of the decision state is not — never sync this file after design.md
lands.
-->

## Exploration evidence

Captured from the 2026-07-19 queue audit after invoking
`superpowers:brainstorming`, `receiving-code-review`,
`systematic-debugging`, and RepoWise risk/context/why/health queries.

- RepoWise resolves the analyzer's item-9 path to
`packages/system/__tests__/types.test-d.tsx`. The file is a 98th-percentile
hotspot and a sole-owner type-contract suite, but its header and inline
rationale already describe it as the living public type contract. A
speculative split would add churn without correcting a demonstrated fault.
- `packages/system/src/SystemBuilder.ts` is a 95th-percentile, fix-heavy
hotspot with four current dependents. RepoWise's broad “no governing
decision” label is incomplete: archived OpenSpec change
`flatten-system-builder` defines overlap tolerance, and the current
`system-builder` and `system-serialization` specs govern its public shape.
- `createSystem({ includes })` is intentionally a static package-discovery
marker. Git archaeology shows the API began as a no-op `.includes(...)`
method, then moved to the constructor argument. The extraction tests parse
that syntax to discover imported packages. Included registries must not be
merged into `toConfig()` as part of this work.
- The overlap contract says a prop may appear in multiple groups only when its
definition is identical. Both `addGroup()` and `addProps()` duplicate a
shallow comparison that checks `property`, `scale`, `transform`, and
`negative`, but ignores behavior-bearing fields such as `properties`,
`currentVar`, `variable`, and `strict`.
- A live reproduction registers `x` first with
`properties: ['marginLeft', 'marginRight']` and `currentVar: '--first'`, then
with `properties: ['marginTop', 'marginBottom']` and
`currentVar: '--second'`. The builder throws no error and `toConfig()`
silently contains the second definition, changing the meaning of `x` for
the first group.

## Known now

- The defect is at registration time: an incomplete and duplicated equality
policy lets a conflicting definition overwrite the existing registry entry.
- The smallest durable seam is one private definition-equality helper used by
both registration methods.
- `property`, `negative`, `strict`, `variable`, and `currentVar` are primitive
comparisons; `transform` and non-primitive `scale` values retain their
existing identity semantics; ordered `properties` arrays compare by value.
- Public method signatures and serialized field names remain unchanged.
- A dedicated runtime test file is the correct oracle. The existing type suite
proves compile-time inference and should not absorb runtime-only conflict
assertions.

## Deferred variables

- **Structural equality for inline object/array scales** is deferred. Resolving
signal: a concrete consumer case or approved contract requiring separately
allocated but structurally equal scales to overlap. Until then, preserve the
current reference-equality behavior.
- **Splitting the 1,162-line type-contract suite** is deferred. Resolving
signal: an evidence-backed decomposition change with named ownership seams,
or measured compile/merge cost showing that a specific section should move.
- **Canonical consolidation of the historically stale `system-builder` spec**
is deferred beyond the single overlap requirement needed here. Resolving
signal: a dedicated spec-consolidation change that reconciles the old
concentric-builder language with the shipped flat builder without coupling
that documentation migration to this bug fix.

## Candidate north star

- Overlapping registrations are accepted if and only if every field that can
affect authoring, runtime resolution, or serialization has the same meaning.
- A conflicting overlap fails before a new builder instance can expose a
silently replaced definition.
- The equality policy has one implementation and one focused runtime contract,
so adding a future `Prop` field has an obvious review seam.
- Provisional: preserve identity equality for `scale` and `transform`; revisit
only when the deferred consumer/contract signal above exists.

## Candidate guardrails

- **G1 — no include-composition change.** The change SHALL NOT read or merge
`#includesRegistry` in `build()`/`toConfig()`. Executable check: scoped diff
inspection plus the existing package-discovery unit suite.
- **G2 — valid overlap remains valid.** The change SHALL NOT reject the same
shared prop object used by multiple groups. Executable check: a positive
runtime test using one shared definition and the existing type assertion.
- **G3 — conflicts fail in both entry points.** The change SHALL NOT protect
`addGroup()` while leaving `addProps()` able to overwrite. Executable check:
one negative runtime test per method.
- **G4 — identity semantics stay stable.** The change SHALL NOT deep-compare
transforms or object scales. Executable check: helper implementation review
and a regression assertion that distinct object-scale instances remain a
conflict.
- **G5 — no unrelated queue work moves.** The change SHALL NOT modify the
protected Rust, integration, canary, or Next-plugin increments already in the
dirty tree. Executable check: scoped `git diff --check` and path-specific diff
review before/after implementation.
- **G6 — repository verification map is authoritative.** Executable check:
`vp run verify:compile`, `vp run verify:types`, and
`vp run verify:unit:ts`, distilled through RepoWise.

## Decision chain

1. The queue labels were treated as leads and checked against live paths,
callers, tests, history, and OpenSpec records.
2. The type-suite split was rejected because no behavioral defect or precise
decomposition boundary was demonstrated.
3. Include-registry composition was rejected after git archaeology proved the
value is a static discovery marker, not a serialized dependency graph.
4. The overlap reproduction isolated the root cause to incomplete duplicated
equality checks, not serialization or extraction.
5. Inline duplicated comparisons were rejected because they preserve the drift
mechanism. Generic deep equality was rejected because it changes scale
semantics without a requirement.
6. One private helper plus regression-first runtime tests is therefore the
smallest independently revertible increment.
164 changes: 164 additions & 0 deletions openspec/changes/enforce-system-prop-overlap-equality/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
## Context

`SystemBuilder.addGroup()` and `SystemBuilder.addProps()` allow a prop key to
overlap an existing registration when the definitions match. The current
policy is duplicated and compares only four fields. A conflict in
`properties`, `variable`, `strict`, or `currentVar` therefore passes and the
later object spread silently replaces the original definition. The builder is
a high-risk public boundary; changes must preserve its type inference,
serialization shape, static `includes` discovery marker, and all unrelated
dirty-tree increments.

## Goals / Non-Goals

**Goals:**

- Reject overlapping prop definitions that differ in any `Prop` field.
- Keep valid identical overlaps working.
- Give both registration entry points one private equality policy.
- Add a focused runtime oracle without expanding the type-contract monolith.

**Non-Goals:**

- Deep-equality semantics for object/array scales or transform functions.
- Registry composition for `createSystem({ includes })`.
- Splitting `packages/system/__tests__/types.test-d.tsx`.
- Broad canonical-spec consolidation or public API changes.

## Decisions

### D1: Centralize prop-definition equality

- **Choice**: Add one private equality helper and call it from both
`addGroup()` and `addProps()`.
- **Rationale**: The duplicated condition is the drift mechanism. One helper
makes the complete policy reviewable and gives future `Prop` fields a single
update seam.
- **Alternatives considered**: Adding comparisons inline twice preserves the
defect-prone duplication. Comparing serialized output misses type/runtime
fields that are not serialized.

### D2: Compare arrays structurally and preserve identity-sensitive fields

- **Choice**: Compare ordered `properties` entries by value; compare primitive
fields directly; retain reference equality for non-primitive `scale` values
and `transform` functions.
- **Rationale**: Separately allocated `properties` arrays with the same ordered
targets express the same multi-property definition. Current scale and
transform identity behavior is already shipped and has no requirement to
broaden it.
- **Alternatives considered**: Generic deep equality changes inline-scale
overlap semantics and introduces arbitrary object comparison into a narrow
builder invariant.

### D3: Add a dedicated runtime contract

- **Choice**: Create `packages/system/__tests__/system-builder.test.ts` for
positive overlap and conflict behavior across all `Prop` fields and both
registration methods.
- **Rationale**: The existing 1,162-line type suite proves compile-time shape,
not runtime throws. A focused test is a clearer ownership seam and begins to
reduce the hotspot's uncovered behavioral surface.
- **Alternatives considered**: Appending runtime-like expressions to the type
suite would not execute them and would worsen its churn concentration.

### D4: Preserve `includes` as a static discovery marker

- **Choice**: Do not consume `#includesRegistry` during build or serialization.
- **Rationale**: Git history and package-discovery tests prove the value exists
to keep imported external systems visible to source analysis. It is not a
registry-composition contract.
- **Alternatives considered**: Merging included configs would change local
system semantics, transform resolution, and collision policy without a
requirement.

## North Star

**Adversarial cadence K**: 1

- **NS1**: A valid overlap has one meaning across every group that names it.
- **NS2**: A conflicting overlap fails before a later registration can become
observable.
- **NS3**: Equality policy has one implementation and one focused executable
contract.
- **NS4**: Identity-sensitive scale/transform behavior remains stable —
provisional — revisit when `external:inline-scale-overlap-contract` exists.

## Decision Ledger

| ID | Decision | Status | Owner increment | Resolving signal | Review-by |
| --- | --- | --- | --- | --- | --- |
| DEF-1 | Structural equality for inline object/array scales | deferred | external:inline-scale-overlap-contract | external:inline-scale-overlap-contract | 3 reorientations \| 2026-08-19 |
| DEF-2 | Decompose the type-contract monolith | deferred | external:system-type-suite-decomposition-evidence | external:system-type-suite-decomposition-evidence | 3 reorientations \| 2026-08-19 |
| DEF-3 | Consolidate the stale canonical builder specification | deferred | external:system-builder-spec-consolidation-scope | external:system-builder-spec-consolidation-scope | 3 reorientations \| 2026-08-19 |

## Guardrail Register

| ID | Invariant | Scope | On trip | Status |
| --- | --- | --- | --- | --- |
| G1 | The change SHALL NOT alter the `includesRegistry`/constructor-marker path; blind spot: renamed identifiers would evade this text check | inc:01 | STOP | active (calibrated 2026-07-19: empty) |
| G2 | The change SHALL NOT reject equal ordered `properties` arrays or a shared valid definition | inc:01 | STOP | active (inc 01: 2 passed, 13 skipped) |
| G3 | The change SHALL NOT leave either `addGroup()` or `addProps()` able to silently overwrite a conflicting definition | inc:01 | STOP | active (inc 01: 15 passed) |
| G4 | The change SHALL NOT broaden object-scale equality beyond reference identity | inc:01 | STOP | active (inc 01: object/array identity contract passed) |
| G5 | The change SHALL NOT modify the pre-existing Rust, integration, canary, or Next-plugin increments | all | STOP | active (calibrated 2026-07-19: `4d42711d632a83258751c6373f32e3b1148a6dbf7bc2d2b949ff655e2c2db0ad`) |
| G6 | The change SHALL NOT regress system compilation, type contracts, or TypeScript units | change-end | STOP | active (inc 01: 9 workspaces, types, 26 files / 266 tests passed) |

Checks — verbatim commands:

**G1** — expected: empty output

```bash
git diff -- packages/system/src/SystemBuilder.ts | rg '^[+][^+].*(includesRegistry|config[?][.]includes)|^[-][^-].*(includesRegistry|config[?][.]includes)' || true
```

**G2** — expected: targeted positive overlap test passes

```bash
repowise distill bunx vp test run packages/system/__tests__/system-builder.test.ts -t 'accepts equivalent ordered property targets'
```

**G3** — expected: all focused builder tests pass

```bash
repowise distill bunx vp test run packages/system/__tests__/system-builder.test.ts
```

**G4** — expected: identity-semantics test passes

```bash
repowise distill bunx vp test run packages/system/__tests__/system-builder.test.ts -t 'preserves object-scale identity semantics'
```

**G5** — expected:
`4d42711d632a83258751c6373f32e3b1148a6dbf7bc2d2b949ff655e2c2db0ad -`

```bash
git diff -- 'AGENTS.md' 'openspec/specs/pipeline-integration-testing/spec.md' 'packages/_integration/CLAUDE.md' 'packages/_integration/__tests__/cascade-round-trip.test.ts' 'packages/_integration/__tests__/extraction.test.ts' 'packages/_integration/__tests__/run-pipeline.ts' 'packages/_integration/__tests__/selector-rules.test.ts' 'packages/_integration/fixtures/components/selector-rules/selector-rules-create-element.tsx' 'packages/_integration/fixtures/components/selector-rules/selector-rules-unresolvable-token.tsx' 'packages/_integration/fixtures/components/transforms.tsx' 'packages/extract/crates/extract-v2/src/analyze_css.rs' 'packages/extract/crates/extract-v2/src/cross_file.rs' 'packages/extract/crates/extract-v2/src/pipeline.rs' 'packages/extract/tests/canary.test.ts' 'packages/next-plugin/README.md' 'packages/next-plugin/src/with-animus.ts' | shasum -a 256
```

**G6** — expected: every command exits 0

```bash
repowise distill vp run verify:compile
repowise distill vp run verify:types
repowise distill vp run verify:unit:ts
```

## Risks / Trade-offs

- [Risk] A helper omits another `Prop` field -> Mitigation: table-driven tests
enumerate every current field and review compares the helper against the
interface.
- [Risk] The new tests accidentally assert implementation details ->
Mitigation: exercise public `createSystem()` chains and observable throws or
`toConfig()` output only.
- [Trade-off] Separately allocated object scales remain conflicts -> acceptable
because it preserves the shipped comparison contract until a real consumer
signal licenses a broader decision.

## Migration Plan

N/A — no deployment change. The source and regression test land together;
rollback is the independently revertible source/test increment. Acceptance
requires the focused test, mapped system verification, strict OpenSpec
validation, and independent review to pass.
Loading
Loading