Files
C.E.L_Slide_test2/docs/architecture/INTEGRATION-AUDIT-01-REPORT.md
kyeongmin 02e2ae0afb docs(#54): F-4 legacy annotation + F-5 fixture convention -- AUDIT-01 housekeeping
INTEGRATION-AUDIT-01 (#50) §10.4 / §10.5 housekeeping carry-over.

F-4: annotate 14 remaining legacy Phase R'/Q sample-text hits across 10
src/ files with inline marker `# [legacy Phase R'/Q example -- INTEGRATION-AUDIT-01 §10.4]`.
Comment-only. No string-literal / regex / sample dict value mutated.
fit_verifier.py L612 marker keeps Phase Z partial-live import graph
(FitAnalysis / RoleFit / redistribute / salvage) byte-precise.

F-5: docs-only addendum -- §10.5.1 in INTEGRATION-AUDIT-01-REPORT.md +
tests/CLAUDE.md fixture convention note. No root tests/fixtures/ dir
created; existing tests/phase_z2/fixtures/ convention preserved. Documents
test-only sample-reference allowance vs src/** runtime prohibition.

Out of scope: Phase Z source 11 hits (phase_z2_content_extractor /
failure_router / mapper / retry), production behavior change, #19 work.

Verified: pytest -q tests/phase_z2/ = 157 PASS. git diff +210/-0
(35 src/docs lines + 175 new tests/CLAUDE.md). No behavioral delta.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 20:23:36 +09:00

80 KiB

INTEGRATION-AUDIT-01 -- Phase Z closed-issue cumulative consistency review

Section 1. Audit anchor

Anchor (cited verbatim per Stage 1 exit report) :

This audit verifies pipeline contracts. It does not optimize any single MDX sample.

Scope : 22 closed Gitea issues #2-#18 + #45-#49 on Kyeongmin/C.E.L_Slide_test2 against the 22-step Phase Z pipeline (docs/architecture/PHASE-Z-PIPELINE-OVERVIEW.md, Steps 1-22 plus Step 0 precondition).

Mode : audit-only -- no source code changes. Report-only file changes under docs/architecture/INTEGRATION-AUDIT-*.md and one row in docs/architecture/PHASE-Z-IMPLEMENTATION-ISSUE-BACKLOG.md (u7).

Parent / child relationship : Gitea #15 = parent (IMP-15 Step 14 visual_check reinforcement). Execution children = #45 / #46 / #47 / #48 / #49. Locked child SHAs (Stage 1 exit report) :

  • #45 -> e9b3d2e (execution-1, image_aspect_mismatch detection)
  • #46 -> 2827622 (execution-2, table_self_overflow detection; commit message label says IMP-16 but the closed Gitea issue is #46; flagged in Section 3)
  • #47 -> 535c484 (execution-3, classifier consumes image+table events)
  • #48 -> 614c533 (execution-4, debug.json event surfacing + spec taxonomy)
  • #49 -> no new SHA (verification-only per #15 body; re-uses 614c533 evidence)

Close timestamp anomaly (Stage 1 lock, recorded; NOT reopened) :

  • #15 closed 2026-05-19T02:35:05+09:00
  • #45 / #46 / #47 / #48 all closed BEFORE #15 (correct ordering)
  • #49 closed 2026-05-19T02:49:56+09:00 -- about 15 minutes AFTER #15 close (anomaly)
  • Disposition : record-only in Section 3 / Section 6 finding column; no remediation row in backlog beyond the existing audit completion row (u7).

Excluded (open / not in audit) : #1, #19, #20, #21, #22, #23, #24, #25, #26, #27, #28, #38, #39, #40, #41, #42, #43, #44.

Sample budget : samples/mdx_batch/03.mdx (smoke) plus samples/mdx_batch/04.mdx (details + images). Pipeline runs captured in Section 7.


Section 2. Baseline pytest

Method : pytest -q tests is the project regression suite. The audit captures it twice -- once before any u5 / u6 / u7 edits, once after Section 7 / 8 grep + render evidence is collected. Equality of both runs proves the audit-only work surface (docs/architecture/INTEGRATION-AUDIT-*.md + backlog row in u7) did not perturb production code.

Command : pytest -q tests (working dir = repo root D:\ad-hoc\kei\design_agent\).

Pytest BEFORE audit u5 edits (audit date 2026-05-19) :

  • Result : 303 passed in 40.80s
  • Last 5 progress dots aggregated to [100%] then Running teardown with pytest sessionfinish... -- expected suite teardown banner.

Pytest AFTER audit u5 edits (post §7 / §8 evidence collection, same audit date) :

  • Result : 303 passed in 40.54s
  • 303 == 303 ; 0 new failures, 0 skipped, 0 errored. Test count parity proves no test discovery side-effect from new audit docs.

Verdict : OK. Audit-only edits under docs/architecture/INTEGRATION-AUDIT-*.md introduce no regression. Baseline stable across u5 assembly.


Section 3. Axis 1 -- Scope myopia (22 issues x adjacent-contract cross-reference)

Method : per closed issue, list (a) its own scope as declared in body / backlog row / closing commits, (b) adjacent pipeline contracts the change could have leaked into, (c) downstream consumers of its outputs, (d) finding label OK / Warning / Blocker. Each row cites src/, tests/, docs/, or templates/ paths.

De-dup convention : #15 is treated as the integration parent; the actual code/test changes are owned by execution children #45-#49. #15 row records integration glue only (parent close evidence + cross-child reconciliation). No change is double-counted across parent + child.

Pipeline step shorthand (per PHASE-Z-PIPELINE-OVERVIEW.md, full 22-step list) :

  • Step 0 precondition / 1 MDX upload / 2 normalize / 3 content_object / 4 internal composition planning / 5 V4 evidence / 6 composition planning / 7 layout vocabulary / 8 zone+region ratio / 9 region-level frame/display / 10 frame contract / 11 region-to-slot mapping / 12 slot payload / 13 render / 14 visual_check / 15 fit_classification / 16 router / 17 action / 18 failure_classify / 19 next_action / 20 slide_status / 21 debug.json / 22 user UI.

Section 3 table -- 22 rows

# issue (title) declared own_scope adjacent contracts (potential leak surface) downstream consumers finding evidence path
1 #2 IMP-02 A-1 Stage 0 normalize chained adapter Step 2 -- chained normalize_mdx_content + extract_major_sections + extract_conclusion_text with dual-write, preserve raw MDX Step 3 content_object input shape (raw chunk handoff); Step 21 debug.json schema (step02_* keys) Step 3 (IMP-03 ContentObject extractor); Step 21 trace writer; Step 7/8 layout planner (consumes normalized section list) OK -- additive; preserves prior extract_* semantics via dual-write; no AI in path src/phase_z2_pipeline.py (commit bac13c0, +165/-3)
2 #3 IMP-03 A-1 popup/image/table trace Step 3 -- normalize popups/images/tables into ContentObject (B1 v0 extension); slide-level rich ContentObject trace Step 2 normalize output shape (consumer); Step 4 internal composition planning (Step 4 itself still not implemented, so this row only emits trace); Step 21 debug.json schema Step 4 (not yet implemented; receives data only via trace); Step 21 debug.json (content_objects field) OK -- emits trace without coupling to downstream Step 4 (Step 4 still pending); raw content preserved (no AI summarization; satisfies feedback_ai_isolation_contract) src/phase_z2_content_extractor.py + src/phase_z2_pipeline.py (commit fc3f7d8)
3 #4 IMP-04 A-2 catalog expansion Step 0 + Step 9 -- register/expand 16 frame_partials + frame_contracts.yaml schema; F17 paired_rows_4x2 + pill alternation + theme Step 5 V4 evidence (catalog size affects evidence pool); Step 10 frame contract validator (consumes new contracts); Step 12 mapper PAYLOAD_BUILDERS (consumes new schema); Step 13 render template surface (16 new templates/phase_z2/families/*.html) Step 5/9/10/12/13; smoke tests scripts/smoke_frame_render.py OK -- pre-render planning only; catalog is read-only data for V4; frame DB extension matches Step 0 contract; commit 73a98b8 corrected F17 schema after first land (factual_verification path active) templates/phase_z2/catalog/frame_contracts.yaml; templates/phase_z2/families/*.html; src/phase_z2_mapper.py; docs/architecture/IMP-04-FRAME-SUITABILITY-MATRIX.md
4 #5 IMP-05 A-5 V4 fallback Step 9 + Step 16/17 -- deterministic V4 candidate bridge (pre-render rank-2/3 fallback); trace schema; dedup invariant test; new PASS_WITH_FALLBACK status semantics in Step 20 Step 5 evidence (candidate dedup must agree with rank-1 path); Step 6 composition (candidates[0] backward-compat); Step 9 application_plan; Step 20 status enum; debug.json trace Step 9/16/17/20; tests/test_phase_z2_v4_fallback.py; tests/test_catalog_invariant.py OK -- pre-render bridge (Block A); deterministic (no AI); rank-1 path unchanged (backward compat per backlog guardrail); dedup invariant test guards collision with #4 catalog expansion src/phase_z2_pipeline.py + src/phase_z2_composition.py + src/phase_z2_router.py (commits 15c5b9a, 21476ae, 23d1b25)
5 #6 IMP-06 B-1 zone-section override Step 6 + Step 1/22 input -- CLI arg + composition planner override (replaced_auto_unit, render_records, plan-aware traces, units rebuild, empty zone) Step 1 CLI surface; Step 6 plan_composition schema (CompositionUnit); Step 7/8/9 downstream (units rebuild forces re-planning); Step 13 render (Catch K render-path) Step 7/8/9/13; debug.json render_records; Front/ (later wired via #8 U3) Warning -- wide blast radius (4 commits + Stage 4 blocker-fix 52ccb7f); units-rebuild touches Step 7/8/9 implicitly; verified by tests/test_phase_z2_section_assignment_override.py (285 + 42 + 228 lines). No AI; deterministic. Risk = override path widens Step 6 surface where Step 4 is still pending src/phase_z2_pipeline.py (commits d596fab b81e564 1f15495 52ccb7f)
6 #7 IMP-07 B-2 edited HTML to MDX reverse path Step 22 + Step 1/2 input -- Vite/React Front/ plus reverse path glue; pipeline re-entry Step 1 MDX upload; Step 2 normalize (must accept reverse-path MDX); CLI plus service API; feedback_ai_isolation_contract (reverse must not invoke AI rewrite) Step 2 (reverse-path consumer); Front/client/src/services/designAgentApi.ts; pipeline CLI OK -- frontend-shipped (0f0d3fa); reverse path schema aligned with #2 Stage 0 normalize via hard-link declared in backlog. AI isolation preserved (no normal-path LLM in reverse). Front/; src/phase_z2_pipeline.py; backlog row IMP-07
7 #8 IMP-08 B-3 sub-section drag-drop Step 3 schema -- sub_sections schema + V4 alias resolver + aligner canonical sub-id + decimal alias guard (N-R5) + frontend wire Step 3 ContentObject schema (extends #3); Step 5 V4 alias surface; Step 6 composition planner (consumer); Step 9 application_plan; Front/ zoneSections override (U3) Step 5/6/9/13; tests/test_phase_z2_subsection_schema.py (82+100+61 lines) OK -- additive schema with explicit backward-compat guard (alias resolver at 4 lookup sites); Stage 5 R2 blocker-fix 8f6cffc force-drills aligner only on override targets (scope contained) src/phase_z2_pipeline.py + src/phase_z2_composition.py (commits a422d72 5191aca ab2764c 8f6cffc)
8 #9 IMP-09 B-4 non-default layout zone-geometry Step 8 -- col-axis solver + per-zone geometry mapper + retry gate; 2-D dynamic dispatch for 5 preset families (single + horizontal-2 + vertical-2 + top-1-bottom-2 + top-2-bottom-1 + left-1-right-2 + left-2-right-1 + grid-2x2) Step 7 layout vocabulary (consumer); Step 9 region-level (zone geometry feeds region ratios; Step 9 region-level still warning); Step 17 zone_ratio_retry (#12 IMP-12 retry path); Step 13 render build_layout_css Step 9/13/17; tests/phase_z2/fixtures/build_layout_css/*.yaml (16 fixtures); tests/phase_z2/fixtures/retry_gate/*.yaml OK -- all 8 vocabulary entries enabled in build_layout_css; fixtures supply provable diff per preset; no Kei/Phase R' regression (existing build_containers_type_b untouched) src/phase_z2_pipeline.py (commits 201099e PR1, 1fb9732 PR2)
9 #10 IMP-10 D-1 filtered_section_reasons UI Step 20/22 -- frontend read-only display of filtered_section_reasons artifact Step 20 slide_status enum (read-only consumer); Front/ service API; no backend mutation Front/client/src/pages/Home.tsx; Front/client/src/services/designAgentApi.ts OK -- frontend-only; backend artifact strictly read-only per backlog guardrail Front/ (commit 0fb168b, +45 lines)
10 #11 IMP-11 D-2 Frame min_height display Step 22 -- min_height_px hint exposed backend to UI; resize hint read-only; Step 9 v4 all-judgments min_height test Step 0 frame contract (min_height_px field); Step 9 region-level (consumer); Front/ SlideCanvas Step 9; Front/client/src/components/SlideCanvas.tsx; tests/test_phase_z2_step9_v4_all_judgments_min_height.py OK -- contract read-only; backend exposure is additive payload field (src/phase_z2_pipeline.py +32/-12 in a79bd8b) src/phase_z2_pipeline.py + Front/client/src/components/SlideCanvas.tsx; tests/test_phase_z2_step9_v4_all_judgments_min_height.py
11 #12 IMP-12 Step 16/17 retry refinement Step 16 + Step 17 -- multi-donor + 3-stage salvage cascade; redistribute + glue + font compression; new router action; new failure_router taxonomy Step 14 visual_check (donor selection consumes overflow events); Step 18 failure_classify (cascade adds new failure types); Step 19 next_action (downstream router consumer); Step 20 status semantics; feedback_phase_z_spacing_direction (cross-zone redistribute is grant-changing, not common-shrink) Step 18/19/20; tests/phase_z2/test_phase_z2_* (cross_zone, font_step, glue, multi_donor, step17_salvage_chain -- 5 new test modules) Warning -- large blast radius (4 src files + 5 test modules in 56619a0); multi-donor introduces cross-zone state in Step 17; verified by 5 dedicated test modules. Risk = cascade may interact with #5 V4 fallback path in Step 20 status enum (mitigated by separate status enums per #5 exit report) src/phase_z2_failure_router.py + src/phase_z2_pipeline.py + src/phase_z2_retry.py + src/phase_z2_router.py (commit 56619a0)
12 #13 IMP-13 A-3 frame preview consistency Step 0 + Step 14/21 -- build-time frame preview generator (salvage of capture_slide_screenshot) Step 0 catalog frame_partials (consumer for snapshot); Step 14 visual_check (uses preview for sanity, read-only); no Phase R' regression scripts/generate_frame_previews.py; tests/test_generate_frame_previews.py OK -- build-time only (not in runtime pipeline); deterministic; no Phase R' coupling (script lives in scripts/) scripts/generate_frame_previews.py (commit 7d5639a, 239 LOC + 50 LOC test)
13 #14 IMP-14 A-4 slide-base iframe mode Step 13 render -- slide-base.html conditional CSS (embedded vs standalone); Step 0 contract bit Step 0 slide_base template; Step 13 Jinja2 deterministic render; Front/ SlideCanvas (consumer) Step 13; Front/client/src/components/SlideCanvas.tsx; tests/phase_z2/test_slide_base_embedded_mode.py OK -- render-time contract only; Jinja2 deterministic; embedded mode reduces SlideCanvas friction (34 LOC simplified) templates/phase_z2/slide_base.html + src/phase_z2_pipeline.py (commit 7a52ceb)
14 #15 IMP-15 Step 14 visual_check reinforcement (PARENT -- execution children #45-#49) Integration glue only -- no direct code under #15; closure depends on #45-#49 SHAs. De-duped against children (real change attribution = #45-#49 rows below) Step 14 (parent contract); Step 15 fit_classification consumer; Step 21 debug.json trace; PHASE-Z-FIT-CLASSIFIER-ROUTER-SPEC.md (taxonomy row added by #48) Step 15/21; spec doc Warning -- close-timestamp anomaly only : #49 closed at 2026-05-19T02:49:56+09:00, about 15 minutes AFTER #15 close 02:35:05+09:00. All other children (#45-#48) close BEFORE #15. #49 body declares verification-only path (no new SHA; re-uses 614c533), so post-close #49 close does not leak code into #15. Disposition : record-only, no reopen. docs/architecture/PHASE-Z-PIPELINE-OVERVIEW.md Step 14/15; child rows below
15 #16 IMP-16 B-2 verification helper axis Step 1/2/14/21/22 -- phase_z2_verification_utils.py port + 8 verification test modules + U2 wiring design doc Step 22 reverse path verification (consumer is #7 IMP-07 once activated); no normal-path coupling Step 14/21 trace consumers (utility); future #7 reverse-path verification OK -- utility module plus design doc only; no normal-path coupling (gated by #7 activation); commit 23ba8b6 is design + utility port (335 LOC utility + 8 test modules + wiring doc) src/phase_z2_verification_utils.py; docs/architecture/IMP-16-U2-WIRING-DESIGN.md (commit 23ba8b6)
16 #17 IMP-17 AI repair fallback infra (carve-out -- outside normal path) Design-only boundary + 3-cond AND gate (User GO AND B4 frame_selection evidence AND IMP-04/05 live); httpx + SSE + retry + JSON parse pattern reference Step 12 (AI position contract; carve-out body asserts normal path AI = 0); Step 16/17 fallback path (gated activation); feedback_ai_isolation_contract (foundational rule); backlog row + INSIGHT-MAP cross-ref Step 12 (design boundary); future activation gated by 3-cond AND OK -- design-only carve-out; src/phase_z2_pipeline.py change = 1 line (comment anchor for orchestrator test); no runtime AI added docs/architecture/IMP-17-CARVE-OUT.md + tests/orchestrator_unit/test_imp17_comment_anchor.py (commit e10ec36)
17 #18 IMP-18 I3 SVG coordinate reinforcement Doc-only carve-out -- SVG gap report; renderer._preprocess_svg_data pattern reference Step 0 frame_partials SVG geometry (reference); Phase R' (renderer.py) read-only doc consumers; backlog row OK -- doc-only (docs/architecture/IMP-18-SVG-GAP-REPORT.md + 1-line backlog status flip from pending to documented); no code touched docs/architecture/IMP-18-SVG-GAP-REPORT.md (commit cbbc163)
18 #45 (#15 execution-1) image_aspect_mismatch detection + runtime test Step 14 -- image_aspect_mismatch detection in visual_check; runtime test test_phase_z2_step14_image_check.py Step 15 fit_classification consumer; Step 21 debug.json event surfacing (delegated to #48); #15 parent close evidence Step 15 (consumer via classifier event); #47 (classifier integration) OK -- Step 14 detection only (no Step 15 wiring yet; delegated to #47). Test scope local. src/phase_z2_pipeline.py + tests/phase_z2/test_phase_z2_step14_image_check.py (commit e9b3d2e)
19 #46 (#15 execution-2) table overflow + element-identity dedup + Selenium test Step 14 -- table_self_overflow detection; element-identity dedup; Selenium integration test Step 14 dedup logic (must agree with image events from #45); Step 15 consumer (delegated to #47); #15 parent Step 15 (consumer); #47 Warning -- commit-message label drift only (Step 14 scope-discipline pattern itself matches #45). Commit 2827622 message reads feat(IMP-16): ..., which mis-labels the closing Gitea issue (actually closes #46 = #15 execution-2; IMP-16 backlog row is the verification utility carved out separately). Audit attribution corrected here; SHA 2827622 is the authoritative anchor. No code/contract leak; risk is record-keeping only. src/phase_z2_pipeline.py + tests/phase_z2/test_phase_z2_step14_table_check.py (commit 2827622)
20 #47 (#15 execution-3) classifier consumer (image + table) + pure-dict test Step 15 -- classifier consumes image+table events from Step 14; pure-dict test (no Selenium) Step 14 producers (#45 + #46); Step 15 CONTENT_TYPE_PATTERNS taxonomy; Step 16 router (consumer) Step 16; tests/phase_z2/test_phase_z2_visual_classifier.py OK -- classifier wiring with pure-dict tests isolates Step 15 from Selenium dependency; aligns Step 14 producer to Step 15 consumer (Axis 3 invariant -- to be re-verified in Section 5) src/phase_z2_classifier.py (commit 535c484)
21 #48 (#15 execution-4) debug.json event surfacing + spec doc trace + regression Step 21 debug.json event surfacing + PHASE-Z-FIT-CLASSIFIER-ROUTER-SPEC.md taxonomy row + regression test Step 21 trace schema (additive); spec doc; regression guard spec doc consumers; debug.json consumers (Front/, audit tooling) OK -- 3-line pipeline change + 2 test modules + 1-line spec doc row; smallest blast radius of #15 children src/phase_z2_pipeline.py + docs/architecture/PHASE-Z-FIT-CLASSIFIER-ROUTER-SPEC.md (commit 614c533)
22 #49 (#15 execution-5) final integration + parent close Verification-only -- re-uses #48 614c533 evidence; no new SHA per #15 body #15 parent close; integration-only #15 parent Warning -- close-timestamp anomaly (closed 2026-05-19T02:49:56+09:00, about 15 minutes AFTER #15 close 02:35:05+09:00). Verification-only path has no code change, so anomaly is administrative only; no contract leak. #15 body + 614c533 (re-used)

Section 3 finding summary

  • OK rows : #2 #3 #4 #5 #7 #8 #9 #10 #11 #13 #14 #16 #17 #18 #45 #47 #48 (17)
  • Warning rows : #6 (wide override blast radius, contained by tests); #12 (multi-donor + cascade, contained by 5 test modules); #15 (close-timestamp anomaly via #49); #46 (commit-message label drift only, SHA correct); #49 (close-timestamp anomaly, verification-only) -- 5 rows
  • Blocker rows : 0
  • Total : 17 OK + 5 Warning + 0 Blocker = 22 rows (matches 22 closed issues under audit).
  • De-dup audit : #15 row carries no code attribution; all code/test work attributed to #45-#48 (and #49 = verification-only). No double-count.

Section 3 cross-issue scope-myopia adjacency check

Adjacent-contract pairs flagged for Section 5 Axis 3 re-verification (producer to consumer continuity) :

  • #2 Step 2 normalize output -> #3 Step 3 content_object input
  • #3 content_object schema -> #8 sub_sections schema extension
  • #4 catalog expansion -> #5 V4 fallback candidate pool dedup
  • #4 catalog expansion -> #10-#11 min_height_px exposure
  • #9 layout vocabulary -> #12 retry zone-ratio donor selection
  • #9 layout vocabulary -> #11 Step 9 min_height v4-all-judgments test
  • #45 + #46 Step 14 events -> #47 Step 15 classifier -> Step 16 router
  • #48 debug.json event surfacing -> #21 (Step 21 debug consumer; open, excluded)
  • #17 AI carve-out -> #5 + #4 activation 3-cond AND gate (gated, not active)

Axis 3 (Section 5) will verify each pair has agreeing producer-line / consumer-line on the live code.


Section 4. Axis 2 -- 22 issues x 22 steps pipeline matrix

Split rationale : at u1 completion the combined REPORT was 21,070 bytes / 136 lines -- over the 10 KB readability threshold defined in the Stage 2 plan. Per the split rule (combined REPORT >= 10 KB grid moves to MATRIX.md + back-pointer), the 22 x 22 grid lives in the companion file :

  • docs/architecture/INTEGRATION-AUDIT-01-MATRIX.md

What MATRIX.md contains :

  • Step 0 precondition NOTE (NOT a grid column) -- 5 issues (#4 #11 #13 #14 #18) recorded with scope summary + evidence path.
  • 22 x 22 grid (Step 1 through Step 22 columns x 22 issue rows). Cell legend : P = primary touch, A = adjacent contract, . = no touch. ASCII-only.
  • Row footer (touched-step count) and column footer (touching-issue count + H HOTSPOT marker for col total >= 4).
  • HOTSPOT enumeration (9 steps : S2 S9 S13 S14 S15 S16 S17 S21 S22).
  • Cross-check against the 9 adjacent-contract pairs flagged in Section 3.
  • Empty / low-touch column notes (Step 4 and Step 11 are missing per PHASE-Z-PIPELINE-OVERVIEW -- 0 touches expected).
  • Parent/child de-dup sum check : #15 row carries 3 adjacencies and zero P cells ; only #45 #46 #47 #48 carry the 4 primary cells for the #15 family ; #49 is verification-only with all-A.

Section 4 summary (for readers staying in REPORT) :

  • 9 hotspot steps (col total >= 4) : Step 2 (4), Step 9 (6), Step 13 (5), Step 14 (9 highest), Step 15 (6), Step 16 (4), Step 17 (4), Step 21 (8), Step 22 (7).
  • 2 empty columns : Step 4 + Step 11. Consistent with master pipeline missing status -- no audit gap.
  • Total grid cells filled = 77 (row sum = col sum, cross-checked).
  • Top row-total issues : #6 (7), #5 (6), #8 (6), #12 (6) -- the 2 Warning rows (#6 #12) sit at the top, consistent with Section 3 wide-blast-radius finding.

Section 5. Axis 3 -- Cross-issue conflict per invariant category

Method : 6 invariant categories listed in the issue body. Per category, identify producer file:line, consumer file:line, the named state key / contract, the closed issues that touch it, agree-or-conflict verdict, plus grep evidence path. Categories are evaluated against the live tracked code at audit time, not against historical snapshots.

5.1 Invariant category roster (from issue body)

# category issue-body wording
C1 debug.json schema phase_z2 debug payload paths; no conflicting key type / semantics
C2 visual_check_passed src/phase_z2_pipeline.py Step 14 / 17; set-site <-> read-site agree
C3 fit_classification / router src/phase_z2_mapper.py + consumers; labels consistent producer -> consumer (charter mis-cite; live producer = src/phase_z2_classifier.py -- see §10 F-1)
C4 Step 14 / 17 / 21 interactions expected state values stay aligned across the trio
C5 Phase R vs Phase Z boundary no R regression, Z additions don't leak into R
C6 template / catalog / frame count all docs / code use same numbers (family = 13)

5.2 Producer / consumer / agreement table

C# invariant key producer (file : line) consumer(s) (file : line) touching closed issues verdict grep evidence
C1 per-step JSON schema = step_num, step_name, step_status, pipeline_path_connected, input, output, note, data (locked) src/phase_z2_pipeline.py:2593 _write_step_artifact definition; locked schema docstring at 2605-2611 (Locked schema lines 2607-2610) every step writer in src/phase_z2_pipeline.py -- 24 call sites at lines 2782, 2812, 2857, 2934, 3184, 3619, 3652, 3674, 3793, 3804, 3826, 3881, 4056, 4308, 4481, 4507, 4527, 4549, 4658, 4677, 4688, 4706, 4761, 4780; Front/ reads data/runs/.../steps/*.json; audit tooling #2 step02_*; #3 content_objects; #5 v4_fallback_summary + selection_paths + fallback_selection_count; #6 render_records; #11 min_height_px payload; #48 image_events / table_events event surfacing AGREE -- all step writers go through the single _write_step_artifact site with the locked field set; additive data payload only; no conflicting key types observed Grep _write_step_artifact src/phase_z2_pipeline.py = 1 definition (line 2593) + 24 call sites = 25 total occurrences (all 24 call sites enumerated in consumer column); all share the same _write_step_artifact(run_dir, step_num, name, data, *, step_status, pipeline_path_connected, inputs, outputs, note) kwargs surface
C2 visual_check_passed: bool set at Step 14 / read at Step 17 src/phase_z2_classifier.py:495 visual_check_passed = bool(overflow.get("passed", False)) and not classifications returned at 497 src/phase_z2_router.py:128 if fit_classification.get("visual_check_passed", True): ... router_active = False; src/phase_z2_pipeline.py:2560 sets slide_status["visual_check_passed"] = visual_passed; pipeline summary reads at 4800, 4804, 4830 #15 parent; #45 (image_events flip the flag); #46 (table_events flip the flag); #47 (classifier widens semantic to passed AND no classifications) AGREE -- single set-site (classifier.py:495) + slide_status mirror (pipeline.py:2560); router.py:128 + pipeline.py:4800/4804/4830 read the same key. Default .get(..., True) at router.py:128 is safe because absent key = no classification = pass Grep visual_check_passed src = 14 hits across classifier.py + router.py + pipeline.py -- producer / consumer line set matches
C3 fit_classification dict keys = visual_check_passed, classifications, summary, categories_seen, unclassified_signals, placement_diagnostics; classifier <-> router consumer src/phase_z2_classifier.py:496-506 classify_visual_runtime_check return dict src/phase_z2_router.py:109 route_fit_classification(fit_classification); src/phase_z2_pipeline.py:4524 fit_classification = classify_visual_runtime_check(overflow, debug_zones); pipeline re-classify after retry at 4582 / 4643; router decision call at 4540 / 4583 / 4644; retry consumer src/phase_z2_retry.py:47 reads fit_classification #5 (V4 fallback PASS_WITH_FALLBACK semantics); #12 (retry router multi-donor + cascade); #15 parent; #47 (classifier feed); #48 (debug surfacing) AGREE -- producer key set is the exact set consumed downstream. NOTE : the issue body says src/phase_z2_mapper.py for invariant C3, but the live producer is src/phase_z2_classifier.py (mapper.py owns slot payload, not fit classification). This is a record-keeping mismatch in the issue body, not a code conflict. Recorded as Section 10 follow-up candidate F-1 Grep fit_classification src = 30 total occurrences across 4 files (classifier.py 3 hits incl. docstring/comments; pipeline.py 20 hits; router.py 5 hits; retry.py 2 hits). Active code use sites = producer at classifier.py:497; consumers at router.py:128 / 139 + pipeline.py 2732 / 4524 / 4540 / 4571 / 4582 / 4583 / 4643 / 4644 / 4754 / 4804 / 4805 + retry.py:47 / 67. Remaining occurrences are imports / function-parameter declarations / docstring references
C4 Step 14 visual_check overflow events (image_events, table_events, passed) -> Step 15/16 (fit + router) -> Step 17 retry action -> Step 21 debug surface Step 14 emit sites src/phase_z2_pipeline.py:2236 (image_events), 2282 (table_events), 2367 / 2386 (aggregation); Step 15 classifier consumes both event lists at src/phase_z2_classifier.py:429 / 453; Step 16 router at src/phase_z2_router.py:142; Step 17 retry orchestration at src/phase_z2_pipeline.py:4571 / 4583 / 4644; Step 21 trace producer at src/phase_z2_pipeline.py:4762-4777 (step21_debug_index.json + debug.json outputs) Step 21 debug.json index reader (Front/ + audit tooling); pipeline summary 4791-4841 #12 (retry cascade Step 17 multi-donor + glue + font compression); #15 / #45 / #46 / #47 / #48 (Step 14 producer / Step 15 classifier consumer / Step 21 surface); #10 filtered_section_reasons (Step 22 read-only, Step 21 source) AGREE with one DOCUMENTED PARTIAL -- Step 21 writer at pipeline.py:4772 is step_status="partial" with note region marker partial 미주입 -- Step 21 ⚠ partial. This is an acknowledged partial state recorded in trace, not a contract conflict between issues. Recorded as Section 6 status row Grep step_num.*=.*21|outputs.*debug\.json src/phase_z2_pipeline.py = single producer at line 4762-4777
C5 Phase R' (src/renderer.py, src/content_editor.py, src/html_validator.py, src/block_selector.py) <-> Phase Z (src/phase_z2_*.py) module boundary; no cross-import src/phase_z2_pipeline.py (Phase Z entry) has zero imports of Phase R' modules; verified via Grep "from renderer|import renderer|from phase_q|from src\.renderer" src/phase_z2_pipeline.py = No matches found inverse direction src/renderer.py and src/block_selector.py have zero references to phase_z2; verified via Grep phase_z2 src/renderer.py = 0 and Grep phase_z2 src/block_selector.py = 0 #13 (build-time frame preview generator, scripts/ only); #14 (slide-base iframe mode -- Phase Z only); #16 (verification utility for Phase Z, no Phase R coupling); #17 (AI carve-out, design-only no R coupling); #18 (SVG gap report doc-only) AGREE -- boundary clean both directions for the closed-issue scope. No Phase R' regression observed; Phase Z additions stay in phase_z2_*.py modules Grep results above
C6 family templates count vs frame_contracts.yaml count (= 11 in tracked baseline); docs cite "family = 13" including 2 in-progress untracked files templates/phase_z2/families/*.html tracked = 11 (git ls-files templates/phase_z2/families/ produces 11 entries); templates/phase_z2/catalog/frame_contracts.yaml top-level entries = 11 (grep -cE "^[a-z_]+:$" = 11) src/phase_z2_mapper.py PAYLOAD_BUILDERS / ITEM_PARSERS / COLUMN_BODY_PARSERS registries (mapper.py:10-16 docstring + 262 / 306 / 332 / 369 / 414 / 424 / 471 registry sites); render surface templates/phase_z2/families/*.html #4 (16 frame_partials + F17 paired_rows_4x2 schema + theme); #5 (V4 fallback candidate pool dedup); #13 (frame preview generator); #18 (SVG gap report cites families/*.html (13)) AGREE FOR TRACKED BASELINE -- 11 tracked family templates <-> 11 frame_contracts entries. SURFACE NOTE : 2 untracked WIP family templates (app_sw_package_vs_solution.html, pre_construction_model_info_stacked.html) exist on disk but are NOT in any closed issue and NOT yet contracted. IMP-18 doc "families/*.html (13)" is forward-looking, includes the 2 WIP files. No closed-issue contract is broken; documentation drift is recorded as Section 10 follow-up candidate F-2 git ls-files templates/phase_z2/families/ = 11; ls templates/phase_z2/families/*.html = 13 (2 untracked); grep -cE "^[a-z_]+:$" frame_contracts.yaml = 11

5.3 Cross-issue adjacency continuity (Section 3 pairs re-verified)

Section 3 adjacent pair invariant carrying the contract live continuity verdict
#2 Step 2 normalize -> #3 Step 3 content_object input C1 (debug.json step02_* + content_objects) OK -- additive payload, schema preserved via _write_step_artifact
#3 content_object schema -> #8 sub_sections schema C1 + C4 (alias resolver state) OK -- alias resolver covers 4 lookup sites (REPORT Section 3 row #8 evidence)
#4 catalog -> #5 V4 fallback dedup C3 + C6 (frame count + classifier consumer) OK -- candidates[0] backward-compat verified by tests/test_catalog_invariant.py (REPORT Section 3 row #5)
#4 catalog -> #10 / #11 min_height_px exposure C1 + C6 OK -- min_height_px is additive read-only field
#9 layout vocabulary -> #12 retry donor selection C3 + C4 (Step 17 cascade) OK -- multi-donor cross-zone state lives inside Step 17 retry; spacing direction matches feedback_phase_z_spacing_direction (no common-shrink)
#9 layout vocabulary -> #11 Step 9 min_height v4-all-judgments C6 OK -- guarded by tests/test_phase_z2_step9_v4_all_judgments_min_height.py
#45 + #46 Step 14 events -> #47 Step 15 classifier -> Step 16 router C2 + C3 + C4 OK -- live trace image_events / table_events enter classifier at classifier.py:429 / 453, flow into router at router.py:142
#48 debug.json event surfacing -> #21 (open, excluded) C1 OK for closed scope -- open consumer #21 is outside audit window
#17 AI carve-out -> #5 / #4 activation 3-cond AND gate C5 (boundary not yet crossed) OK -- gate is closed (User GO AND B4 frame_selection evidence AND IMP-04/05 live); no normal-path AI active

5.4 Axis 3 summary

  • 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. 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.
  • 0 Blocker findings in Axis 3.

5.5 Live-grep re-verification stamp (audit date 2026-05-19)

All numerical claims in Section 5.2 re-verified against live source on the audit date. Commands and results :

Claim Command Live result Status
C1 producer + consumer count Grep _write_step_artifact src/phase_z2_pipeline.py -n 1 definition (pipeline.py:2593) + 24 call sites at lines 2782, 2812, 2857, 2934, 3184, 3619, 3652, 3674, 3793, 3804, 3826, 3881, 4056, 4308, 4481, 4507, 4527, 4549, 4658, 4677, 4688, 4706, 4761, 4780 = 25 total occurrences MATCH (Section 5.2 C1 row already lists all 24 call sites)
C2 consumer scan Grep visual_check_passed src 14 hits across 3 files (classifier.py:5, pipeline.py:6, router.py:3) MATCH (Section 5.2 C2 row says "14 hits across classifier.py + router.py + pipeline.py")
C3 consumer scan Grep fit_classification src 30 hits across 4 files (classifier.py:3, pipeline.py:20, retry.py:2, router.py:5) MATCH (Section 5.2 C3 row says "30 total occurrences across 4 files")
C6 family templates -- tracked git ls-files templates/phase_z2/families/ 11 entries MATCH (Section 5.2 C6 row says "tracked = 11")
C6 family templates -- on disk `ls templates/phase_z2/families/*.html wc -l` 13 files (11 tracked + 2 WIP untracked : app_sw_package_vs_solution.html, pre_construction_model_info_stacked.html)
C6 frame_contracts entries grep -cE "^[a-z_]+:$" templates/phase_z2/catalog/frame_contracts.yaml 11 MATCH (Section 5.2 C6 row says "= 11")

No discrepancy between Section 5.2 grep evidence and live code. Re-verification re-confirms u3 Axis 3 conclusion : 6 invariant categories all AGREE; 2 record-keeping follow-up candidates (F-1, F-2); 1 documented partial (Step 21); 0 Blocker findings.


Section 6. Axis 4 -- Backlog vs code reality status matrix

Method : per closed issue, compare (a) docs/architecture/PHASE-Z-IMPLEMENTATION-ISSUE-BACKLOG.md status column at audit time (live read 2026-05-19), (b) live src/ + templates/ + tests/ + docs/ evidence (grep hits + file existence), (c) the audit-allowed status enum implemented | documented (deferred) | pending, (d) mismatch flag.

Per issue-body rule set :

  • implemented -> live grep on src/** MUST show wired call site(s); not just a single declaration with no consumer.
  • documented (deferred) -> live grep on src/** MUST NOT show a production code path that assumes the feature is active (carve-out only).
  • pending -> live grep on src/** MUST NOT show wired implementation (or evidence shows incomplete).
  • pending -> documented flip -> reason cited in backlog row must match what src/** actually contains.

6.1 Backlog status legend (live read on audit date)

backlog status IMP rows under audit meaning
documented IMP-18 (1 row) doc-only carve-out, no production path
pending IMP-02 through IMP-17 (16 rows) backlog status column has NOT been flipped, despite Gitea issue being closed
(no backlog row) #45 / #46 / #47 / #48 / #49 (5 rows) execution children of #15; backlog tracks the parent IMP-15 only -- and IMP-15 is itself still marked pending in §2 row

Headline Axis 4 finding : PHASE-Z-IMPLEMENTATION-ISSUE-BACKLOG.md status column is stale across the entire closed-issue audit scope -- 16 of 22 audited issues are flagged BACKLOG_STALE (backlog pending vs Gitea closed + live code wired); additionally 5 of 22 carry NO_BACKLOG_ROW for the #15 execution children (#45-#49), and only 1 of 22 (#18) is AGREE. Reconciliation: 16 BACKLOG_STALE + 5 NO_BACKLOG_ROW + 1 AGREE = 22 (matches Section 6.3 summary and the 15+1=16 flip plan in §6.3 follow-up reference). This is documentation drift, not a code-side contract conflict; recorded as Section 10 follow-up candidate F-3.

6.2 Axis 4 -- 22 row backlog vs code reality matrix

Status meaning (audit verdict column) :

  • implemented_live = backlog should be flipped to implemented; live src/ wiring proves it (grep evidence below).
  • documented_live = backlog documented matches code reality (doc-only carve-out; no prod path).
  • child_of_parent = no backlog row by design (execution child of parent IMP-15); status tracked via parent row.

Mismatch flag :

  • BACKLOG_STALE = backlog says pending but code is wired live. Documentation drift only; no code conflict.
  • AGREE = backlog status matches live code reality.
  • NO_BACKLOG_ROW = execution child, child not represented in backlog; not an error, but parent IMP-15 row is itself stale.
# issue (title) backlog status (live read) audit verdict mismatch flag live grep evidence
1 #2 IMP-02 A-1 Stage 0 normalize chained adapter pending (§1 row 2) implemented_live BACKLOG_STALE Grep "normalize_mdx_content|extract_major_sections|extract_conclusion_text" src/ = 24 hits across 6 files (mdx_normalizer.py, phase_z2_content_extractor.py, phase_z2_pipeline.py 9 hits, pipeline.py, pipeline_v2.py, section_parser.py); commit bac13c0 +165/-3
2 #3 IMP-03 A-1 popup/image/table trace pending (§1 row 3) implemented_live BACKLOG_STALE src/phase_z2_content_extractor.py file exists (Glob hit); commit fc3f7d8
3 #4 IMP-04 A-2 catalog expansion pending (§1 row 4) implemented_live BACKLOG_STALE git ls-files templates/phase_z2/families/ = 11 tracked; frame_contracts.yaml top-level entries = 11; commit 73a98b8 corrects F17 schema; matches Axis 3 C6
4 #5 IMP-05 A-5 V4 fallback pending (§1 row 5) implemented_live BACKLOG_STALE Grep "PASS_WITH_FALLBACK|v4_fallback|fallback_selection" src/ = 28 hits in phase_z2_pipeline.py; commits 15c5b9a, 21476ae, 23d1b25
5 #6 IMP-06 B-1 Zone-section override pending (§1 row 6) implemented_live BACKLOG_STALE Grep "replaced_auto_unit|render_records|zone_section_override" src/ = 33 hits in phase_z2_pipeline.py; commits d596fab / b81e564 / 1f15495 / 52ccb7f
6 #7 IMP-07 B-2 edited HTML to MDX reverse path pending (§1 row 7) implemented_live BACKLOG_STALE Front/client/src/services/designAgentApi.ts file exists (Glob hit); commit 0f0d3fa
7 #8 IMP-08 B-3 sub-section drag-drop pending (§1 row 8) implemented_live BACKLOG_STALE Grep "sub_sections|sub_section_id|subsection_alias" src/ = 14 hits across block_assembler.py (12) + phase_z2_pipeline.py (2); commits a422d72 / 5191aca / ab2764c / 8f6cffc
8 #9 IMP-09 B-4 non-default layout zone-geometry pending (§1 row 9) implemented_live BACKLOG_STALE Grep "build_layout_css|preset_layout|zone_geometry" src/ = 11 hits in phase_z2_pipeline.py; commits 201099e / 1fb9732
9 #10 IMP-10 D-1 filtered_section_reasons UI pending (§1 row 10) implemented_live BACKLOG_STALE Grep "filtered_section_reasons" Front/ = 4 hits (Home.tsx, designAgentApi.ts); + src/phase_z2_pipeline.py 6 hits (read-only consumer); commit 0fb168b +45 lines
10 #11 IMP-11 D-2 Frame min_height display pending (§1 row 11) implemented_live BACKLOG_STALE Grep "min_height_px" src/ = 50 hits across 6 files (block_reference.py, block_selector.py, fit_verifier.py, phase_z2_pipeline.py 21 hits, phase_z2_retry.py, space_allocator.py); + Front/ 21 hits across 7 files including SlideCanvas.tsx (8); commit a79bd8b
11 #12 IMP-12 Step 16/17 retry refinement pending (§2 row 12 IMP-12) implemented_live BACKLOG_STALE Grep "phase_z2_failure_router|phase_z2_retry|redistribute|font_compression" src/ = 63 hits across 7 files (incl. phase_z2_failure_router.py 17, phase_z2_retry.py 16, phase_z2_router.py 6, phase_z2_pipeline.py 17); commit 56619a0
12 #13 IMP-13 A-3 frame preview consistency pending (§2 row 13) implemented_live BACKLOG_STALE scripts/generate_frame_previews.py file exists (Glob hit); build-time only (scripts/, not runtime src/) -- matches documented (deferred) semantics for runtime path but verdict here = implemented_live because the script is the deliverable per issue body; commit 7d5639a
13 #14 IMP-14 A-4 slide-base iframe mode pending (§2 row 14) implemented_live BACKLOG_STALE templates/phase_z2/slide_base.html file exists (Glob hit); Grep "slide_base|embedded_mode|standalone_mode" src/ = 25 hits across 5 files (incl. block_assembler.py 8, phase_z2_pipeline.py 11); commit 7a52ceb
14 #15 IMP-15 Step 14 visual_check reinforcement (PARENT) pending (§2 row 15) implemented_live (via children #45-#49) BACKLOG_STALE parent integration only; live code attribution belongs to child rows below (Stage 1 de-dup rule). All 4 child SHAs present in repo (e9b3d2e / 2827622 / 535c484 / 614c533)
15 #16 IMP-16 B-2 verification helper axis pending (§2 row 16) implemented_live BACKLOG_STALE src/phase_z2_verification_utils.py file exists (Glob hit); docs/architecture/IMP-16-U2-WIRING-DESIGN.md exists; commit 23ba8b6
16 #17 IMP-17 AI repair fallback infra (carve-out) pending (§2 row 17) documented_live BACKLOG_STALE (status semantics) docs/architecture/IMP-17-CARVE-OUT.md file exists (Glob hit); src/ runtime AI = 0 (verified Axis 3 C5 boundary); 3-cond AND gate closed; commit e10ec36 -- 1 line in src/phase_z2_pipeline.py is comment anchor only, not a runtime path. Mismatch FLAG semantics : backlog says pending, but reality = documented (deferred). The flag is BACKLOG_STALE with status-class shift, distinguished from rows above.
17 #18 IMP-18 I3 SVG coordinate reinforcement documented (§2 row 18) documented_live AGREE docs/architecture/IMP-18-SVG-GAP-REPORT.md file exists (Glob hit); pure doc carve-out; no src/** touched; commit cbbc163 -- the ONLY closed audited issue whose backlog status already reflects code reality
18 #45 (#15 execution-1) image_aspect_mismatch detection no backlog row child_of_parent NO_BACKLOG_ROW tests/phase_z2/test_phase_z2_step14_image_check.py file exists (Glob hit); Grep "image_aspect_mismatch" src/ = 6 hits across phase_z2_classifier.py (2 : lines 426, 435) + phase_z2_pipeline.py (4 : lines 131, 2236, 2367, 4517); commit e9b3d2e
19 #46 (#15 execution-2) table_self_overflow detection no backlog row child_of_parent NO_BACKLOG_ROW tests/phase_z2/test_phase_z2_step14_table_check.py file exists (Glob hit); Grep "table_self_overflow" src/ = 3 hits all in phase_z2_pipeline.py (lines 136, 2282, 2386); commit 2827622 (commit-message label drift feat(IMP-16) flagged in Section 3 row 19)
20 #47 (#15 execution-3) classifier consumer (image + table) no backlog row child_of_parent NO_BACKLOG_ROW tests/phase_z2/test_phase_z2_visual_classifier.py file exists (Glob hit); Grep "classify_visual_runtime_check|CONTENT_TYPE_PATTERNS" src/ = 8 hits across phase_z2_classifier.py (4) + phase_z2_pipeline.py (4); commit 535c484
21 #48 (#15 execution-4) debug.json event surfacing + spec doc + regression no backlog row child_of_parent NO_BACKLOG_ROW Grep "step21_debug_index|step21_debug" src/ = 1 hit (phase_z2_pipeline.py); docs/architecture/PHASE-Z-FIT-CLASSIFIER-ROUTER-SPEC.md has taxonomy row (Section 3 row 21 evidence); commit 614c533; Axis 3 C4 confirms image_events / table_events end-to-end
22 #49 (#15 execution-5) final integration + parent close no backlog row child_of_parent (verification-only) NO_BACKLOG_ROW + close-timestamp anomaly (recorded Section 3 row 22) verification-only per #15 body; no new SHA; re-uses 614c533 evidence; no fresh grep needed

6.3 Axis 4 summary

  • BACKLOG_STALE rows : #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 #16 #17 = 16 rows (status column reads pending but live code is wired; for #17 the right target status is documented (deferred) while for the other 15 it is implemented).
  • AGREE rows : #18 = 1 row (the only issue whose backlog status truthfully reflects code reality).
  • NO_BACKLOG_ROW rows : #45 #46 #47 #48 #49 = 5 rows (execution children, by-design no backlog row; parent IMP-15 row exists but is itself BACKLOG_STALE).
  • Total : 16 + 1 + 5 = 22 rows (matches 22 closed issues under audit).
  • Implementation-vs-documented split (audit verdict, ignoring backlog wording) :
    • implemented_live (runtime path wired) : #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15(via children) #16 = 15 rows
    • documented_live (doc-only / design-only carve-out, no runtime path) : #17 #18 = 2 rows
    • child_of_parent (no backlog row, attribution via parent) : #45-#49 = 5 rows
  • 0 Blocker findings in Axis 4. No closed issue is pending and unimplemented; the only mismatches are documentation drift in the backlog status column.
  • Cross-axis consistency :
    • Axis 3 C6 frame count 11 tracked / 11 contract entries / 13 on disk (2 WIP) matches the IMP-04 evidence in Axis 4 row 3 (BACKLOG_STALE but live code present).
    • Axis 3 C5 boundary (Phase R' <-> Phase Z) clean both ways re-confirms #17 #18 as documented_live (no R' leak).
    • Axis 1 (Section 3) Warning rows #6 #12 #15 #46 #49 are all still implemented_live in Axis 4 -- the warnings are about blast radius and administrative drift, not implementation absence.
  • Follow-up candidate F-3 (Section 10) : PHASE-Z-IMPLEMENTATION-ISSUE-BACKLOG.md status column needs a sweep to flip 15 rows pending -> implemented, 1 row pending -> documented (deferred) for IMP-17, and either add child-row stubs for #45-#49 or add a footnote on the IMP-15 row pointing at the 5 execution children. This is a single-file documentation-only edit; orthogonal to source-code Stage 3 work; safe under audit-only scope (deferred to a separate follow-up issue, NOT this audit's u7 backlog row).

Section 7. Representative pipeline runs

Method : run the Phase Z runtime entry (python -m src.phase_z2_pipeline <mdx_path> <run_id>) on the two locked samples (samples/mdx_batch/03.mdx smoke + samples/mdx_batch/04.mdx details+images). Per run capture (from data/runs/<run_id>/phase_z2/debug.json) : top-level keys, slide_status.visual_check_passed, slide_status.overall, zone count, per-zone frame template + slot keys + slot key count, slide_status.visual_fail_reasons, slide_status.filtered_section_reasons, selection_paths, image_events / table_events count. Compare invariants across both runs.

Audit date : 2026-05-19. Both runs are fresh on this audit pass (run_ids audit50_run_03_smoke + audit50_run_04_details).

7.1 Run #1 -- samples/mdx_batch/03.mdx (smoke baseline)

field value
run_id audit50_run_03_smoke
MDX title parsed DX 실행 체계 구축 방안
sections parsed 2 (03-1, 03-2)
layout preset horizontal-2 (composition v0 count-based)
mode composition_v0_layout_8preset
debug.json top-level keys composition_planner_debug, fit_classification, image_events, layout_css, layout_preset, mode, mode_note, mvp1_allowed_statuses, retry_trace, router_decision, slide_status, table_events, v4_label_to_phase_z_status, v4_source, visual_runtime_check, zone_geometries_px, zones (17 keys)
slide_status.visual_check_passed True
slide_status.full_mdx_coverage True
slide_status.rendered True
slide_status.overall PASS
slide_status.visual_fail_reasons [] (empty)
slide_status.filtered_section_reasons [] (empty)
slide_status.fallback_selection_count 0
fit_classification.visual_check_passed True (mirrors slide_status)
fit_classification.classifications []
fit_classification.categories_seen []
router_decision.action None (no retry path triggered)
image_events count 0
table_events count 0
zone count 2
zone[0] (top) template three_parallel_requirements (frame 13), contract three_parallel_requirements, label use_as_is, slot keys ['pillars', 'title'] (2), sections ['03-1'], height_px=228, width_px=1180
zone[1] (bottom) template process_product_two_way (frame 29), contract process_product_two_way, label use_as_is, slot keys ['banner_left', 'banner_right', 'process', 'product', 'title'] (5), sections ['03-2'], height_px=343, width_px=1180
selection_paths both rank_1 (no fallback)
fail / overflow events none

7.2 Run #2 -- samples/mdx_batch/04.mdx (details + images)

field value
run_id audit50_run_04_details
MDX title parsed DX 지연 요인
sections parsed 2 (04-1, 04-2)
sections aligned 3 (04-1, 04-2-sub-1, 04-2-sub-2) -- IMP-08 sub_section schema active
layout preset single (composition v0 count-based; only 1 unit survived filtering)
mode composition_v0_layout_8preset
debug.json top-level keys identical 17 keys as Run #1 (composition_planner_debug, fit_classification, image_events, layout_css, layout_preset, mode, mode_note, mvp1_allowed_statuses, retry_trace, router_decision, slide_status, table_events, v4_label_to_phase_z_status, v4_source, visual_runtime_check, zone_geometries_px, zones)
slide_status.visual_check_passed True
slide_status.full_mdx_coverage False
slide_status.rendered True (partial artifact -- viable units only)
slide_status.overall PARTIAL_COVERAGE
slide_status.visual_fail_reasons [] (visual side OK; coverage failure is upstream of visual_check)
slide_status.filtered_section_reasons [] (filtering recorded via selection_paths chain_exhausted / no_v4_candidate, not via filtered_section_reasons)
slide_status.fallback_selection_count 0
fit_classification.visual_check_passed True
fit_classification.classifications []
fit_classification.categories_seen []
router_decision.action None
image_events count 0
table_events count 0
zone count 1 (single preset)
zone[0] (primary) template bim_issues_quadrant_four (frame 16), contract bim_issues_quadrant_four, label light_edit, slot keys ['quadrant_1_body', 'quadrant_1_label', 'quadrant_2_body', 'quadrant_2_label', 'quadrant_3_body', 'quadrant_3_label', 'quadrant_4_body', 'quadrant_4_label', 'title'] (9), sections ['04-2-sub-2'], height_px=585, width_px=1180
selection_paths 04-1=chain_exhausted, 04-2-sub-1=chain_exhausted, 04-2-sub-2=rank_1, 04-2=no_v4_candidate
fail / overflow events none

7.3 Cross-run invariants

invariant run #1 (03.mdx) run #2 (04.mdx) verdict
debug.json top-level key set 17 keys (above) identical 17 keys AGREE -- Step 21 schema stable across both runs (Axis 3 C1)
slide_status schema keys 19 keys (visual_check_passed, full_mdx_coverage, rendered, overall, visual_fail_reasons, filtered_section_ids, filtered_section_reasons, aligned_section_ids, covered_section_ids, adapter_needed_count, adapter_needed_units, content_truncated_count, content_truncated_units, fallback_selection_count, fallback_selections, fallback_used, selection_path, selection_paths, note) identical 19 keys AGREE -- slide_status surface stable (Axis 3 C2 + C4)
fit_classification shape {visual_check_passed, classifications, summary, categories_seen, ...} -- matches Axis 3 C3 row identical shape AGREE -- classifier output schema invariant
visual_check_passed semantic True AND classifications=[] -> overall PASS True AND classifications=[] AND full_mdx_coverage=False -> overall PARTIAL_COVERAGE AGREE -- visual side passing under both runs; PARTIAL_COVERAGE is composition-planner side (upstream of Step 14), so visual_check_passed does NOT contradict overall status (Axis 3 C2 verdict re-confirmed)
zone count vs layout preset horizontal-2 -> 2 zones (top + bottom) single -> 1 zone (primary) AGREE -- preset-to-zone arity matches IMP-09 B-4 vocabulary (Axis 1 row 8)
frame contract resolution both zones resolved to a contract id (rank_1 path) only 1 of 4 selection paths resolved (3 chain_exhausted / no_v4_candidate) DIFF EXPECTED -- 04.mdx exhibits v4 candidate gap; this is the composition-planner maturity gap (not in any closed-issue scope). Not a contract conflict.
image_events / table_events arity both = 0 both = 0 AGREE -- neither sample triggers Step 14 image/table self-overflow; #45 image_aspect_mismatch and #46 table_self_overflow event-arrays exist in the schema and are correctly empty when no overflow is detected
router action triggered None None AGREE -- Step 16 router is dormant when classifications=[] (Axis 3 C3 verdict re-confirmed; #12 retry cascade not exercised by these samples)
pipeline final banner PASS (full MDX coverage + visual OK) PARTIAL_COVERAGE (visual OK + composition-planner filter) self-honest status naming per feedback_artifact_status_naming

7.4 Run-level findings

  • Both runs pass the visual_check axis (Axis 3 C2 contract). visual_check_passed=True agrees between fit_classification and slide_status mirror in both runs.
  • 04.mdx PARTIAL_COVERAGE is a composition-planner side filter (3 sections drop to chain_exhausted / no_v4_candidate before reaching Step 14). This is NOT an audit Blocker because (a) no closed issue under audit targets composition-planner coverage, (b) the status field is self-honestly named PARTIAL_COVERAGE rather than misnamed PASS (matches feedback_artifact_status_naming).
  • Step 21 debug.json writer surfaces a stable 17-key top-level surface across both runs; Axis 3 C1 invariant re-confirmed at runtime.
  • Zero Blocker findings in Section 7.

Section 8. Anti-hardcoding grep checklist

Method : run the 6 anti-hardcoding patterns enumerated in the Issue #50 body. For each, capture the live hit set, classify hits (Phase Z scope vs. legacy Phase R'/Q out-of-scope vs. docstring/comment vs. test fixture), then return a verdict. Raw output preserved at D:\ad-hoc\kei\design_agent\.orchestrator\tmp\50_grep_checklist_raw.txt (evidence-only, not staged for commit per Stage 3 directive).

Audit date : 2026-05-19. Searched against tracked source on this date.

8.1 Checklist

# pattern (issue body) expected live hit count (src/) hit classification verdict
G1 grep -E 'if .* == ["'\\''].*\.mdx' src/ 0 hits 0 none PASS
G2 grep -E 'OVERRIDES\s*=\s*\{' src/ each match sample-agnostic 0 none PASS (vacuously sample-agnostic)
G3 grep -E '재구성|건설산업 DX|BIM' src/ -- sample text leak 0 hits 31 source hits across 14 .py files (binary .pyc matches ignored) (a) 20 hits in legacy Phase R'/Q files (block_assembler_b2.py 1, block_matcher_tfidf.py 1, block_reference.py 3, content_editor.py 3, design_director.py 2, design_tokens.py 1, fit_verifier.py 1, frame_extractor.py 1, kei_client.py 4, pipeline.py 3) -- pre-Phase-Z; not in audit window; (b) 11 hits in Phase Z files (phase_z2_content_extractor.py 7 -- all inside if __name__ == "__main__" self-test data blocks at lines 466/493/511/556/565/573/591; phase_z2_failure_router.py:123 1 -- internal taxonomy string "topology 부터 재구성. frame_reselect 는 그 다음 단계"; phase_z2_mapper.py:519/529 2 -- docstring examples; phase_z2_retry.py:59 1 -- docstring). Per-file count sum = 20 + 11 = 31, matching the live total. PASS for the audit scope -- 0 closed-issue (#2-#18 + #45-#49) introduces new sample-specific hardcoded BIM/재구성/건설산업 string literals into runtime code paths. All 11 Phase Z hits are docstring/taxonomy/self-test fixtures, none injected into runtime contracts. Legacy 20 hits are out of audit window. Recorded as Section 10 follow-up candidate F-4 for future cleanup (doc-only, optional).
G4 grep -E 'height\s*=\s*720|aspect\s*=\s*0\.5' src/ -- magic literal pinning 0 hits 0 none PASS
G5 sample paths come from CLI args / config, not hardcoded sample-agnostic 4 occurrences across 2 files (src/block_assembler.py:1390/1393 + src/image_utils.py:62/65) all 4 hits use samples/mdx_batch as one of several generic asset search directories alongside samples/images (image asset discovery fallback). The directory is treated as a discovery namespace, not as a path to a specific MDX file. CLI entry (src/phase_z2_pipeline.py:4861) takes mdx_path as positional arg -- no hardcoded MDX path on the runtime entry. PASS -- sample-agnostic asset discovery default; not a per-sample pin.
G6 tests/ : sample-specific fixtures only under tests/fixtures/, not in production pipeline fixtures isolated tests/fixtures/ directory does not exist; closest hits = tests/phase_z2/test_pz2_vu_integration.py:6, 82 referencing samples/mdx_batch/02.mdx as smoke-coverage MDX the references in test_pz2_vu_integration.py are inside a verification-utility integration test (#16 IMP-16 scope). The test file is named with the test prefix and lives in tests/phase_z2/, so pytest discovery treats it as a test, not as a production module. No production pipeline file imports a sample MDX path literal. PASS WITH NOTE -- no tests/fixtures/ directory exists today; the existing integration tests already keep sample references inside tests/phase_z2/test_*.py, which discharges the spirit of the rule. Optional follow-up: formalize a tests/fixtures/ directory if sample inventory grows. Recorded as Section 10 follow-up candidate F-5 (low priority, doc-only).

8.2 Anti-hardcoding verdict

  • 4 patterns PASS cleanly with 0 hits (G1, G2, G4) and 1 PASS with sample-agnostic hits (G5).
  • 1 pattern PASS-for-audit-scope with classification (G3) : 11 Phase Z hits are all docstrings/taxonomy/self-test fixtures; 20 legacy hits are out of the 22-closed-issue audit window. Per-file counts sum to 31, matching the live grep total. No closed issue introduces new hardcoded sample text into a runtime code path.
  • 1 pattern PASS WITH NOTE (G6) : tests/fixtures/ directory not yet established; existing integration test references stay inside tests/phase_z2/. Already aligned with the spirit of the rule.
  • 0 Blocker findings in Section 8.
  • Cross-axis : the F-4 / F-5 follow-up candidates are doc-only optional cleanup; they do not alter any closed-issue contract.

Section 9. Final decision

Decision : CONDITIONAL GO for #19.

9.1 Summary across all 4 audit axes + supporting sections

section axis Blocker Warning OK follow-up candidates
§3 Axis 1 -- scope myopia 0 5 (#6 #12 #15 #46 #49) 17 none Blocker; warnings are blast-radius + administrative drift
§4 + MATRIX.md Axis 2 -- 22 x 22 pipeline matrix 0 (9 hotspot steps, 2 expected-empty cols) 22 issues mapped none Blocker; hotspots match expected Step 14 / 21 attention
§5 Axis 3 -- cross-issue conflict (6 invariants) 0 0 6 categories AGREE F-1 (body cites mapper.py; live producer is classifier.py for fit_classification); F-2 (13 family templates on disk vs. 11 tracked / contracted -- 2 WIP outside any closed issue)
§6 Axis 4 -- backlog vs code reality 0 -- 1 AGREE, 16 BACKLOG_STALE (doc drift), 5 NO_BACKLOG_ROW (by design for #45-#49) F-3 (backlog status sweep : flip 15 rows pending -> implemented, 1 row pending -> documented (deferred) for IMP-17, footnote IMP-15 row with 5 children)
§7 representative runs (03.mdx + 04.mdx) 0 -- both runs visual_check_passed = True; debug.json schema stable; 04.mdx PARTIAL_COVERAGE is composition-planner side (no audit-window contract conflict) none new
§8 grep checklist (6 patterns from issue body) 0 -- G1/G2/G4/G5 PASS; G3/G6 PASS WITH NOTE F-4 (legacy Phase R'/Q BIM literals -- optional cleanup); F-5 (formalize tests/fixtures/ -- optional)
§2 baseline pytest -- -- 303 passed BEFORE + 303 passed AFTER audit none

9.2 Blocker tally

  • 0 Blocker findings across all four axes and all supporting sections.
  • 5 Warning rows in §3 are about blast radius (#6 #12), administrative commit-label drift (#46), and parent/child close-timestamp anomaly (#15 #49). None of them indicate broken code contracts.
  • All BACKLOG_STALE rows in §6 are documentation drift, not implementation absence. Live grep on src/** confirms each closed issue is wired (or carved-out as designed for #17 #18).
  • 5 follow-up candidates (F-1 .. F-5) are all doc-only. None require source code changes.

9.3 Why CONDITIONAL GO, not unconditional GO

Audit found zero Blocker, but the conditions for upgrading to unconditional GO are not met because:

  1. F-3 (backlog sweep) is the largest doc-drift surface (16 of 22 audited rows mislabeled). Issue #19 will read the backlog when scoping next-step coverage; running #19 against a stale backlog risks a planner who treats already-implemented features as still pending. The F-3 follow-up should be filed and merged before -- or at minimum in parallel with -- #19 Stage 2 planning.
  2. F-2 (family template count drift) matters if #19 touches the catalog / frame_contracts.yaml (likely). The audit confirms 11 tracked entries are consistent today, but #19 should reconcile the 2 WIP files (app_sw_package_vs_solution.html, pre_construction_model_info_stacked.html) before adding any new family templates.
  3. F-1 (record-keeping for invariant C3 producer file path) -- a small but real mismatch between the issue body wording (src/phase_z2_mapper.py) and the live producer (src/phase_z2_classifier.py). Should be fixed in the audit charter / spec doc before the next integration audit so future audits do not repeat the same drift check.

F-4 / F-5 are optional and do not gate #19.

9.4 Conditions to satisfy for #19 progression

  • File F-1 / F-2 / F-3 as Section 10 follow-up issues (text-only drafts produced in u6).
  • F-3 backlog sweep should land before #19 Stage 2 (so #19 plans against accurate status).
  • F-2 family template reconciliation should land before #19 introduces new family templates (whichever comes first).
  • F-1 is a one-line spec-doc edit, can land any time before the next INTEGRATION-AUDIT issue is opened.

9.5 Decision sentence

Issue #19 is approved for entry under CONDITIONAL GO, with the explicit dependency that follow-up F-3 (backlog status sweep) must land before #19 Stage 2 planning consumes the backlog, and F-2 (family template reconciliation) must land before any #19 work that extends the catalog. No production source code change is required from this audit. Pytest baseline stable (303 passed BEFORE + AFTER).


Section 10. Follow-up issue drafts (text-only, not auto-posted)

Scope rule (Stage 2 u6 contract) : per-draft fields = title + source_axis (1-4) + scope (what files / what change) + evidence_link (REPORT section that produced the finding). No Gitea post. Final disposition of each candidate is the orchestrator / human triage decision after #50 closes; this REPORT only records the audit-side text.

Five candidates were produced by Axes 1-4. F-3 + F-2 + F-1 are blocking conditions for upgrading §9 CONDITIONAL GO -> unconditional GO for #19; F-4 + F-5 are optional housekeeping. None require source-code changes inside this audit.

10.1 F-1 -- audit charter record-keeping : invariant C3 producer file path -- RESOLVED via IMP-53 (2026-05-19)

  • title : [AUDIT-CHARTER-FIX] invariant C3 (fit_classification) producer cited as src/phase_z2_mapper.py; live producer is src/phase_z2_classifier.py
  • source_axis : Axis 3 (cross-issue conflict, invariant category C3) -- recorded in §5.2 C3 row + §5.4 follow-up bullet F-1.
  • scope :
    • one-line fix in any future INTEGRATION-AUDIT-* issue body or in docs/architecture/PHASE-Z-PIPELINE-OVERVIEW.md if it cites the wrong file for fit_classification producer.
    • replace text src/phase_z2_mapper.py -> src/phase_z2_classifier.py only in the context of fit_classification invariant (mapper.py legitimately owns slot payload and registries, so do not blanket-rename).
    • update audit charter template (if one exists) so the next integration audit does not repeat the drift check.
  • scope-lock : doc-only, zero src/** / templates/** / tests/** edits.
  • evidence_link :
    • REPORT §5.2 row C3 (issue body wording vs. live producer).
    • REPORT §5.4 follow-up bullet F-1.
    • 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 -- 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.
  • scope :
    • decide whether the 2 untracked WIP family templates (app_sw_package_vs_solution.html, pre_construction_model_info_stacked.html) should be (a) tracked + contracted (add to frame_contracts.yaml, add to git ls-files), (b) removed if abandoned, or (c) explicitly noted as in-progress with a parent issue.
    • reconcile the IMP-18 SVG-gap report doc citation families/*.html (13) against whichever decision is chosen (so the doc count matches code reality).
  • scope-lock : touches templates/phase_z2/families/*.html, templates/phase_z2/catalog/frame_contracts.yaml, docs/architecture/IMP-18-SVG-GAP-REPORT.md. Must NOT be folded into #19 silently: any catalog growth needs a dedicated issue per feedback_workflow_atomicity_rules (one commit = one decision).
  • evidence_link :
    • REPORT §5.2 row C6 ("AGREE FOR TRACKED BASELINE -- 11 tracked family templates <-> 11 frame_contracts entries").
    • 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

  • title : [BACKLOG-STATUS-SWEEP] PHASE-Z-IMPLEMENTATION-ISSUE-BACKLOG.md has 16 of 22 audited rows mislabeled as pending; flip 15 to implemented, 1 (IMP-17) to documented (deferred), footnote IMP-15 with 5 execution children
  • source_axis : Axis 4 (backlog vs code reality) -- recorded in §6.1 headline finding + §6.2 22-row matrix + §6.3 follow-up candidate F-3 + §9.1 §6 row + §9.3 condition 1.
  • scope :
    • 15 rows to flip pending -> implemented : IMP-02, IMP-03, IMP-04, IMP-05, IMP-06, IMP-07, IMP-08, IMP-09, IMP-10, IMP-11, IMP-12, IMP-13, IMP-14, IMP-15 (parent), IMP-16.
    • 1 row to flip pending -> documented (deferred) : IMP-17 (status-class shift; runtime AI = 0, 3-cond AND gate closed; matches §6.2 row 16).
    • IMP-15 row : add inline footnote citing the 5 execution children commits #45 (e9b3d2e), #46 (2827622), #47 (535c484), #48 (614c533), #49 (verification-only, re-uses 614c533). Either as a footnote on the IMP-15 row or as 5 child stub rows -- pick one and apply consistently.
    • IMP-18 row : leave as documented (already AGREE per §6.2 row 17).
  • scope-lock : single-file edit to docs/architecture/PHASE-Z-IMPLEMENTATION-ISSUE-BACKLOG.md. Doc-only, zero src/** / templates/** / tests/** edits. Must be filed as a separate Gitea issue with its own Stage 5 commit (not merged into #19 or any other improvement issue).
  • evidence_link :
    • REPORT §6.1 headline finding (16 BACKLOG_STALE + 5 NO_BACKLOG_ROW + 1 AGREE = 22).
    • REPORT §6.2 22-row matrix (per-row grep evidence + commit SHAs).
    • REPORT §6.3 follow-up reference.
    • REPORT §9.1 / §9.3 condition 1 ("F-3 backlog sweep should land before #19 Stage 2 planning consumes the backlog").
  • priority / gating : highest of the 5 candidates. Must land before #19 Stage 2 planning (per §9.3 condition 1) so that #19's planner reads accurate implemented / documented (deferred) status and does not treat already-wired features as still pending.

10.4 F-4 -- legacy Phase R' / Q sample-literal cleanup (OPTIONAL)

  • title : [LEGACY-LITERAL-CLEANUP] 20 hits of 재구성 / 건설산업 DX / BIM across 10 legacy Phase R'/Q files (block_assembler_b2.py, block_matcher_tfidf.py, block_reference.py, content_editor.py, design_director.py, design_tokens.py, fit_verifier.py, frame_extractor.py, kei_client.py, pipeline.py)
  • source_axis : Axis-supporting Section 8 (anti-hardcoding grep checklist) -- recorded in §8.1 row G3 + §8.2 third bullet + §9.1 §8 row.
  • scope :
    • per-file review of the 20 legacy hits to determine which are docstrings / comments (keep), legacy taxonomy (keep with annotation), or true sample-literal pins (remove or generalize).
    • per-file counts to triage : block_assembler_b2.py 1, block_matcher_tfidf.py 1, block_reference.py 3, content_editor.py 3, design_director.py 2, design_tokens.py 1, fit_verifier.py 1, frame_extractor.py 1, kei_client.py 4, pipeline.py 3.
    • NOT touching the 11 Phase Z hits (phase_z2_content_extractor.py 7 self-test data, phase_z2_failure_router.py:123 taxonomy, phase_z2_mapper.py:519/529 docstring examples, phase_z2_retry.py:59 docstring) -- those passed audit verdict G3.
  • scope-lock : potentially touches legacy src/** files NOT in the Phase Z 22-step pipeline. Must be filed as a deliberate cleanup issue with its own scope-lock. If any file flagged here turns out to be on a live Phase Z code path on review, demote the candidate or split it.
  • evidence_link :
    • REPORT §8.1 row G3 (per-file count breakdown, audit date 2026-05-19).
    • REPORT §8.2 third bullet (20 legacy + 11 Phase Z = 31, reconciliation).
    • Raw grep output : D:\ad-hoc\kei\design_agent\.orchestrator\tmp\50_grep_checklist_raw.txt.
  • priority / gating : optional, low priority, doc-only follow-up note. Does NOT gate #19; §8 verdict already PASS for audit scope. Recorded for completeness so future audits do not re-discover the same 20-hit baseline.

10.5 F-5 -- formalize tests/fixtures/ directory (OPTIONAL)

  • title : [TESTS-FIXTURES-FORMALIZE] tests/fixtures/ directory does not exist; sample MDX references currently live in tests/phase_z2/test_pz2_vu_integration.py
  • source_axis : Axis-supporting Section 8 (anti-hardcoding grep checklist G6) -- recorded in §8.1 row G6 + §8.2 fourth bullet.
  • scope :
    • if-and-only-if sample inventory grows beyond what fits inside tests/phase_z2/test_*.py files, formalize a tests/fixtures/ directory holding sample-specific fixtures.
    • migrate existing samples/mdx_batch/02.mdx references in tests/phase_z2/test_pz2_vu_integration.py:6, 82 only if migration is part of a broader test-fixture refactor (otherwise leave them as integration smoke).
    • update the issue-body rule wording to acknowledge that tests/phase_z2/test_*.py already discharges the spirit of "no sample-specific fixtures in production pipeline".
  • scope-lock : touches tests/fixtures/ (new directory if filed) + the cited test files. Must NOT be folded into any unrelated test refactor.
  • evidence_link :
    • REPORT §8.1 row G6 verdict "PASS WITH NOTE".
    • REPORT §8.2 fourth bullet (tests/fixtures/ not yet established).
  • priority / gating : optional, very low priority. Filing is only justified when sample inventory grows; the current state is already aligned with the spirit of the rule.

10.5.1 F-5 docs-only resolution addendum (#54 Stage 3 u5, 2026-05-19)

Per issue #54 Stage 2 plan, F-5 is closed as docs-only; no root tests/fixtures/ directory is created in this work. The current fixture inventory does not justify migration, and the existing convention is sufficient. The convention is recorded here so future anti-hardcoding audits can distinguish fixture / test-only paths from production paths without re-discovering the §8 G6 PASS-WITH-NOTE baseline.

  • Existing convention (DO NOT CHANGE) : tests/phase_z2/fixtures/ exists as a YAML regression fixture root (loaded by tests/phase_z2/test_fixtures_loader.py). Subdirectories present at audit time : tests/phase_z2/fixtures/build_layout_css/, tests/phase_z2/fixtures/retry_gate/. This is the canonical home for Phase Z regression fixtures.
  • Root tests/fixtures/ (ABSENT) : not created in #54. If a future change requires a non-Phase-Z, non-YAML fixture corpus (for example, multi-file MDX golden inputs that grow beyond what tests/phase_z2/test_*.py can hold inline), the migration must be filed as its own Gitea issue with its own scope-lock per §10.5.
  • Allowed sample references : samples/mdx_batch/** and samples/mdx/** may be referenced from tests/** (test-only paths) for integration smoke -- e.g. the existing samples/mdx_batch/02.mdx references in tests/phase_z2/test_pz2_vu_integration.py. These do not violate the §8 anti-hardcoding rule because the spirit of the rule targets production pipeline code, not test runners.
  • Forbidden sample references : production pipeline code (src/** runtime path) must NOT hardcode sample-specific MDX filenames or content (e.g. 02.mdx, 03.mdx, frame-specific labels keyed to a sample). The 20 legacy Phase R'/Q hits annotated under F-4 (#54 Stage 3 u1-u4) are intentional documented examples in docstrings / comments / glossary regex / sample-data dicts, not runtime input pins; they are out of scope for this rule by §10.4 verdict.
  • AI-isolation contract : this addendum is text-only. No production behavior change, no runtime sample-path mutation, no new fixture file. Compatible with PZ-1 (AI = 0 on normal path) and feedback_ai_isolation_contract.
  • Cross-reference : tests/CLAUDE.md fixture convention note (#54 Stage 3 u5) mirrors the test-only / production rule split documented here.

10.6 Follow-up summary

candidate source axis doc-only? gates #19? priority
F-1 audit-charter producer file path Axis 3 (§5) YES NO low (charter cleanup)
F-2 family template count reconcile Axis 3 (§5) NO -- touches templates / catalog / docs gate IF #19 extends catalog medium
F-3 backlog status sweep Axis 4 (§6) YES YES -- must land before #19 Stage 2 plan highest
F-4 legacy R'/Q literal cleanup §8 (anti-hardcoding) NO -- legacy src/ touch surface NO low (optional)
F-5 tests/fixtures/ formalize §8 (anti-hardcoding) NO -- tests/ migration NO very low (optional)
  • Counts : 5 candidates total. 3 are blocking conditions for upgrading §9 CONDITIONAL GO to unconditional GO for #19 (F-3 hard-gates, F-2 conditional-gates on catalog touch, F-1 nice-to-have before next audit). 2 are optional housekeeping (F-4, F-5).
  • Compliance with Stage 2 u6 contract : per-draft fields (title / source_axis / scope / evidence_link) populated for each of F-1 .. F-5. Zero auto-posts -- this section is text-only. Filing decisions = orchestrator / human after #50 closes.
  • AI-isolation contract : none of the 5 follow-up candidates require AI on a normal path. F-2 / F-4 / F-5 are scope decisions to be made by a human reviewer. Compatible with feedback_ai_isolation_contract and PZ-1 (AI = 0 on normal path).