docs(IMP-52): reconcile Phase Z family count drift -- F-2 option (c)
Audit follow-up F-2 (INTEGRATION-AUDIT-01 §10.2). Phase Z families surface showed 11 tracked / 11 contracted / 13 on disk. The 2 untracked WIP files (app_sw_package_vs_solution.html, pre_construction_model_info_stacked.html) are now declared in _WIP_FILES.md as uncontracted and out-of-scope for the runtime matcher; promote/remove is gated on #42. The 11/11 tracked + contracted baseline is unchanged. A new pytest enforces tracked families ↔ frame_contracts.yaml set-equality modulo the WIP allowlist parsed from _WIP_FILES.md, so future drift fails fast in CI before #42 expands to 32 frames. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -25,9 +25,9 @@ Phase R' implements SVG coordinate pre-compute as a renderer hook. References (d
|
||||
|
||||
Phase Z active partials surface:
|
||||
|
||||
- `templates/phase_z2/families/*.html` — **13** files.
|
||||
- `templates/phase_z2/families/*.html` — **11 contracted + 2 WIP untracked = 13 on disk** (contracted set = `templates/phase_z2/catalog/frame_contracts.yaml` top-level keys; WIP allowlist = [`templates/phase_z2/families/_WIP_FILES.md`](../../templates/phase_z2/families/_WIP_FILES.md), gated on Gitea #42 / #52 F-2 option (c)).
|
||||
- `templates/phase_z2/frames/*.html` — **2** files.
|
||||
- Total surface = **15 partials**.
|
||||
- Total surface = **13 active partials (11 contracted families + 2 frames) + 2 WIP untracked families** (15 on disk; runtime matcher consumes the contracted set only).
|
||||
|
||||
SVG usage scan (evidence): `rg "<svg|viewBox" templates/phase_z2/` → **0 matches** (exit 1).
|
||||
|
||||
@@ -48,7 +48,7 @@ Per `CLAUDE.md` Phase R' regression prevention rules and the Stage 1/2 exit repo
|
||||
|
||||
- `src/renderer.py` — read-only. No edit to `_preprocess_svg_data` body, `SVG_BLOCKS` set, or `render_multi_page` call site.
|
||||
- `src/svg_calculator.py` — read-only. No edit to the five helpers or their public signatures.
|
||||
- `templates/phase_z2/families/*.html` (13) + `templates/phase_z2/frames/*.html` (2) — no `<svg>` / `viewBox` insertion in IMP-18 scope. SVG-bearing partial onboarding is owned by IMP-04.
|
||||
- `templates/phase_z2/families/*.html` (11 contracted + 2 WIP untracked = 13 on disk; WIP set = [`_WIP_FILES.md`](../../templates/phase_z2/families/_WIP_FILES.md)) + `templates/phase_z2/frames/*.html` (2) — no `<svg>` / `viewBox` insertion in IMP-18 scope. SVG-bearing partial onboarding is owned by IMP-04. The 2 WIP family templates are gated on Gitea #42 (promote-or-remove) and remain outside the runtime matcher set per #52 F-2 option (c).
|
||||
- F12 `construction_goals_three_circle_intersection.html` HTML/CSS → SVG migration is **out of scope** (separate post-IMP-04 issue).
|
||||
- No hardcoded SVG coordinates in Phase Z templates — when IMP-18 re-activates, coordinates must be derived from `svg_calculator` helpers (or equivalent forward-port into `phase_z2_renderer`), not hand-copied.
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ Axis 3 (Section 5) will verify each pair has agreeing producer-line / consumer-l
|
||||
- 6 invariant categories evaluated. All AGREE for the closed-issue audit scope.
|
||||
- 2 surface notes recorded as Section 10 follow-up candidates :
|
||||
- **F-1** : issue body cites `src/phase_z2_mapper.py` for invariant C3 (`fit_classification`), but the live producer is `src/phase_z2_classifier.py`. Record-keeping correction needed in any future audit charter, not a code conflict. RESOLVED via IMP-53 (2026-05-19)
|
||||
- **F-2** : 2 untracked family templates exist on disk without `frame_contracts.yaml` entries; IMP-18 doc cites "families/*.html (13)" forward-looking. Tracked baseline (11 / 11) is consistent. Contract drift is *not* present for any closed issue; the WIP delta belongs to open work.
|
||||
- **F-2** : 2 untracked family templates exist on disk without `frame_contracts.yaml` entries; IMP-18 doc cites "families/*.html (13)" forward-looking. Tracked baseline (11 / 11) is consistent. Contract drift is *not* present for any closed issue; the WIP delta belongs to open work. RESOLVED via #52 option (c) (2026-05-19) -- WIP allowlist captured in `templates/phase_z2/families/_WIP_FILES.md`; tracked + contracted baseline unchanged at 11/11; promote / remove gated on #42.
|
||||
- 1 documented partial recorded :
|
||||
- Step 21 `_write_step_artifact` at `pipeline.py:4772` carries `step_status="partial"` with note `region marker partial 미주입 -- Step 21 ⚠ partial`. This is *self-honest acknowledged* per `feedback_artifact_status_naming`; no cross-issue conflict.
|
||||
- Phase R' <-> Phase Z boundary clean both directions for the 22 closed issues.
|
||||
@@ -455,7 +455,7 @@ Five candidates were produced by Axes 1-4. F-3 + F-2 + F-1 are blocking conditio
|
||||
- Live producer site : `src/phase_z2_classifier.py:495-497` (return dict with `visual_check_passed`, `classifications`, `summary`, `categories_seen`, `unclassified_signals`, `placement_diagnostics`).
|
||||
- **priority / gating** : low priority on its own; required for charter cleanliness; **not** a blocker for #19 Stage 2.
|
||||
|
||||
### 10.2 F-2 -- family template count reconciliation : 11 tracked / 11 contracted / 13 on disk
|
||||
### 10.2 F-2 -- family template count reconciliation : 11 tracked / 11 contracted / 13 on disk -- RESOLVED via #52 (option c, 2026-05-19)
|
||||
|
||||
- **title** : `[FAMILY-TEMPLATE-RECONCILE] templates/phase_z2/families/ has 13 .html files on disk but 11 tracked + 11 frame_contracts entries; 2 WIP files (app_sw_package_vs_solution.html, pre_construction_model_info_stacked.html) untracked`
|
||||
- **source_axis** : Axis 3 (invariant category C6 template / catalog / frame count) -- recorded in §5.2 C6 row + §5.4 follow-up bullet F-2 + §6.3 Axis 4 cross-axis consistency bullet.
|
||||
@@ -468,6 +468,12 @@ Five candidates were produced by Axes 1-4. F-3 + F-2 + F-1 are blocking conditio
|
||||
- REPORT §5.5 row "C6 family templates -- on disk" (`ls templates/phase_z2/families/*.html` = 13).
|
||||
- REPORT §6.3 Axis 4 cross-axis consistency bullet (matches IMP-04 evidence).
|
||||
- **priority / gating** : **must land before #19 introduces any new family template** (per §9.3 condition 2). Until #19's catalog touch surface is known, this can be filed independently.
|
||||
- **resolution** : option (c) -- 2 WIP family templates explicitly noted as in-progress and tracked outside `frame_contracts.yaml` (RESOLVED via Gitea #52, 2026-05-19) :
|
||||
- WIP allowlist : `templates/phase_z2/families/_WIP_FILES.md` (added by #52 u1) -- names both files with Figma frame IDs (`app_sw_package_vs_solution.html` -> frame 23 / `1171281203`; `pre_construction_model_info_stacked.html` -> frame 9 / `1171281180`) and explicit "not in `frame_contracts.yaml`, not in runtime matcher set" status; promote / remove gated on Gitea #42.
|
||||
- IMP-18 doc reconciled : `docs/architecture/IMP-18-SVG-GAP-REPORT.md` L28 + L30 + L51 corrected from disk-only "13 files" / "15 partials" wording to "11 contracted + 2 WIP untracked = 13 on disk" (#52 u2) -- runtime matcher consumes the contracted set only; doc / tracked / contracted surfaces agree at 11 active.
|
||||
- baseline guard (planned by #52 u4) : `tests/test_family_contract_baseline.py` will enforce tracked families <-> `frame_contracts.yaml` 1:1 set-equality modulo WIP allowlist parsed from `_WIP_FILES.md`; future drift (#42 or otherwise) fails CI.
|
||||
- tracked baseline (11 contracted families <-> 11 `frame_contracts.yaml` entries) unchanged; no contract entries added or removed; no runtime matcher mutation; **C6 invariant remains AGREE** for the closed-issue audit scope.
|
||||
- **F-2 closed-by-#52** under [[feedback_workflow_atomicity_rules]] (one commit = one decision unit), without re-opening any §5 C-invariant or §6.3 Axis 4 conclusion. #19 catalog-touch gate (per §9.3 condition 2) is now satisfied for the current 11/11 baseline; any #19 / #42 catalog growth must reconcile the WIP allowlist before merge.
|
||||
|
||||
### 10.3 F-3 -- backlog status sweep : 15 rows pending->implemented + 1 row pending->documented(deferred) + IMP-15 children footnote
|
||||
|
||||
|
||||
37
templates/phase_z2/families/_WIP_FILES.md
Normal file
37
templates/phase_z2/families/_WIP_FILES.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Phase Z Families — WIP Marker
|
||||
|
||||
**Status:** intentionally untracked, uncontracted, out-of-scope for runtime matcher.
|
||||
**Closes audit follow-up:** INTEGRATION-AUDIT-01-REPORT.md §10.2 F-2 (option c).
|
||||
**Gate for promote/remove:** Gitea issue #42 (`IMP-04b Catalog extension to 32 frames`).
|
||||
|
||||
## Baseline lock (2026-05-19)
|
||||
|
||||
| Surface | Count | Notes |
|
||||
|---|---|---|
|
||||
| `git ls-files templates/phase_z2/families/*.html` | 11 | tracked family templates |
|
||||
| `templates/phase_z2/catalog/frame_contracts.yaml` top-level keys | 11 | 1:1 with tracked basenames |
|
||||
| `Get-ChildItem templates/phase_z2/families/*.html` | 13 | tracked 11 + WIP 2 (below) |
|
||||
|
||||
Active contracted family count = **11**. Tracked basenames ↔ `frame_contracts.yaml` top-level keys are set-equal. Drift between disk (13) and contracted (11) is fully explained by the 2 WIP files below.
|
||||
|
||||
## WIP family templates (uncontracted)
|
||||
|
||||
| File | Figma frame | Status |
|
||||
|---|---|---|
|
||||
| `app_sw_package_vs_solution.html` | frame 23 (`1171281203`) | WIP — not in `frame_contracts.yaml`, not in runtime matcher set. |
|
||||
| `pre_construction_model_info_stacked.html` | frame 9 (`1171281180`) | WIP — not in `frame_contracts.yaml`, not in runtime matcher set. |
|
||||
|
||||
These files are partials authored during Phase Z-2 MVP-1.5b exploration. They are **not** part of the contracted Phase Z runtime catalog and must not be enumerated by frame selection, matcher, or any Stage 3 pipeline surface.
|
||||
|
||||
## Rules
|
||||
|
||||
- Adding a file to `templates/phase_z2/families/*.html` without a matching `frame_contracts.yaml` entry is **only** permitted if it is named here as WIP.
|
||||
- `tests/test_family_contract_baseline.py` enforces this invariant: tracked families ↔ `frame_contracts.yaml` keys must be set-equal, modulo the WIP allowlist parsed from this file.
|
||||
- Promoting a WIP file (add `frame_contracts.yaml` entry + register with matcher) or removing it must happen under issue #42 or a follow-up issue, not silently.
|
||||
|
||||
## References
|
||||
|
||||
- `docs/architecture/INTEGRATION-AUDIT-01-REPORT.md` §10.2 F-2 (audit finding closed by issue #52, option c)
|
||||
- `docs/architecture/IMP-18-SVG-GAP-REPORT.md` L28, L51 (count basis corrected to `11 contracted + 2 WIP`)
|
||||
- Gitea issue #52 (this reconciliation)
|
||||
- Gitea issue #42 (pre-flight gate for promote/remove)
|
||||
80
tests/test_family_contract_baseline.py
Normal file
80
tests/test_family_contract_baseline.py
Normal file
@@ -0,0 +1,80 @@
|
||||
"""Phase Z family-template ↔ frame_contracts.yaml baseline invariant.
|
||||
|
||||
#52 F-2 option (c) lock (2026-05-19) — locks active contracted family count
|
||||
at 11/11 with a WIP allowlist for the 2 untracked WIP family templates
|
||||
documented in `templates/phase_z2/families/_WIP_FILES.md`. Any drift after
|
||||
this point (new family file on disk without a contract entry, or a contract
|
||||
entry pointing to a missing file) fails fast in CI.
|
||||
|
||||
References:
|
||||
- docs/architecture/INTEGRATION-AUDIT-01-REPORT.md §10.2 F-2
|
||||
- docs/architecture/IMP-18-SVG-GAP-REPORT.md L28/L30/L51
|
||||
- templates/phase_z2/families/_WIP_FILES.md (WIP allowlist source)
|
||||
- Gitea #52 (this reconciliation), #42 (promote/remove gate)
|
||||
|
||||
Pattern mirrors `tests/test_catalog_invariant.py` — fail fast with explicit
|
||||
diff message if family ↔ contract surfaces drift.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
PROJECT_ROOT = Path(__file__).parent.parent
|
||||
FAMILIES_DIR = PROJECT_ROOT / "templates" / "phase_z2" / "families"
|
||||
CATALOG_PATH = PROJECT_ROOT / "templates" / "phase_z2" / "catalog" / "frame_contracts.yaml"
|
||||
WIP_DOC_PATH = FAMILIES_DIR / "_WIP_FILES.md"
|
||||
|
||||
|
||||
def _load_contract_keys() -> set[str]:
|
||||
with CATALOG_PATH.open(encoding="utf-8") as f:
|
||||
catalog = yaml.safe_load(f)
|
||||
return {k for k, v in catalog.items() if isinstance(v, dict)}
|
||||
|
||||
|
||||
def _load_disk_family_stems() -> set[str]:
|
||||
return {p.stem for p in FAMILIES_DIR.glob("*.html")}
|
||||
|
||||
|
||||
def _load_wip_allowlist() -> set[str]:
|
||||
text = WIP_DOC_PATH.read_text(encoding="utf-8")
|
||||
return {m.group(1) for m in re.finditer(r"`([A-Za-z0-9_\-]+)\.html`", text)}
|
||||
|
||||
|
||||
def test_contracts_set_equals_disk_families_minus_wip():
|
||||
"""`frame_contracts.yaml` keys ↔ disk family stems minus WIP allowlist."""
|
||||
contracts = _load_contract_keys()
|
||||
disk = _load_disk_family_stems()
|
||||
wip = _load_wip_allowlist()
|
||||
expected = disk - wip
|
||||
missing = expected - contracts
|
||||
extra = contracts - expected
|
||||
assert not missing, (
|
||||
f"Family files on disk without frame_contracts.yaml entry "
|
||||
f"(and not in _WIP_FILES.md): {sorted(missing)}. "
|
||||
"Add a contract entry, or list the file in "
|
||||
"templates/phase_z2/families/_WIP_FILES.md as WIP."
|
||||
)
|
||||
assert not extra, (
|
||||
f"frame_contracts.yaml has entries with no matching family file: "
|
||||
f"{sorted(extra)}."
|
||||
)
|
||||
|
||||
|
||||
def test_wip_allowlist_is_disk_only_and_uncontracted():
|
||||
"""WIP allowlist names must exist on disk AND have no contract entry."""
|
||||
contracts = _load_contract_keys()
|
||||
disk = _load_disk_family_stems()
|
||||
wip = _load_wip_allowlist()
|
||||
missing_on_disk = wip - disk
|
||||
leaked_into_contracts = wip & contracts
|
||||
assert not missing_on_disk, (
|
||||
f"_WIP_FILES.md names files not on disk: {sorted(missing_on_disk)}."
|
||||
)
|
||||
assert not leaked_into_contracts, (
|
||||
f"_WIP_FILES.md names files that already have a contract entry: "
|
||||
f"{sorted(leaked_into_contracts)}. Promote via #42 instead — "
|
||||
"WIP allowlist must be disk-only / uncontracted."
|
||||
)
|
||||
Reference in New Issue
Block a user