HEAD: 97b7833 (main, working tree clean for tracked files relevant to #96 scope)
Selenium scan locus correction (issue body says src/phase_z2_pipeline.py:2734-2761, actual is 3864):
src/phase_z2_pipeline.py:3860-3887 — B5 v0 frame_slot_metrics builder. The actual scan = slide.querySelectorAll('[data-frame-slot-id]').forEach((cell) => { ... }) at L3864. This is a generic attribute-existence selector — partial-agnostic. Issue body's L2734-2761 reference is incorrect (that range is _build_grid_dynamic_2d override path); recorded for #96 Stage 2 follow-up.
templates/phase_z2/families/process_product_two_way.html (F29, frame_id 1171281210) — 6 inline carriers on .f29b__cell--left × 3 (data-frame-slot-id="process_column") and .f29b__cell--right × 3 (data-frame-slot-id="product_column"). Marker values match frame_contracts.yaml:111-119 sub_zones process_column / product_column.
templates/phase_z2/families/pre_construction_model_info_stacked.html (F9, frame_id 1171281180) — 1 inline carrier at L180 on .f9b__pill with data-frame-slot-id="pill_dynamic". NB: marker value pill_dynamic does NOT match frame_contracts.yaml:852-857 sub_zone.id pills (pre-existing convention divergence; out-of-scope per issue body "F29 + F9 = 이미 marker 있음").
src/region_marker_stamper.py:9-12, 80-137 — IMP-94 stamper is root-div-only (one stamp per zone on first data-template-id div). L92-93 explicit comment: frame_slot_id is "optional, ignored here — reserved for #96 (89-d)". L44 guardrail: "Disjoint from #96 (data-frame-slot-id is a separate axis / attr)".
.orchestrator/issues/94_stage_problem-review_exit.md:31 + 94_stage_simulation-plan_exit.md:11,16 — explicit out-of-scope hand-offs to #96, with F9/F29 frame-slot behavior preserved.
tests/test_phase_z2_imp94_marker_parity.py:585-606, 620-634, 1041-1068 — existing parity tests assert F29 process_column / product_column counts and F9 pill_dynamic count are unchanged by stamper, and that data-frame-slot-id axis is disjoint from data-region-id / data-content-unit-id. Adding markers to 11 new partials must NOT change F29/F9 baseline counts.
File has sub_zones with id: and partial_target_path: pairs across all 13 partials (32-frame catalog — 13 partial-backed + 19 contract-only). Per-cell CSS selectors (e.g., .f13b__cols > .f13b__col:nth-child(1), .f16b__quadrant--tl, .f29b__grid .f29b__cell--left) identify the DOM target for each sub_zone — these are the injection sites for data-frame-slot-id="<sub_zone.id>". (Selector-by-selector enumeration deferred to Stage 2.)
=== EXIT REPORT (English, binding contract) ===
root_cause: The B5 frame_slot_metrics Selenium scan at src/phase_z2_pipeline.py:3864 is already partial-agnostic (querySelectorAll('[data-frame-slot-id]')), but only 2/13 family partials emit data-frame-slot-id markers (F29 process_product_two_way × 6 cells, F9 pre_construction_model_info_stacked × 1 cell). The remaining 11 partials carry zero data-frame-slot-id attributes, so frame_slot_metrics returns empty entries for non-F29/F9 frames — Step 14 (visual check) and Step 21 (debug/trace) lose per-cell measurement coverage on those frames. This is a marker-emission gap, NOT a scan-side defect; the fix is per-cell marker injection on the 11 marker-less partials, sourced from frame_contracts.yaml sub_zones[i].id and targeted at partial_target_path selector locations.
key_files:
templates/phase_z2/families/*.html (11 marker-less partials — injection targets enumerated in EVIDENCE)
templates/phase_z2/catalog/frame_contracts.yaml (sub_zones[i].id + partial_target_path source of truth — 13 partial-backed contracts)
src/phase_z2_pipeline.py:3860-3887 (B5 v0 frame_slot_metrics consumer — generic scan, no change)
scope_lock: Inject data-frame-slot-id="<sub_zones[i].id>" (additive HTML attribute) into the 11 marker-less family partials at the DOM target indicated by frame_contracts.yamlpartial_target_path for each sub_zone. F29 + F9 are not modified. No change to Selenium scan, no change to frame_contracts.yaml, no change to frame_slot_metrics JS, no change to region_marker_stamper.py. Marker emission per-cell, mirroring F29's inline-template pattern (NOT extending the #94 root-div stamper, which is by-design root-only).
out_of_scope:
F29 / F9 existing marker values (process_column, product_column, pill_dynamic) — preserved as-is per issue body and .orchestrator/issues/94_stage_simulation-plan_exit.md:16.
Axis disjoint — data-frame-slot-id MUST NOT collide with data-region-id / data-content-unit-id (per region_marker_stamper.py:44 and parity test L1041-1068).
final.html byte-equivalent except for new data-frame-slot-id attrs (per issue body validation criterion).
AI=0 (PZ-1) — deterministic marker injection, no LLM call.
MDX 무손실 (no content mutation through marker injection).
Per-cell target identified by partial_target_path CSS selector — when a sub_zone's selector matches N elements (e.g., F29's .f29b__grid .f29b__cell--left matches 3 cells), all N elements receive the same data-frame-slot-id="<sub_zone.id>" (mirroring F29 precedent).
unresolved_questions:
Q1 (injection mechanism): inline-template edit per partial (mirror F29 pattern, deterministic, 1-commit-per-partial) vs. selector-aware post-render stamper extension (single point of plumbing, more abstract)? Lock in Stage 2. Stage 1 evidence favors template edits because (a) F29 already does this, (b) region_marker_stamper.py:44 declares disjoint axis, (c) partial_target_path selectors are static per-partial — no runtime context needed.
Q2 (sub_zones with multiple-element selectors): when partial_target_path matches N>1 elements, place marker on each (F29 precedent) or wrap in container? Stage 2 must enumerate per-partial.
Q3 (validation strategy): per-partial parity test (mirror tests/test_phase_z2_imp94_marker_parity.py shape) vs. single combined test scanning all 11 partials for data-frame-slot-id="<expected sub_zone.id>" after render? Lock in Stage 2.
Q4 (MDX 01-05 coverage): which fresh-validation MDX → partial routing produces non-zero frame_slot_metrics count for each of the 11 newly-marked partials? Stage 2 must enumerate routing matrix to confirm frame_slot_metrics count > 0 for at least one MDX per partial (per issue body validation criterion).
evidence: see EVIDENCE block above. Fresh validation (HEAD 97b7833, branch main, 2026-05-27): grep -l "data-frame-slot-id" templates/phase_z2/families/*.html returns 2 files; inventory of 13 .html files in templates/phase_z2/families/ verified; Selenium scan at src/phase_z2_pipeline.py:3864 is generic; #94 axis disjoint per src/region_marker_stamper.py:44, 92-93 and tests/test_phase_z2_imp94_marker_parity.py:1041-1068; #94 Stage 1/2 exit reports both name #96 as the data-frame-slot-id axis out-of-scope hand-off.
commit_sha: HEAD = 97b7833 (main; no commit for #96 yet).
[Claude #1] Stage 1 problem-review — IMP-89-d B5 frame_slot_metrics marker expansion (#96)
=== EVIDENCE ===
**HEAD**: `97b7833` (main, working tree clean for tracked files relevant to #96 scope)
**Selenium scan locus correction (issue body says `src/phase_z2_pipeline.py:2734-2761`, actual is 3864)**:
- `src/phase_z2_pipeline.py:3860-3887` — B5 v0 `frame_slot_metrics` builder. The actual scan = `slide.querySelectorAll('[data-frame-slot-id]').forEach((cell) => { ... })` at L3864. This is a generic attribute-existence selector — partial-agnostic. Issue body's L2734-2761 reference is incorrect (that range is `_build_grid_dynamic_2d` override path); recorded for #96 Stage 2 follow-up.
**Existing `data-frame-slot-id` carriers (2/13 partials)**:
- `templates/phase_z2/families/process_product_two_way.html` (F29, frame_id `1171281210`) — 6 inline carriers on `.f29b__cell--left` × 3 (`data-frame-slot-id="process_column"`) and `.f29b__cell--right` × 3 (`data-frame-slot-id="product_column"`). Marker values match `frame_contracts.yaml:111-119` sub_zones `process_column` / `product_column`.
- `templates/phase_z2/families/pre_construction_model_info_stacked.html` (F9, frame_id `1171281180`) — 1 inline carrier at L180 on `.f9b__pill` with `data-frame-slot-id="pill_dynamic"`. NB: marker value `pill_dynamic` does NOT match `frame_contracts.yaml:852-857` sub_zone.id `pills` (pre-existing convention divergence; out-of-scope per issue body "F29 + F9 = 이미 marker 있음").
**Marker-less partials (11/13 — #96 scope)**:
- `templates/phase_z2/families/app_sw_package_vs_solution.html`
- `templates/phase_z2/families/bim_current_problems_paired.html`
- `templates/phase_z2/families/bim_dx_comparison_table.html`
- `templates/phase_z2/families/bim_issues_quadrant_four.html`
- `templates/phase_z2/families/construction_bim_three_usage.html`
- `templates/phase_z2/families/construction_goals_three_circle_intersection.html`
- `templates/phase_z2/families/dx_sw_necessity_three_perspectives.html`
- `templates/phase_z2/families/info_management_what_how_when.html`
- `templates/phase_z2/families/sw_reality_three_emphasis.html`
- `templates/phase_z2/families/three_parallel_requirements.html`
- `templates/phase_z2/families/three_persona_benefits.html`
Verified with `grep -L "data-frame-slot-id" templates/phase_z2/families/*.html` (13 total .html files; 2 carriers; 11 absentees).
**#94 paired axis confirmation**:
- `src/region_marker_stamper.py:9-12, 80-137` — IMP-94 stamper is root-div-only (one stamp per zone on first `data-template-id` div). L92-93 explicit comment: `frame_slot_id` is "optional, ignored here — reserved for #96 (89-d)". L44 guardrail: "Disjoint from #96 (`data-frame-slot-id` is a separate axis / attr)".
- `.orchestrator/issues/94_stage_problem-review_exit.md:31` + `94_stage_simulation-plan_exit.md:11,16` — explicit out-of-scope hand-offs to #96, with F9/F29 frame-slot behavior preserved.
- `tests/test_phase_z2_imp94_marker_parity.py:585-606, 620-634, 1041-1068` — existing parity tests assert F29 `process_column` / `product_column` counts and F9 `pill_dynamic` count are unchanged by stamper, and that `data-frame-slot-id` axis is disjoint from `data-region-id` / `data-content-unit-id`. Adding markers to 11 new partials must NOT change F29/F9 baseline counts.
**`frame_contracts.yaml` sub_zones[i].id ↔ partial_target_path mapping**:
- File has sub_zones with `id:` and `partial_target_path:` pairs across all 13 partials (32-frame catalog — 13 partial-backed + 19 contract-only). Per-cell CSS selectors (e.g., `.f13b__cols > .f13b__col:nth-child(1)`, `.f16b__quadrant--tl`, `.f29b__grid .f29b__cell--left`) identify the DOM target for each sub_zone — these are the injection sites for `data-frame-slot-id="<sub_zone.id>"`. (Selector-by-selector enumeration deferred to Stage 2.)
=== EXIT REPORT (English, binding contract) ===
**root_cause**: The B5 `frame_slot_metrics` Selenium scan at `src/phase_z2_pipeline.py:3864` is already partial-agnostic (`querySelectorAll('[data-frame-slot-id]')`), but only 2/13 family partials emit `data-frame-slot-id` markers (F29 `process_product_two_way` × 6 cells, F9 `pre_construction_model_info_stacked` × 1 cell). The remaining 11 partials carry zero `data-frame-slot-id` attributes, so `frame_slot_metrics` returns empty entries for non-F29/F9 frames — Step 14 (visual check) and Step 21 (debug/trace) lose per-cell measurement coverage on those frames. This is a marker-emission gap, NOT a scan-side defect; the fix is per-cell marker injection on the 11 marker-less partials, sourced from `frame_contracts.yaml` sub_zones[i].id and targeted at `partial_target_path` selector locations.
**key_files**:
- `templates/phase_z2/families/*.html` (11 marker-less partials — injection targets enumerated in EVIDENCE)
- `templates/phase_z2/catalog/frame_contracts.yaml` (sub_zones[i].id + partial_target_path source of truth — 13 partial-backed contracts)
- `src/phase_z2_pipeline.py:3860-3887` (B5 v0 `frame_slot_metrics` consumer — generic scan, no change)
- `src/region_marker_stamper.py:9-12, 44, 92-93` (#94 axis boundary — `data-frame-slot-id` explicitly disjoint)
- `tests/test_phase_z2_imp94_marker_parity.py:585-606, 620-634, 1041-1068` (existing F29/F9 baseline counts + axis-disjoint asserts — must remain green)
**scope_lock**: Inject `data-frame-slot-id="<sub_zones[i].id>"` (additive HTML attribute) into the 11 marker-less family partials at the DOM target indicated by `frame_contracts.yaml` `partial_target_path` for each sub_zone. F29 + F9 are not modified. No change to Selenium scan, no change to `frame_contracts.yaml`, no change to `frame_slot_metrics` JS, no change to `region_marker_stamper.py`. Marker emission per-cell, mirroring F29's inline-template pattern (NOT extending the #94 root-div stamper, which is by-design root-only).
**out_of_scope**:
- F29 / F9 existing marker values (`process_column`, `product_column`, `pill_dynamic`) — preserved as-is per issue body and `.orchestrator/issues/94_stage_simulation-plan_exit.md:16`.
- F9 marker convention reconciliation (`pill_dynamic` ≠ sub_zone.id `pills`) — pre-existing divergence, separate issue.
- 19 contract-only frames (no partial HTML) — no DOM exists to mark.
- Selenium scan / `frame_slot_metrics` JS — already generic at `src/phase_z2_pipeline.py:3864`.
- `data-region-id` / `data-content-unit-id` markers — #94 axis, root-div only, disjoint.
- `frame_contracts.yaml` schema or sub_zones[i].id renames.
- `region_marker_stamper.py` extension to per-cell — stays root-only.
- MDX mutation, AI call, layout change, visual change, B4 frame_selection evidence integration.
**guardrails**:
- Additive only — append `data-frame-slot-id="..."` as a new attribute; never modify existing attributes / classes / content.
- Marker value = `frame_contracts.yaml` sub_zones[i].id verbatim (process fix, not artifact fix — `feedback_no_hardcoding`).
- F29 / F9 untouched — `tests/test_phase_z2_imp94_marker_parity.py:585-606, 620-634` must remain green; F29 6 carriers + F9 1 carrier counts preserved.
- Axis disjoint — `data-frame-slot-id` MUST NOT collide with `data-region-id` / `data-content-unit-id` (per `region_marker_stamper.py:44` and parity test L1041-1068).
- final.html byte-equivalent except for new `data-frame-slot-id` attrs (per issue body validation criterion).
- 1 commit = 1 partial (`feedback_workflow_atomicity_rules`) — 11 partials → 11 implementation units.
- AI=0 (PZ-1) — deterministic marker injection, no LLM call.
- MDX 무손실 (no content mutation through marker injection).
- Per-cell target identified by `partial_target_path` CSS selector — when a sub_zone's selector matches N elements (e.g., F29's `.f29b__grid .f29b__cell--left` matches 3 cells), all N elements receive the same `data-frame-slot-id="<sub_zone.id>"` (mirroring F29 precedent).
**unresolved_questions**:
- Q1 (injection mechanism): inline-template edit per partial (mirror F29 pattern, deterministic, 1-commit-per-partial) vs. selector-aware post-render stamper extension (single point of plumbing, more abstract)? Lock in Stage 2. Stage 1 evidence favors template edits because (a) F29 already does this, (b) `region_marker_stamper.py:44` declares disjoint axis, (c) `partial_target_path` selectors are static per-partial — no runtime context needed.
- Q2 (sub_zones with multiple-element selectors): when `partial_target_path` matches N>1 elements, place marker on each (F29 precedent) or wrap in container? Stage 2 must enumerate per-partial.
- Q3 (validation strategy): per-partial parity test (mirror `tests/test_phase_z2_imp94_marker_parity.py` shape) vs. single combined test scanning all 11 partials for `data-frame-slot-id="<expected sub_zone.id>"` after render? Lock in Stage 2.
- Q4 (MDX 01-05 coverage): which fresh-validation MDX → partial routing produces non-zero `frame_slot_metrics` count for each of the 11 newly-marked partials? Stage 2 must enumerate routing matrix to confirm `frame_slot_metrics count > 0` for at least one MDX per partial (per issue body validation criterion).
**evidence**: see EVIDENCE block above. Fresh validation (HEAD `97b7833`, branch `main`, 2026-05-27): `grep -l "data-frame-slot-id" templates/phase_z2/families/*.html` returns 2 files; inventory of 13 .html files in `templates/phase_z2/families/` verified; Selenium scan at `src/phase_z2_pipeline.py:3864` is generic; #94 axis disjoint per `src/region_marker_stamper.py:44, 92-93` and `tests/test_phase_z2_imp94_marker_parity.py:1041-1068`; #94 Stage 1/2 exit reports both name #96 as the `data-frame-slot-id` axis out-of-scope hand-off.
**source_comment_ids**: [Claude #1] (this turn).
**commit_sha**: HEAD = `97b7833` (main; no commit for #96 yet).
Verdict: root cause and scope are verified, with two path/location corrections that should be carried into Stage 2.
Root cause:
B5 frame_slot_metrics is built from a generic DOM selector, slide.querySelectorAll('[data-frame-slot-id]'), in src/phase_z2_pipeline.py:3864. The Selenium consumer is already partial-agnostic; missing metrics are caused by marker-less family partials, not by a consumer selector problem.
The issue body's src/phase_z2_pipeline.py:2734-2761 location is stale. That range is the _build_grid_dynamic_2d override path, not the B5 scan. The current scan locus is src/phase_z2_pipeline.py:3860-3887.
The contract file is templates/phase_z2/catalog/frame_contracts.yaml, not root-level frame_contracts.yaml. A root-level lookup fails; Stage 2 should use the catalog path.
Scope lock:
The implementation scope should be additive data-frame-slot-id="<sub_zones[i].id>" injection into the 11 marker-less partials only.
Do not change the Selenium scan in src/phase_z2_pipeline.py; it already consumes any element carrying data-frame-slot-id.
Do not merge this with #94 marker stamping. src/region_marker_stamper.py is explicitly root-div-only for data-region-id / data-content-unit-id, ignores frame_slot_id, and documents data-frame-slot-id as the separate #96 axis.
Preserve existing F29 and F9 behavior unless a later stage explicitly plans a compatibility migration. F29 is already aligned to contract sub-zone ids; F9 has a pre-existing divergence (pill_dynamic in HTML vs pills in the contract), which the issue body treats as existing marker behavior rather than the 11-partial expansion target.
Verified marker state:
There are 13 family partials under templates/phase_z2/families.
Existing carriers are 2/13 partials:
templates/phase_z2/families/process_product_two_way.html: six carriers, three process_column and three product_column; these match templates/phase_z2/catalog/frame_contracts.yaml sub-zone ids for F29.
templates/phase_z2/families/pre_construction_model_info_stacked.html: one carrier, pill_dynamic; the contract sub-zone id is pills.
Marker-less partials are 11/13 and match the issue scope:
Plan against templates/phase_z2/catalog/frame_contracts.yaml sub-zone id plus partial_target_path mappings.
Use deterministic partial edits or a deterministic marker-injection helper; no MDX 03/04/05 hardcoding.
Validation should confirm the new markers increase frame_slot_metrics coverage for non-F29 partials while leaving rendered content structure byte-equivalent except for additive data-* attributes.
rg -n "frame_slot_id|data-frame-slot-id|reserved for #96|Disjoint from #96|IMP-94" src/region_marker_stamper.py .orchestrator/issues tests/test_phase_z2_imp94_marker_parity.py
Not run. Current Stage 1 instruction is grep/find scope verification and explicitly says NO pytest.
Verified facts:
HEAD is 97b7833.
Working tree is dirty with unrelated tracked/untracked files; no files were modified for implementation in this Stage 1 review.
Root-level frame_contracts.yaml is missing; the live contract file is templates/phase_z2/catalog/frame_contracts.yaml.
B5 scan uses the generic selector [data-frame-slot-id] at src/phase_z2_pipeline.py:3864.
src/phase_z2_pipeline.py:2734-2761 is not the B5 scan; it is layout override logic.
Family partial count is 13 total, 2 with data-frame-slot-id, 11 without.
F29 existing marker values match contract sub-zone ids process_column and product_column.
F9 existing marker value pill_dynamic does not match contract sub-zone id pills; this is pre-existing and should be treated as compatibility-sensitive.
#94 artifacts explicitly reserve data-frame-slot-id for #96 and preserve F9/F29 frame-slot behavior.
FINAL_CONSENSUS: YES
[Codex #1] Stage 1 problem-review - IMP-96
Verdict: root cause and scope are verified, with two path/location corrections that should be carried into Stage 2.
Root cause:
- B5 `frame_slot_metrics` is built from a generic DOM selector, `slide.querySelectorAll('[data-frame-slot-id]')`, in `src/phase_z2_pipeline.py:3864`. The Selenium consumer is already partial-agnostic; missing metrics are caused by marker-less family partials, not by a consumer selector problem.
- The issue body's `src/phase_z2_pipeline.py:2734-2761` location is stale. That range is the `_build_grid_dynamic_2d` override path, not the B5 scan. The current scan locus is `src/phase_z2_pipeline.py:3860-3887`.
- The contract file is `templates/phase_z2/catalog/frame_contracts.yaml`, not root-level `frame_contracts.yaml`. A root-level lookup fails; Stage 2 should use the catalog path.
Scope lock:
- The implementation scope should be additive `data-frame-slot-id="<sub_zones[i].id>"` injection into the 11 marker-less partials only.
- Do not change the Selenium scan in `src/phase_z2_pipeline.py`; it already consumes any element carrying `data-frame-slot-id`.
- Do not merge this with #94 marker stamping. `src/region_marker_stamper.py` is explicitly root-div-only for `data-region-id` / `data-content-unit-id`, ignores `frame_slot_id`, and documents `data-frame-slot-id` as the separate #96 axis.
- Preserve existing F29 and F9 behavior unless a later stage explicitly plans a compatibility migration. F29 is already aligned to contract sub-zone ids; F9 has a pre-existing divergence (`pill_dynamic` in HTML vs `pills` in the contract), which the issue body treats as existing marker behavior rather than the 11-partial expansion target.
Verified marker state:
- There are 13 family partials under `templates/phase_z2/families`.
- Existing carriers are 2/13 partials:
- `templates/phase_z2/families/process_product_two_way.html`: six carriers, three `process_column` and three `product_column`; these match `templates/phase_z2/catalog/frame_contracts.yaml` sub-zone ids for F29.
- `templates/phase_z2/families/pre_construction_model_info_stacked.html`: one carrier, `pill_dynamic`; the contract sub-zone id is `pills`.
- Marker-less partials are 11/13 and match the issue scope:
- `templates/phase_z2/families/app_sw_package_vs_solution.html`
- `templates/phase_z2/families/bim_current_problems_paired.html`
- `templates/phase_z2/families/bim_dx_comparison_table.html`
- `templates/phase_z2/families/bim_issues_quadrant_four.html`
- `templates/phase_z2/families/construction_bim_three_usage.html`
- `templates/phase_z2/families/construction_goals_three_circle_intersection.html`
- `templates/phase_z2/families/dx_sw_necessity_three_perspectives.html`
- `templates/phase_z2/families/info_management_what_how_when.html`
- `templates/phase_z2/families/sw_reality_three_emphasis.html`
- `templates/phase_z2/families/three_parallel_requirements.html`
- `templates/phase_z2/families/three_persona_benefits.html`
Stage 2 implications:
- Plan against `templates/phase_z2/catalog/frame_contracts.yaml` sub-zone `id` plus `partial_target_path` mappings.
- Use deterministic partial edits or a deterministic marker-injection helper; no MDX 03/04/05 hardcoding.
- Validation should confirm the new markers increase `frame_slot_metrics` coverage for non-F29 partials while leaving rendered content structure byte-equivalent except for additive `data-*` attributes.
=== EVIDENCE ===
Commands run:
- `git status --short; git rev-parse --short HEAD`
- `rg -n "data-frame-slot-id|querySelectorAll\('\[data-frame-slot-id\]'\)|frame_slot_metrics" src/phase_z2_pipeline.py templates/phase_z2/families frame_contracts.yaml`
- `rg -n "frame_slot_id|data-frame-slot-id|reserved for #96|Disjoint from #96|IMP-94" src/region_marker_stamper.py .orchestrator/issues tests/test_phase_z2_imp94_marker_parity.py`
- `rg --files -g 'frame_contracts.yaml' -g '*contracts*.yaml'`
- `Get-ChildItem -LiteralPath 'templates\phase_z2\families' -Filter '*.html' | Select-Object -ExpandProperty Name | Sort-Object`
- `Get-ChildItem -LiteralPath 'templates\phase_z2\families' -Filter '*.html' | Where-Object { -not (Select-String -LiteralPath $_.FullName -Pattern 'data-frame-slot-id' -Quiet) } | ForEach-Object { $_.FullName }`
- `Get-ChildItem -LiteralPath 'templates\phase_z2\families' -Filter '*.html' | Select-String -Pattern 'data-frame-slot-id'`
- `Get-Content -LiteralPath 'src\phase_z2_pipeline.py' | Select-Object -Index (2733..2761)`
- `Get-Content -LiteralPath 'src\phase_z2_pipeline.py' | Select-Object -Index (3859..3887)`
- `Get-Content -LiteralPath 'templates\phase_z2\catalog\frame_contracts.yaml' | Select-Object -Index (20..70)`
- `Get-Content -LiteralPath 'templates\phase_z2\catalog\frame_contracts.yaml' | Select-Object -Index (80..125)`
- `Get-Content -LiteralPath 'templates\phase_z2\catalog\frame_contracts.yaml' | Select-Object -Index (830..860)`
- `Get-Content -LiteralPath 'src\region_marker_stamper.py' | Select-Object -Index (0..15), (40..45), (78..137)`
- `Select-String -LiteralPath '.orchestrator\issues\94_stage_problem-review_exit.md','.orchestrator\issues\94_stage_simulation-plan_exit.md' -Pattern '#96|data-frame-slot-id|frame-slot|frame_slot'`
- `Select-String -LiteralPath 'tests\test_phase_z2_imp94_marker_parity.py' -Pattern 'data-frame-slot-id|#96 axis|baseline_frame_slot_id_count|REGION_ID_ATTR|CONTENT_UNIT_ID_ATTR'`
Files checked:
- `src/phase_z2_pipeline.py`
- `src/region_marker_stamper.py`
- `templates/phase_z2/catalog/frame_contracts.yaml`
- `templates/phase_z2/families/*.html` via per-file `Get-ChildItem` / `Select-String`
- `.orchestrator/issues/94_stage_problem-review_exit.md`
- `.orchestrator/issues/94_stage_simulation-plan_exit.md`
- `tests/test_phase_z2_imp94_marker_parity.py`
Test results:
- Not run. Current Stage 1 instruction is grep/find scope verification and explicitly says NO pytest.
Verified facts:
- HEAD is `97b7833`.
- Working tree is dirty with unrelated tracked/untracked files; no files were modified for implementation in this Stage 1 review.
- Root-level `frame_contracts.yaml` is missing; the live contract file is `templates/phase_z2/catalog/frame_contracts.yaml`.
- B5 scan uses the generic selector `[data-frame-slot-id]` at `src/phase_z2_pipeline.py:3864`.
- `src/phase_z2_pipeline.py:2734-2761` is not the B5 scan; it is layout override logic.
- Family partial count is 13 total, 2 with `data-frame-slot-id`, 11 without.
- F29 existing marker values match contract sub-zone ids `process_column` and `product_column`.
- F9 existing marker value `pill_dynamic` does not match contract sub-zone id `pills`; this is pre-existing and should be treated as compatibility-sensitive.
- #94 artifacts explicitly reserve `data-frame-slot-id` for #96 and preserve F9/F29 frame-slot behavior.
FINAL_CONSENSUS: YES
ids verbatim from templates/phase_z2/catalog/frame_contracts.yaml.
Partial
Frame
sub_zone.id (catalog)
Carriers
construction_bim_three_usage.html
F11
category_1/2/3
3
three_parallel_requirements.html
F13
3 col sub_zones
3
three_persona_benefits.html
F14
3 col sub_zones
3
info_management_what_how_when.html
F8
3 col sub_zones
3
dx_sw_necessity_three_perspectives.html
F20
3 col sub_zones
3
sw_reality_three_emphasis.html
F28
3 col sub_zones
3
app_sw_package_vs_solution.html
F23
col_a / col_b
2
construction_goals_three_circle_intersection.html
F12
3 circles + intersection
4
bim_issues_quadrant_four.html
F16
quadrant tl/tr/bl/br
4
bim_current_problems_paired.html
F17
4 rows × 2 sides
8
bim_dx_comparison_table.html
F18
col_a_header / col_b_header / rows
3
Action: add data-frame-slot-id="<sub_zone.id>" to every DOM element matching catalog partial_target_path. Multi-element matches (descendant combinator) emit multiple identical markers on the same id. Additive only — no DOM/class/text/layout change. Convention: process_product_two_way.html:220-284.
id: u7
summary: Regression marker presence and catalog parity
files:
tests/phase_z2/test_frame_slot_marker_presence.py
tests: null
estimate_lines: null
verdict:
completeness: no
unit_constraints: u1-u6 pass file-count and line-estimate limits; u7 is not verifiable because tests and estimate_lines are absent in the visible plan body.
missing_plan_details:
F13/F14/F17 are loop-rendered source templates; the plan must specify loop-aware marker emission that resolves to exact catalog ids, not only nth-child DOM selectors.
F23 catalog selectors .f23b__col-a-body and .f23b__col-b-body do not exist in the current source partial, so the plan cannot be implemented by matching partial_target_path as stated.
F17 source has one loop body for rows 1..4; the plan must state deterministic row_{{r}}_left/right mapping against row_1_left through row_4_right.
u7 must define exact assertions: all 11 in-scope partials gain data-frame-slot-id values equal to catalog sub_zones ids; F29/F9 remain untouched; no src/phase_z2_pipeline.py or src/region_marker_stamper.py edits.
u1 rationale: F11 selector mapping is source-visible, but F13/F14 require loop-index id mapping because only one source carrier is present before Jinja render.
u2 rationale: F8/F20/F28 selectors all resolve once in source and fit additive attribute-only edits.
u3 rationale: F12 selectors resolve, but F23 needs a repaired carrier plan because the catalog body selectors are absent from the current partial.
u4 rationale: F16 quadrant selectors resolve one-to-one and remain a small isolated unit.
u5 rationale: F17 is the riskiest unit because eight runtime cells are produced from one loop body; Stage 2 must pin the dynamic id expression before edit.
u6 rationale: F18 selectors resolve one-to-one for the two headers and rows container.
u7 rationale: Regression belongs in scope, but the plan body must complete the test command/assertion list and line estimate before Stage 3.
Out-of-scope notes: Keep Selenium B5 scan in src/phase_z2_pipeline.py read-only; keep src/region_marker_stamper.py and #94 region/content marker behavior untouched; do not reconcile F9 pill_dynamic vs catalog pills; do not change F29 existing carriers.
Rollback strategy: Revert only the 11 partial edits plus tests/phase_z2/test_frame_slot_marker_presence.py if Stage 3 fails; no pipeline or stamper rollback should be needed because they are out of scope.
tests/phase_z2/test_frame_slot_marker_presence.py
Test results:
pytest -q tests failed pre-edit after 466.3s. First observed failure: tests/integration/test_multi_mdx_regression.py::test_pipeline_run_produces_step20_status[03] because phase_z2_pipeline.py raised ValueError for unknown override ZONE_ID(s) ['left', 'right'] on layout horizontal-2. Other snapshot failures were also observed.
Verified facts:
There are 13 family partial HTML files on disk; 11 are marker-less and in scope, while process_product_two_way.html and pre_construction_model_info_stacked.html already carry data-frame-slot-id markers.
B5 still uses slide.querySelectorAll('[data-frame-slot-id]') at src/phase_z2_pipeline.py:3864; no Stage 2 plan should modify it.
src/region_marker_stamper.py owns data-region-id/data-content-unit-id only; it remains out of scope for data-frame-slot-id.
Selector cross-check misses: three_parallel_requirements pillar_2/pillar_3, three_persona_benefits persona_2/persona_3, app_sw_package_vs_solution col_a/col_b, and bim_current_problems_paired row_2_left through row_4_right in source-template form.
rewind_target: stage_2_plan
FINAL_CONSENSUS: NO
[Codex #1] Stage 2 simulation-plan review - IMP-89-d B5 marker expansion (#96)
=== IMPLEMENTATION_UNITS ===
reviewed_units:
- id: u1
summary: F11/F13/F14 3-slot carrier markers
files:
- templates/phase_z2/families/construction_bim_three_usage.html
- templates/phase_z2/families/three_parallel_requirements.html
- templates/phase_z2/families/three_persona_benefits.html
tests: []
estimate_lines: 12
- id: u2
summary: F8/F20/F28 3-slot carrier markers
files:
- templates/phase_z2/families/info_management_what_how_when.html
- templates/phase_z2/families/dx_sw_necessity_three_perspectives.html
- templates/phase_z2/families/sw_reality_three_emphasis.html
tests: []
estimate_lines: 12
- id: u3
summary: F23 col_a/col_b plus F12 circle/intersection markers
files:
- templates/phase_z2/families/app_sw_package_vs_solution.html
- templates/phase_z2/families/construction_goals_three_circle_intersection.html
tests: []
estimate_lines: 8
- id: u4
summary: F16 quadrant markers
files:
- templates/phase_z2/families/bim_issues_quadrant_four.html
tests: []
estimate_lines: 6
- id: u5
summary: F17 paired 8-cell markers
files:
- templates/phase_z2/families/bim_current_problems_paired.html
tests: []
estimate_lines: 10
- id: u6
summary: F18 table header/rows markers
files:
- templates/phase_z2/families/bim_dx_comparison_table.html
tests: []
estimate_lines: 6
- id: u7
summary: Regression marker presence and catalog parity
files:
- tests/phase_z2/test_frame_slot_marker_presence.py
tests: null
estimate_lines: null
verdict:
completeness: no
unit_constraints: u1-u6 pass file-count and line-estimate limits; u7 is not verifiable because tests and estimate_lines are absent in the visible plan body.
missing_plan_details:
- F13/F14/F17 are loop-rendered source templates; the plan must specify loop-aware marker emission that resolves to exact catalog ids, not only nth-child DOM selectors.
- F23 catalog selectors .f23b__col-a-body and .f23b__col-b-body do not exist in the current source partial, so the plan cannot be implemented by matching partial_target_path as stated.
- F17 source has one loop body for rows 1..4; the plan must state deterministic row_{{r}}_left/right mapping against row_1_left through row_4_right.
- u7 must define exact assertions: all 11 in-scope partials gain data-frame-slot-id values equal to catalog sub_zones ids; F29/F9 remain untouched; no src/phase_z2_pipeline.py or src/region_marker_stamper.py edits.
u1 rationale: F11 selector mapping is source-visible, but F13/F14 require loop-index id mapping because only one source carrier is present before Jinja render.
u2 rationale: F8/F20/F28 selectors all resolve once in source and fit additive attribute-only edits.
u3 rationale: F12 selectors resolve, but F23 needs a repaired carrier plan because the catalog body selectors are absent from the current partial.
u4 rationale: F16 quadrant selectors resolve one-to-one and remain a small isolated unit.
u5 rationale: F17 is the riskiest unit because eight runtime cells are produced from one loop body; Stage 2 must pin the dynamic id expression before edit.
u6 rationale: F18 selectors resolve one-to-one for the two headers and rows container.
u7 rationale: Regression belongs in scope, but the plan body must complete the test command/assertion list and line estimate before Stage 3.
Out-of-scope notes: Keep Selenium B5 scan in src/phase_z2_pipeline.py read-only; keep src/region_marker_stamper.py and #94 region/content marker behavior untouched; do not reconcile F9 pill_dynamic vs catalog pills; do not change F29 existing carriers.
Rollback strategy: Revert only the 11 partial edits plus tests/phase_z2/test_frame_slot_marker_presence.py if Stage 3 fails; no pipeline or stamper rollback should be needed because they are out of scope.
=== EVIDENCE ===
Commands run:
- git status --short
- rg --files templates/phase_z2 | rg "(frame_contracts.yaml|families/)"
- rg -n "data-frame-slot-id|partial_target_path|sub_zones|frame_id|family" templates/phase_z2/catalog/frame_contracts.yaml templates/phase_z2/families
- python catalog/partial marker inventory using PyYAML
- pytest -q tests
- python BeautifulSoup selector cross-check for the 11 in-scope partials
- rg -n "querySelectorAll('\\[data-frame-slot-id\\]')|frame_slot_metrics|region_marker_stamper|data-region-id|data-content-unit-id" src/phase_z2_pipeline.py src/region_marker_stamper.py templates/phase_z2/families/process_product_two_way.html templates/phase_z2/families/pre_construction_model_info_stacked.html
- git rev-parse --short HEAD
Files checked:
- templates/phase_z2/catalog/frame_contracts.yaml
- templates/phase_z2/families/*.html
- src/phase_z2_pipeline.py
- src/region_marker_stamper.py
- tests/phase_z2/test_frame_slot_marker_presence.py
Test results:
- pytest -q tests failed pre-edit after 466.3s. First observed failure: tests/integration/test_multi_mdx_regression.py::test_pipeline_run_produces_step20_status[03] because phase_z2_pipeline.py raised ValueError for unknown override ZONE_ID(s) ['left', 'right'] on layout horizontal-2. Other snapshot failures were also observed.
Verified facts:
- HEAD is 97b7833.
- There are 13 family partial HTML files on disk; 11 are marker-less and in scope, while process_product_two_way.html and pre_construction_model_info_stacked.html already carry data-frame-slot-id markers.
- B5 still uses slide.querySelectorAll('[data-frame-slot-id]') at src/phase_z2_pipeline.py:3864; no Stage 2 plan should modify it.
- src/region_marker_stamper.py owns data-region-id/data-content-unit-id only; it remains out of scope for data-frame-slot-id.
- Selector cross-check misses: three_parallel_requirements pillar_2/pillar_3, three_persona_benefits persona_2/persona_3, app_sw_package_vs_solution col_a/col_b, and bim_current_problems_paired row_2_left through row_4_right in source-template form.
rewind_target: stage_2_plan
FINAL_CONSENSUS: NO
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
IMP — 89-d B5 frame_slot_metrics marker expansion (split from #89)
관련 step: Step 14 (visual check) + Step 21 (debug/trace)
source: #89 split — Stage 1 R1 의 4 sub-axis 중 89-d 분리 (2026-05-24)
roadmap axis: R1 (안정성) — Layer A first-class lock
wave: P2 (after #91 acceptance gate, after #94 89-b)
priority: 낮 — Selenium consumer 이미 partial-agnostic, marker 만 확대
dependency: #89 closed (89-a complete), #94 89-b 선결 (marker convention 통일), #91 acceptance gate 권장
scope
data-frame-slot-idattribute 를 marker-less partial 에 추가 (B5 measurement marker 확대).#89 Stage 1 R1 evidence:
src/phase_z2_pipeline.py:2734-2761Selenium scan = 이미 generic (slide.querySelectorAll('[data-frame-slot-id]'))data-region-id/data-content-unit-idmarker convention 과 paired — 같은 partial 동시 작업 또는 #94 후out of scope
guardrail / validation
data-frame-slot-id="<sub_zone.id>"추가 (additive only)frame_contracts.yaml의sub_zones[i].id와partial_target_pathselector 의 mapping 정합frame_slot_metricscount 가 F29 외 다른 partial 에서도 측정 됨 확인data-*attrsrelevant feedback
feedback_workflow_atomicity_rules— 1 commit = 1 partial 권장feedback_no_hardcoding— deterministic marker injection🤖 Claude Opus 4.7 — #89 split (2026-05-24)
[Claude #1] Stage 1 problem-review — IMP-89-d B5 frame_slot_metrics marker expansion (#96)
=== EVIDENCE ===
HEAD:
97b7833(main, working tree clean for tracked files relevant to #96 scope)Selenium scan locus correction (issue body says
src/phase_z2_pipeline.py:2734-2761, actual is 3864):src/phase_z2_pipeline.py:3860-3887— B5 v0frame_slot_metricsbuilder. The actual scan =slide.querySelectorAll('[data-frame-slot-id]').forEach((cell) => { ... })at L3864. This is a generic attribute-existence selector — partial-agnostic. Issue body's L2734-2761 reference is incorrect (that range is_build_grid_dynamic_2doverride path); recorded for #96 Stage 2 follow-up.Existing
data-frame-slot-idcarriers (2/13 partials):templates/phase_z2/families/process_product_two_way.html(F29, frame_id1171281210) — 6 inline carriers on.f29b__cell--left× 3 (data-frame-slot-id="process_column") and.f29b__cell--right× 3 (data-frame-slot-id="product_column"). Marker values matchframe_contracts.yaml:111-119sub_zonesprocess_column/product_column.templates/phase_z2/families/pre_construction_model_info_stacked.html(F9, frame_id1171281180) — 1 inline carrier at L180 on.f9b__pillwithdata-frame-slot-id="pill_dynamic". NB: marker valuepill_dynamicdoes NOT matchframe_contracts.yaml:852-857sub_zone.idpills(pre-existing convention divergence; out-of-scope per issue body "F29 + F9 = 이미 marker 있음").Marker-less partials (11/13 — #96 scope):
templates/phase_z2/families/app_sw_package_vs_solution.htmltemplates/phase_z2/families/bim_current_problems_paired.htmltemplates/phase_z2/families/bim_dx_comparison_table.htmltemplates/phase_z2/families/bim_issues_quadrant_four.htmltemplates/phase_z2/families/construction_bim_three_usage.htmltemplates/phase_z2/families/construction_goals_three_circle_intersection.htmltemplates/phase_z2/families/dx_sw_necessity_three_perspectives.htmltemplates/phase_z2/families/info_management_what_how_when.htmltemplates/phase_z2/families/sw_reality_three_emphasis.htmltemplates/phase_z2/families/three_parallel_requirements.htmltemplates/phase_z2/families/three_persona_benefits.htmlVerified with
grep -L "data-frame-slot-id" templates/phase_z2/families/*.html(13 total .html files; 2 carriers; 11 absentees).#94 paired axis confirmation:
src/region_marker_stamper.py:9-12, 80-137— IMP-94 stamper is root-div-only (one stamp per zone on firstdata-template-iddiv). L92-93 explicit comment:frame_slot_idis "optional, ignored here — reserved for #96 (89-d)". L44 guardrail: "Disjoint from #96 (data-frame-slot-idis a separate axis / attr)"..orchestrator/issues/94_stage_problem-review_exit.md:31+94_stage_simulation-plan_exit.md:11,16— explicit out-of-scope hand-offs to #96, with F9/F29 frame-slot behavior preserved.tests/test_phase_z2_imp94_marker_parity.py:585-606, 620-634, 1041-1068— existing parity tests assert F29process_column/product_columncounts and F9pill_dynamiccount are unchanged by stamper, and thatdata-frame-slot-idaxis is disjoint fromdata-region-id/data-content-unit-id. Adding markers to 11 new partials must NOT change F29/F9 baseline counts.frame_contracts.yamlsub_zones[i].id ↔ partial_target_path mapping:id:andpartial_target_path:pairs across all 13 partials (32-frame catalog — 13 partial-backed + 19 contract-only). Per-cell CSS selectors (e.g.,.f13b__cols > .f13b__col:nth-child(1),.f16b__quadrant--tl,.f29b__grid .f29b__cell--left) identify the DOM target for each sub_zone — these are the injection sites fordata-frame-slot-id="<sub_zone.id>". (Selector-by-selector enumeration deferred to Stage 2.)=== EXIT REPORT (English, binding contract) ===
root_cause: The B5
frame_slot_metricsSelenium scan atsrc/phase_z2_pipeline.py:3864is already partial-agnostic (querySelectorAll('[data-frame-slot-id]')), but only 2/13 family partials emitdata-frame-slot-idmarkers (F29process_product_two_way× 6 cells, F9pre_construction_model_info_stacked× 1 cell). The remaining 11 partials carry zerodata-frame-slot-idattributes, soframe_slot_metricsreturns empty entries for non-F29/F9 frames — Step 14 (visual check) and Step 21 (debug/trace) lose per-cell measurement coverage on those frames. This is a marker-emission gap, NOT a scan-side defect; the fix is per-cell marker injection on the 11 marker-less partials, sourced fromframe_contracts.yamlsub_zones[i].id and targeted atpartial_target_pathselector locations.key_files:
templates/phase_z2/families/*.html(11 marker-less partials — injection targets enumerated in EVIDENCE)templates/phase_z2/catalog/frame_contracts.yaml(sub_zones[i].id + partial_target_path source of truth — 13 partial-backed contracts)src/phase_z2_pipeline.py:3860-3887(B5 v0frame_slot_metricsconsumer — generic scan, no change)src/region_marker_stamper.py:9-12, 44, 92-93(#94 axis boundary —data-frame-slot-idexplicitly disjoint)tests/test_phase_z2_imp94_marker_parity.py:585-606, 620-634, 1041-1068(existing F29/F9 baseline counts + axis-disjoint asserts — must remain green)scope_lock: Inject
data-frame-slot-id="<sub_zones[i].id>"(additive HTML attribute) into the 11 marker-less family partials at the DOM target indicated byframe_contracts.yamlpartial_target_pathfor each sub_zone. F29 + F9 are not modified. No change to Selenium scan, no change toframe_contracts.yaml, no change toframe_slot_metricsJS, no change toregion_marker_stamper.py. Marker emission per-cell, mirroring F29's inline-template pattern (NOT extending the #94 root-div stamper, which is by-design root-only).out_of_scope:
process_column,product_column,pill_dynamic) — preserved as-is per issue body and.orchestrator/issues/94_stage_simulation-plan_exit.md:16.pill_dynamic≠ sub_zone.idpills) — pre-existing divergence, separate issue.frame_slot_metricsJS — already generic atsrc/phase_z2_pipeline.py:3864.data-region-id/data-content-unit-idmarkers — #94 axis, root-div only, disjoint.frame_contracts.yamlschema or sub_zones[i].id renames.region_marker_stamper.pyextension to per-cell — stays root-only.guardrails:
data-frame-slot-id="..."as a new attribute; never modify existing attributes / classes / content.frame_contracts.yamlsub_zones[i].id verbatim (process fix, not artifact fix —feedback_no_hardcoding).tests/test_phase_z2_imp94_marker_parity.py:585-606, 620-634must remain green; F29 6 carriers + F9 1 carrier counts preserved.data-frame-slot-idMUST NOT collide withdata-region-id/data-content-unit-id(perregion_marker_stamper.py:44and parity test L1041-1068).data-frame-slot-idattrs (per issue body validation criterion).feedback_workflow_atomicity_rules) — 11 partials → 11 implementation units.partial_target_pathCSS selector — when a sub_zone's selector matches N elements (e.g., F29's.f29b__grid .f29b__cell--leftmatches 3 cells), all N elements receive the samedata-frame-slot-id="<sub_zone.id>"(mirroring F29 precedent).unresolved_questions:
region_marker_stamper.py:44declares disjoint axis, (c)partial_target_pathselectors are static per-partial — no runtime context needed.partial_target_pathmatches N>1 elements, place marker on each (F29 precedent) or wrap in container? Stage 2 must enumerate per-partial.tests/test_phase_z2_imp94_marker_parity.pyshape) vs. single combined test scanning all 11 partials fordata-frame-slot-id="<expected sub_zone.id>"after render? Lock in Stage 2.frame_slot_metricscount for each of the 11 newly-marked partials? Stage 2 must enumerate routing matrix to confirmframe_slot_metrics count > 0for at least one MDX per partial (per issue body validation criterion).evidence: see EVIDENCE block above. Fresh validation (HEAD
97b7833, branchmain, 2026-05-27):grep -l "data-frame-slot-id" templates/phase_z2/families/*.htmlreturns 2 files; inventory of 13 .html files intemplates/phase_z2/families/verified; Selenium scan atsrc/phase_z2_pipeline.py:3864is generic; #94 axis disjoint persrc/region_marker_stamper.py:44, 92-93andtests/test_phase_z2_imp94_marker_parity.py:1041-1068; #94 Stage 1/2 exit reports both name #96 as thedata-frame-slot-idaxis out-of-scope hand-off.source_comment_ids: [Claude #1] (this turn).
commit_sha: HEAD =
97b7833(main; no commit for #96 yet).[Codex #1] Stage 1 problem-review - IMP-96
Verdict: root cause and scope are verified, with two path/location corrections that should be carried into Stage 2.
Root cause:
frame_slot_metricsis built from a generic DOM selector,slide.querySelectorAll('[data-frame-slot-id]'), insrc/phase_z2_pipeline.py:3864. The Selenium consumer is already partial-agnostic; missing metrics are caused by marker-less family partials, not by a consumer selector problem.src/phase_z2_pipeline.py:2734-2761location is stale. That range is the_build_grid_dynamic_2doverride path, not the B5 scan. The current scan locus issrc/phase_z2_pipeline.py:3860-3887.templates/phase_z2/catalog/frame_contracts.yaml, not root-levelframe_contracts.yaml. A root-level lookup fails; Stage 2 should use the catalog path.Scope lock:
data-frame-slot-id="<sub_zones[i].id>"injection into the 11 marker-less partials only.src/phase_z2_pipeline.py; it already consumes any element carryingdata-frame-slot-id.src/region_marker_stamper.pyis explicitly root-div-only fordata-region-id/data-content-unit-id, ignoresframe_slot_id, and documentsdata-frame-slot-idas the separate #96 axis.pill_dynamicin HTML vspillsin the contract), which the issue body treats as existing marker behavior rather than the 11-partial expansion target.Verified marker state:
templates/phase_z2/families.templates/phase_z2/families/process_product_two_way.html: six carriers, threeprocess_columnand threeproduct_column; these matchtemplates/phase_z2/catalog/frame_contracts.yamlsub-zone ids for F29.templates/phase_z2/families/pre_construction_model_info_stacked.html: one carrier,pill_dynamic; the contract sub-zone id ispills.templates/phase_z2/families/app_sw_package_vs_solution.htmltemplates/phase_z2/families/bim_current_problems_paired.htmltemplates/phase_z2/families/bim_dx_comparison_table.htmltemplates/phase_z2/families/bim_issues_quadrant_four.htmltemplates/phase_z2/families/construction_bim_three_usage.htmltemplates/phase_z2/families/construction_goals_three_circle_intersection.htmltemplates/phase_z2/families/dx_sw_necessity_three_perspectives.htmltemplates/phase_z2/families/info_management_what_how_when.htmltemplates/phase_z2/families/sw_reality_three_emphasis.htmltemplates/phase_z2/families/three_parallel_requirements.htmltemplates/phase_z2/families/three_persona_benefits.htmlStage 2 implications:
templates/phase_z2/catalog/frame_contracts.yamlsub-zoneidpluspartial_target_pathmappings.frame_slot_metricscoverage for non-F29 partials while leaving rendered content structure byte-equivalent except for additivedata-*attributes.=== EVIDENCE ===
Commands run:
git status --short; git rev-parse --short HEADrg -n "data-frame-slot-id|querySelectorAll\('\[data-frame-slot-id\]'\)|frame_slot_metrics" src/phase_z2_pipeline.py templates/phase_z2/families frame_contracts.yamlrg -n "frame_slot_id|data-frame-slot-id|reserved for #96|Disjoint from #96|IMP-94" src/region_marker_stamper.py .orchestrator/issues tests/test_phase_z2_imp94_marker_parity.pyrg --files -g 'frame_contracts.yaml' -g '*contracts*.yaml'Get-ChildItem -LiteralPath 'templates\phase_z2\families' -Filter '*.html' | Select-Object -ExpandProperty Name | Sort-ObjectGet-ChildItem -LiteralPath 'templates\phase_z2\families' -Filter '*.html' | Where-Object { -not (Select-String -LiteralPath $_.FullName -Pattern 'data-frame-slot-id' -Quiet) } | ForEach-Object { $_.FullName }Get-ChildItem -LiteralPath 'templates\phase_z2\families' -Filter '*.html' | Select-String -Pattern 'data-frame-slot-id'Get-Content -LiteralPath 'src\phase_z2_pipeline.py' | Select-Object -Index (2733..2761)Get-Content -LiteralPath 'src\phase_z2_pipeline.py' | Select-Object -Index (3859..3887)Get-Content -LiteralPath 'templates\phase_z2\catalog\frame_contracts.yaml' | Select-Object -Index (20..70)Get-Content -LiteralPath 'templates\phase_z2\catalog\frame_contracts.yaml' | Select-Object -Index (80..125)Get-Content -LiteralPath 'templates\phase_z2\catalog\frame_contracts.yaml' | Select-Object -Index (830..860)Get-Content -LiteralPath 'src\region_marker_stamper.py' | Select-Object -Index (0..15), (40..45), (78..137)Select-String -LiteralPath '.orchestrator\issues\94_stage_problem-review_exit.md','.orchestrator\issues\94_stage_simulation-plan_exit.md' -Pattern '#96|data-frame-slot-id|frame-slot|frame_slot'Select-String -LiteralPath 'tests\test_phase_z2_imp94_marker_parity.py' -Pattern 'data-frame-slot-id|#96 axis|baseline_frame_slot_id_count|REGION_ID_ATTR|CONTENT_UNIT_ID_ATTR'Files checked:
src/phase_z2_pipeline.pysrc/region_marker_stamper.pytemplates/phase_z2/catalog/frame_contracts.yamltemplates/phase_z2/families/*.htmlvia per-fileGet-ChildItem/Select-String.orchestrator/issues/94_stage_problem-review_exit.md.orchestrator/issues/94_stage_simulation-plan_exit.mdtests/test_phase_z2_imp94_marker_parity.pyTest results:
Verified facts:
97b7833.frame_contracts.yamlis missing; the live contract file istemplates/phase_z2/catalog/frame_contracts.yaml.[data-frame-slot-id]atsrc/phase_z2_pipeline.py:3864.src/phase_z2_pipeline.py:2734-2761is not the B5 scan; it is layout override logic.data-frame-slot-id, 11 without.process_columnandproduct_column.pill_dynamicdoes not match contract sub-zone idpills; this is pre-existing and should be treated as compatibility-sensitive.data-frame-slot-idfor #96 and preserve F9/F29 frame-slot behavior.FINAL_CONSENSUS: YES
[Claude #2] Stage 2 simulation-plan — IMP-89-d B5 marker expansion (#96)
Axis — 11 marker-less partials (
templates/phase_z2/families/)ids verbatim from
templates/phase_z2/catalog/frame_contracts.yaml.Action: add
data-frame-slot-id="<sub_zone.id>"to every DOM element matching catalogpartial_target_path. Multi-element matches (descendant combinator) emit multiple identical markers on the same id. Additive only — no DOM/class/text/layout change. Convention:process_product_two_way.html:220-284.=== IMPLEMENTATION_UNITS ===
Rationale
sub_zone.idappears asdata-frame-slot-id.frame_slot_metrics, asserts non-F29/F9 frames contribute keys.Out-of-scope (Stage 1 scope_lock carried)
src/phase_z2_pipeline.pyB5 selector;src/region_marker_stamper.pyand #94 region/content axis; F29 + F9 existing carriers; F9pill_dynamicvs catalogpillsdivergence; B4 frame_selection evidence; Layer A render-path activation; 19 contract-only templates.Rollback
Per-unit isolated commits →
git revert <sha>. Additivedata-*only; no schema/consumer/fixture impact. u7/u8 catch future removal.=== EVIDENCE ===
97b7833(main; tracked tree clean for #96-scope).ls templates/phase_z2/families/*.html→ 13 partials.grep -l data-frame-slot-id templates/phase_z2/families/*.html→ 2 marker-bearing (process_product_two_way, pre_construction_model_info_stacked).grep -oE 'f[0-9]+b__'→ F8/F11/F12/F13/F14/F16/F17/F18/F20/F23/F28.process_product_two_way.html:220-284.frame_contracts.yaml:381-396. F18::451-466. F23::803-813.partial_target_pathentries in catalog (catalog-driven u7 feasible via existing YAML loader).FINAL_CONSENSUS: YES
[Codex #1] Stage 2 simulation-plan review - IMP-89-d B5 marker expansion (#96)
=== IMPLEMENTATION_UNITS ===
reviewed_units:
summary: F11/F13/F14 3-slot carrier markers
files:
tests: []
estimate_lines: 12
summary: F8/F20/F28 3-slot carrier markers
files:
tests: []
estimate_lines: 12
summary: F23 col_a/col_b plus F12 circle/intersection markers
files:
tests: []
estimate_lines: 8
summary: F16 quadrant markers
files:
tests: []
estimate_lines: 6
summary: F17 paired 8-cell markers
files:
tests: []
estimate_lines: 10
summary: F18 table header/rows markers
files:
tests: []
estimate_lines: 6
summary: Regression marker presence and catalog parity
files:
tests: null
estimate_lines: null
verdict:
completeness: no
unit_constraints: u1-u6 pass file-count and line-estimate limits; u7 is not verifiable because tests and estimate_lines are absent in the visible plan body.
missing_plan_details:
u1 rationale: F11 selector mapping is source-visible, but F13/F14 require loop-index id mapping because only one source carrier is present before Jinja render.
u2 rationale: F8/F20/F28 selectors all resolve once in source and fit additive attribute-only edits.
u3 rationale: F12 selectors resolve, but F23 needs a repaired carrier plan because the catalog body selectors are absent from the current partial.
u4 rationale: F16 quadrant selectors resolve one-to-one and remain a small isolated unit.
u5 rationale: F17 is the riskiest unit because eight runtime cells are produced from one loop body; Stage 2 must pin the dynamic id expression before edit.
u6 rationale: F18 selectors resolve one-to-one for the two headers and rows container.
u7 rationale: Regression belongs in scope, but the plan body must complete the test command/assertion list and line estimate before Stage 3.
Out-of-scope notes: Keep Selenium B5 scan in src/phase_z2_pipeline.py read-only; keep src/region_marker_stamper.py and #94 region/content marker behavior untouched; do not reconcile F9 pill_dynamic vs catalog pills; do not change F29 existing carriers.
Rollback strategy: Revert only the 11 partial edits plus tests/phase_z2/test_frame_slot_marker_presence.py if Stage 3 fails; no pipeline or stamper rollback should be needed because they are out of scope.
=== EVIDENCE ===
Commands run:
Files checked:
Test results:
Verified facts:
97b7833.rewind_target: stage_2_plan
FINAL_CONSENSUS: NO