- categorize F13/F29/F16 frame_contracts fields as overlay-only, templates_v1-derived, or validation duplicate - lock duplicate hard-error, 1:1 keyspace, manual trigger, semantic-identical rollback - defer analysis.md direction inversion and 32-frame audit as separate axes
11 KiB
Phase Z Overlay Schema (Option E first migration)
Status: Step 0 schema lock — pre-execution spec. Scope: F13 / F29 / F16 only (3 active Phase Z frames). Last updated: 2026-05-07.
0. Why this doc exists
templates/phase_z2/catalog/frame_contracts.yaml is currently a hand-curated island. It must be hand-edited whenever a Phase Z frame's slot/sub_zone/payload structure changes, even though most of its sibling Phase Z artifacts (V4 matching results, structure_ontology.yaml templates_v1 section, figma_to_html_agent/blocks/{frame_id}/analysis.md) are auto-generated from upstream sources.
Option E first migration converts frame_contracts.yaml to a generated artifact:
runtime_overlay/{template_id}.yaml (per-template Phase Z fields, hand-edited)
+
templates_v1[frame_id] (cross-validation only in first migration)
↓
build_phase_z2_frame_contracts.py (generator)
↓
templates/phase_z2/catalog/frame_contracts.yaml (committed, generated)
↓
Phase Z runtime (unchanged — reads frame_contracts.yaml the same way)
This doc locks the schema/keyspace/trigger/rollback rules before any overlay file or generator code is written.
0-1. Field owner table
Each field in current frame_contracts.yaml is classified as one of:
- (a) templates_v1-derived — generator looks up from
tests/matching/structure_ontology.yamltemplates_v1section. Not declared in overlay. - (b) overlay-only — Phase Z 전용 operational config. Declared in
runtime_overlay/{template_id}.yaml. - (c) validation duplicate — generator cross-checks overlay value against
templates_v1. Disagreement → hard error.
F13 — three_parallel_requirements (frame_id 1171281190)
| Field | Current value | Category | Note |
|---|---|---|---|
template_id |
three_parallel_requirements |
(c) | overlay filename = template_id; cross-check templates_v1['1171281190'].template_id |
frame_id |
1171281190 |
(b) | overlay declares; generator confirms it exists as a templates_v1 key |
family |
three_parallel |
(b) | Phase Z categorization. Not the same as templates_v1.visual_pattern.family (= list) — different semantic axis. Name overlap is incidental. |
source_shape |
top_bullets |
(b) | Phase Z B1 extractor signal. Not in templates_v1. |
cardinality.strict |
3 |
(c) | cross-check against templates_v1.visual_pattern.cardinality — must equal min (and max if min==max). |
cardinality.overflow_policy |
abort_or_review |
(b) | Phase Z fallback policy. |
role_order |
[tech, people, nature] |
(b) | F13-specific visual role mapping. Not in templates_v1. |
visual_hints.min_height_px |
230 |
(b) | Phase Z layout calculation. Not in templates_v1. |
accepted_content_types |
[text_block] |
(b) | SPEC v1 §3 Layer A→B input. Not in templates_v1. |
sub_zones |
[pillar_1, pillar_2, pillar_3] (with partial_target_path) |
(b) | Phase Z Frame Slot declaration. Conceptually overlaps with templates_v1.slots (pillar_*_label/body) but different shape (sub_zones = column units; slots = label+body pairs). |
payload.* |
{title, builder, builder_options} |
(b) | Phase Z mapper directives. Not in templates_v1. |
F29 — process_product_two_way (frame_id 1171281210)
| Field | Current value | Category | Note |
|---|---|---|---|
template_id |
process_product_two_way |
(c) | filename = template_id; cross-check |
frame_id |
1171281210 |
(b) | |
family |
two_column_h3 |
(b) | templates_v1.visual_pattern.family = compare; intentionally different |
source_shape |
h3_subsections |
(b) | F29-specific B1 path |
cardinality.strict |
2 |
(c) | cross-check against templates_v1.visual_pattern.cardinality.{ideal:2, min:2, max:2} |
cardinality.overflow_policy |
abort_or_review |
(b) | |
visual_hints.min_height_px |
345 |
(b) | |
accepted_content_types |
[text_block, transform_table] |
(b) | F29 process column accepts AS-IS/TO-BE table |
sub_zones |
[process_column, product_column] (each cardinality.strict: 3) |
(b) | 2 column × 3 sections; sub_zone unit = column |
payload.* |
{title, builder=process_product_pair, builder_options} |
(b) |
F16 — bim_issues_quadrant_four (frame_id 1171281193)
| Field | Current value | Category | Note |
|---|---|---|---|
template_id |
bim_issues_quadrant_four |
(c) | |
frame_id |
1171281193 |
(b) | |
family |
bim_issues_quadrant |
(b) | templates_v1.visual_pattern.family = cards |
source_shape |
top_bullets |
(b) | |
cardinality.strict |
(not declared) | n/a | F16 intentionally omits — uses pad_to=4 + truncate>4 policy in payload.builder_options. templates_v1.cardinality has {ideal:4, min:4, max:4} but Phase Z does not enforce strict here. Generator must NOT auto-derive this from templates_v1. |
accepted_content_types |
[text_block] |
(b) | |
sub_zones |
[quadrant_1, quadrant_2, quadrant_3, quadrant_4] (each cardinality.strict: 1) |
(b) | sub_zone-level cardinality = capacity; not the same as frame-level |
payload.* |
{title, builder=quadrant_flat_slots, builder_options.{item_parser, pad_to, truncate_at, label_key_pattern, body_key_pattern, empty_label, empty_body}} |
(b) |
Summary
- (a) templates_v1-derived: none in first migration. Overlay declares all operational config.
- (b) overlay-only: ~all fields. Overlay file is essentially a per-template extract of the current
frame_contracts.yamlentry. - (c) validation duplicate: 2 fields per template —
template_id(overlay filename matchestemplates_v1[frame_id].template_id) andcardinality.strictwhen present (must matchtemplates_v1.visual_pattern.cardinality.min).
Implication: first migration is mostly a split-and-concatenate refactor with light cross-validation. Migration value is (i) per-template editing isolation, (ii) templates_v1 consistency check at build time. Heavier derivation (e.g., generating cardinality.strict from templates_v1 automatically) is 별 axis — defer until a concrete need surfaces.
0-2. Duplicate rule — hard error
If an overlay file declares a field that is classified as (a) templates_v1-derived, generator fails immediately with a clear message. No silent override semantics.
Currently no fields are in (a), so this rule is dormant for first migration. It exists to prevent regression: a future overlay author cannot quietly duplicate a templates_v1-owned field without removing the (a) classification first.
For (c) validation duplicate fields, the rule is:
- Overlay declares the value.
- Generator looks up the corresponding value in
templates_v1. - If the two disagree, hard error with both values printed and a pointer to this doc.
This preserves the lock-layer "overlay = single source of truth for declared values" while making templates_v1 drift visible.
0-3. Keyspace rule
- Source ref =
frame_id(Figma origin). - Overlay identity =
template_id(filename:runtime_overlay/{template_id}.yaml). - First-migration assumption:
frame_id↔template_idis 1:1 for all active Phase Z frames (F13/F29/F16). - Generator verification: for each overlay, the generator looks up
templates_v1[overlay.frame_id].template_idand asserts it equals the overlay filename's{template_id}. Mismatch → hard error. - Multi-variant (one frame ↔ multiple templates) = 별 axis. When that case appears, this doc is updated and the keyspace becomes a composite key. Until then, 1:1 is locked.
0-4. Trigger
- Manual run for first migration:
python scripts/build_phase_z2_frame_contracts.py - Verification (semantic-identical) is required after each run. Generator should refuse to write output if the result differs semantically from current
frame_contracts.yamlduring first migration. - CI / pre-commit automation = 별 axis. Will be considered after first migration ships and a drift incident actually occurs. Adding it now is over-engineering for 3 templates.
0-5. Rollback / failure path
If semantic-identical verification fails (yaml.safe_load(current) != yaml.safe_load(generated)):
- Generated artifact is NOT adopted. No write to
frame_contracts.yaml. - Current
frame_contracts.yamlremains canonical (status quo preserved). - Diff source: overlay schema, generator implementation, or
templates_v1cross-check rule. Identify root cause. - Fix overlay/schema/generator. Retry from Step 1.
- Migration is incomplete: 3 active templates partially migrated count as failure — either all 3 ship or none.
The migration commit (Step 5) is only made after all 3 templates pass both (a) semantic-identical and (b) Phase Z runtime regression (final.html identical).
0-6. Scope boundary
Option E first migration solves:
frame_contracts.yamlisland problem (hand-curated → generated).- Per-template editing isolation.
- Light
templates_v1consistency check at build time.
Option E first migration does NOT solve (= 별 axis, NOT addressed by this migration):
analysis.md↔structure_ontology.yamlownership direction conflict. Specifically:figma_to_html_agent/CLAUDE.mdimplies forward direction (agent ownsanalysis.md), whiletests/matching/sync_analysis_from_ontology.pyenforces reverse direction (structure_ontology.yamlis source,analysis.mdis mirror). This is a separate architectural decision and is out of scope here.templates/blocks/structureslegacy retirement.legacy templates/catalog/blocks.yamlremoval.zone_extractrule formalization (long-term zone_application policy).- 32-frame full audit (only active F13/F29/F16 in first migration).
When the user resumes work on the direction inversion axis, this doc is not invalidated — overlay schema and generator continue to work because they read templates_v1 (not analysis.md).
Migration steps (post-Step 0)
- Create
templates/phase_z2/catalog/runtime_overlay/{F13,F29,F16}.yamlfiles. Each contains the (b) overlay-only fields + (c) validation duplicate values for one template. - Write
scripts/build_phase_z2_frame_contracts.pygenerator. Reads overlays +templates_v1, applies 0-2 / 0-3 rules, writes generatedframe_contracts.yaml. - Run generator. Verify
yaml.safe_load(current) == yaml.safe_load(generated). List order preserved (sub_zones especially). On disagreement → 0-5 rollback. - Run Phase Z pipeline once (regression MDX) — confirm
final.htmlis byte-identical to current run. - Commit Option E migration (overlay files + generator + new generated
frame_contracts.yamlwith header). Separate commit from Step 0 schema doc.
Out of scope for this doc
- Generator implementation details (parsing, emit format, error message templates) — captured in the script itself.
- Overlay file format details beyond field categorization — captured in the overlay files themselves.
- Direction inversion fixes — see future axis docs.