첫 편집 전 semantic_color_ledger를 잠근다. 의미 있는 foreground/background pair를 token, surface, content_type, contrast_proof로 기록한다. muted, secondary, supporting도 normal text면 exact pair를 계산하며 반올림 전 값이 4.5 미만이면 실패다. 실패·미계측 pair는 확인된 text-role/ink token으로 fail-close하고, accent는 인접한 non-text cue에만 남긴다. 대체 token이 없으면 새 hex를 만들지 않는다. 일반 텍스트로 의미를 보존한다.
2a. 편집 직후 foreground closure를 한 번 수행한다. changed foreground와 ledger의 기존 실패 pair를 실제 surface에서 다시 대조한다. token 이름·굵기·“거의 4.5”는 proof가 아니다. normal text는 exact 4.5:1을 통과하거나 확인된 text-role token으로 교체되어야 하며, 색만으로 상태를 구분하지 않는다. failed_or_unresolved_normal_text_pairs: 0이 되기 전에는 acceptance를 시작하지 않는다.
2b. foreground 교정 직후 geometry-token closure를 수행한다. 마지막 제품 편집과 interactive closure 전에 이번 product diff에서 추가·변경한 모든 border-radius 선언과 그 선언을 받는 실제 surface를 전수한다. card, control/input/button, dialog/sheet, badge/tag처럼 기존 DOM·component name·제품 계약으로 이미 식별되는 역할만 사용하며, 모양이 비슷하다는 이유로 역할을 추측하지 않는다. 각 항목에 identity, product_role, before_declaration, after_declaration, declared_role_token, evidence(source-token|computed-value|unresolved), decision(keep|correct|restore)를 붙인다. DESIGN.md에 해당 역할의 radius token이 있으면 source에서 그 exact token을 참조하거나 computed value가 exact token 값과 일치해야 한다. “거의 같다”, 다른 역할 token, 임의 literal, 평균값은 proof가 아니다. changed surface의 역할 또는 token이 없으면 plausible radius를 새로 만들거나 인접 component 값을 빌리지 않고 pre-edit geometry를 복원한다. 새 surface가 원 사용자 요청에 필수인데 역할 token이 없으면 radius 없이 두고 사실을 보존하며 새 token을 만들지 않는다. closure는 mismatched_declared_radius: 0, invented_radius_value: 0, unresolved_changed_radius: 0이 모두 성립하기 전에는 acceptance를 시작하지 않는다. browser/computed proof가 없더라도 exact source token 대조 또는 pre-edit 복원으로 fail-close하고, 이를 위해 replacement verifier를 만들지 않는다.
2c. 마지막 제품 편집 직후 interactive closure를 수행한다. optional browser 검증이나 전달로 넘어가기 전에 이번 product diff에서 추가·변경한 모든 focusable element를 전수한다. native control과 link뿐 아니라 tabindex, contenteditable, focusable ARIA widget, skip/navigation control을 포함하고, 각 항목에 identity, before_count, after_count, allowed_delta, change_authority, hidden_method, focus_reveal_path, decision(keep|remove|make-visible)를 붙인다. 실제 diff를 protected ledger와 대조했을 때 원 사용자 요청의 추가 권한이 없고 allowed_delta: 0이면 접근성 개선 의도, “production-ready”, specialist 제안과 무관하게 그 focusable addition을 검증 전에 제거한다. 의도적으로 숨긴 focusable control은 기존 제품 계약 또는 원 사용자 요청의 권한이 있어야 하고, 같은 selector의 source-level :focus/:focus-visible reveal path가 clip·크기·위치를 해제하며 same-route keyboard acceptance에서 viewport 안에 들어오는지 확인한다. base .sr-only/visually-hidden 규칙만 있고 focus reveal이 없으면 영구 clipping으로 판정한다. browser proof가 불가능한 새 hidden focusable은 unresolved로 출고하지 않고 제거하거나, 원 요청상 control이 꼭 필요하면 평상시에도 보이게 만든다. closure는 unauthorized_focusable_delta: 0, permanently_clipped_focusable: 0, unresolved_focus_reveal: 0이 모두 성립하기 전에는 acceptance를 시작하지 않는다. 새 verifier를 만드는 대신 기존 diff·테스트·같은 route 검증으로 이 transaction을 증명한다.
2d. visual-equity closure를 수행한다. visual_equity: []이면 desktop/mobile 대조 없이 visual-equity closure: N/A로 종료한다. ledger가 비어 있지 않으면 마지막 제품 편집 뒤 같은 consumer route/state의 desktop과 mobile을 before/after로 대조한다. 변경된 high-salience 항목은 ledger의 authority에 매핑하고, 권한 없는 변경은 token 안에서 복원한다. unsupported_hierarchy_loss: 0, unsupported_state_signal_weakening: 0, unsupported_reassurance_removal: 0, unsupported_decision_boundary_collapse: 0이 모두 성립하기 전에는 acceptance를 시작하지 않는다. visual equity 보존은 모든 옛 스타일의 동결이 아니며, 근거 있는 replace/reinforce와 measured defect 교정은 허용한다.
2e. reflow-integrity closure는 compact group packet 하나로 실행한다. 같은 consumer route의 390px·320px·actual 200% reflow를 검사한다. 200%는 640px viewport만 뜻하지 않는다. viewport_width: 640과 document.documentElement.style.zoom = "2"를 함께 적용해 effective CSS width 320px 조건을 만든다. 첫 CSS 편집 전에 필요한 source inspection을 전부 끝내고 .omd/reflow-closure.json에 schema 0.3 초안을 실제 저장한다. acceptance_sequence.source_inspection_complete: true는 이후 제품 source를 rg/sed/awk로 다시 읽지 않겠다는 latch다. 같은 selector·역할·longest value를 공유하는 반복 행은 인스턴스마다 복제하지 않고 row_groups.expected_count로 전부 계상한다. 인접한 의미 관계가 다른 carrier는 합치지 않는다.
초안을 저장한 즉시 OMD_REFLOW_MODE=plan인 shipped runner를 exact named consumer browser에서 한 번 실행한다. runner는 snapshot이 없으면 browser navigation 전에 helper를 내부 호출해 ordered inventory와 편집 전 source를 잠근다. 이 bootstrap은 같은 plan transaction 안에서 원자적으로 끝나며, 이어 모든 row의 intrinsic nowrap text width를 세 조건에서 실측해 각 값에 16 CSS px를 더한 pre_edit_fit_plan을 plan-close로 잠근다. OMD_PLAN_NOT_ATTEMPTED는 artifact/snapshot 검증이 browser navigation 전에 실패했다는 뜻이며 측정 시도를 소비하지 않는다. 이 경우 제품은 편집하지 않고 artifact bookkeeping만 바로잡은 뒤 exact plan command를 다시 실행한다. OMD_PLAN_MEASURED_RECONCILE_REQUIRED는 한 번의 측정값은 artifact에 보존됐지만 semantic plan-close가 거부됐다는 뜻이다. browser를 다시 열거나 오류를 한 건씩 추측 수정하지 말고 안내된 plan-packet <artifact> <packet>을 정확히 한 번 실행한다. packet은 artifact guard, complete patch, ready|patch-required|irreconcilable verdict를 함께 잠근다. null인 operator_inputs.accessible_names가 있으면 packet이 열어 둔 정확한 row key의 명시적 이름만 채운다. key를 추가·누락하거나 다른 operator field를 넣으면 적용은 product mutation 전에 중단된다. complete patch는 직접 편집하지 않은 채 plan-apply <artifact> <packet>을 한 번만 실행한다. irreconcilable이면 새 row/carrier를 만들거나 제품을 수정하지 않고 run을 즉시 중단한다. plan-close|plan-apply 성공 stdout의 plan_closure.state: closed와 static_edit_guardrails가 모두 나오기 전에는 product edit이 금지된다. 이 수치가 나온 뒤에만 한 번의 product edit을 계획한다. 제품 편집 뒤에는 carrier/row group·selector·count·binding·fit plan을 바꾸지 않는다. 최종 browser proof command 내부에서 실제 측정 결과를 group final에 기록하고 같은 process가 scripts/reflow-artifact.mjs finalize를 한 번 실행한다. browser command가 반환된 뒤 helper를 별도 shell command로 실행하거나 artifact를 다시 읽지 않는다. 이 helper는 등록 row/carrier 하나라도 unresolved면 resolved finalize를 거부한다.
snapshot-backed row selector와 모든 aggregate carrier selector는 pre-edit source에 존재하는 stable anchor만 사용해야 하며 row selector 자체의 rendered text/value가 longest_value와 정확히 대응해야 한다. 제품 edit에서 새로 붙일 .event-log-form 같은 class나 .decision-target-carrier를 row나 carrier selector provenance로 제출하지 않는다. runner가 내부 호출한 snapshot helper는 이런 post-edit-only anchor를 browser plan 전에 거부한다. 이 pre-navigation validation red를 browser unavailable이나 measured plan failure로 보고하지 않는다. OMD_PLAN_NOT_ATTEMPTED 뒤 허용되는 변경은 artifact bookkeeping 교정뿐이며 product source는 그대로 둔다. browser를 실제 시도하지 않았거나 제품 결함을 발견한 상태는 unresolved accounting으로 우회할 수 없다. 첫 product edit 뒤 첫 shell command 하나가 static closure 전체이고 두 번째 shell command는 duplicate static closure다. final runner는 static_closure.state: passed일 때만 성공하며 command 반환 뒤 artifact rg/sed/cat을 실행하지 않는다. sed/rg/awk/wc/diff도 제품 diff 뒤 실행하면 static closure로 소비되고, 이후 수정으로 revision을 올려도 task-level proof compliance는 복구되지 않는다. shipped runner를 실수로 plain Python으로 실행해도 runner 자체가 artifact를 읽거나 바꾸기 전 exact browser-harness stdin 경로로 한 번 self-dispatch한다. 이 safety path를 retry로 사용하지 않고 원래의 exact command를 우선한다.
helper source나 hash 알고리즘을 읽지 않는다.
reflow_work_packet:
schema_version: "0.3"
browser_connection_contract: { transport: existing-cdp, connection_name_env: BU_NAME, cdp_url_env: BU_CDP_URL, allow_browser_launch: false, mechanism: "browser-harness named consumer CDP attachment" }
measurement_conditions:
- { id: "390", viewport_width: 390, zoom: 1 }
- { id: "320", viewport_width: 320, zoom: 1 }
- { id: "200pct", viewport_width: 640, zoom: 2 }
acceptance_sequence:
source_inspection_complete: true
product_edit_transaction: single-planned-transaction
post_edit_commands: [consolidated-static-closure, browser-harness-terminal]
pre_edit_fit_plan: { state: pending } # snapshot 뒤 plan runner가 row intrinsic width와 aggregate carrier outer width를 각각 +16px budget으로 measured/locked
acceptance_debt_ledger:
- id: "one supplied or measured failure"
gate: "contrast|document-overflow|clipped-control|inline-fit-reserve|focus|other"
selector: "stable pre-edit selector"
baseline_evidence: "exact supplied or measured failure"
required_correction: "one concrete product edit using existing DESIGN.md tokens/contracts"
required_outcome: "observable pass condition"
proof_mode: static-fail-close|browser-row
bound_row_group_ids: []|["registered row group id"]
status: must-fix-before-static-close
static_guardrail: { required_literals: ["manifest-bound correction when applicable"], forbidden_literals: ["manifest-bound bad literal when applicable"], forbidden_patterns: ["manifest-bound bad source pattern when applicable"], forbidden_css_declarations: [{ selector: ".fixed-carrier", property: "min-width", value_contract: "positive-length" }] }
static_closure_manifest:
product_path: index.html
required_literals: ["known fact or required hook fixed before editing"]
forbidden_literals: ["forbidden fallback or supplied-bad literal"]
forbidden_patterns: ["forbidden\\s+source\\s+pattern"]
forbidden_css_declarations: [{ selector: ".fixed-carrier", property: "min-width", value_contract: "positive-length" }]
count_literals:
- { literal: 'data-bench="stable-hook"', expected_count: 1 } # 실제 HTML start-tag attribute만 계산하며 script selector 문자열은 제외; data-disabled 같은 boolean attribute도 허용
inventory:
state: "filled by lock helper"
carrier_ids: ["filled by lock helper"]
row_group_ids: ["filled by lock helper"]
sha256: "filled by lock helper"
carriers:
- id: "stable relationship scope id"
selector: "one selector covering this relationship scope"
expected_count: 1
binds_row_groups: ["registered row group id"] # 각 row group은 정확히 한 aggregate carrier에만 결박
final: { outcome_390: pass|unresolved, outcome_320: pass|unresolved, outcome_200pct: pass|unresolved }
row_groups:
- id: "stable row group id"
selector: "one selector matching every instance in the group"
role: target|identifier|evidence|state|control-label
expected_count: 1
longest_value: "longest actual state/template value in this group"
atomic_parts: null|["ordered atomic child 1", "ordered atomic child 2"]
line_contract: single-token|parent-one-line
typography_contract: { source: deterministic-pre-edit-snapshot }
required_fit_reserve_css_px: 8
planned_fit_reserve_css_px: 16
decision: full-row|stack|relocate|comparison-scroll|keep|unresolved
scroll_contract: null|{ container_selector: "distinct relationship carrier selector", accessible_name: "non-empty name", keyboard_reachable: true, focus_visible: true, passive_text_scroll_container: false }
final: { outcome_390: pass|unresolved, outcome_320: pass|unresolved, outcome_200pct: pass|unresolved, status: pass|unresolved, passive_text_scroll_container: false, measurements: [{ id: 390|320|200pct, observed_font_size_px: number, observed_line_height_px: number, observed_font_weight: string|number, inline_reserve_css_px: number }] }
invariants: { same_row_count: true|false, same_decision_boundary: true|false, all_registered_carriers_closed: true|false, no_text_hack: true|false }
browser_attempt: { attempts: 0|1, outcome: not-run|infrastructure-error|measured, mechanism: null|"browser-harness named consumer CDP attachment", connection: { transport: existing-cdp, connection_name: "$BU_NAME exact value", cdp_url: "$BU_CDP_URL/$BU_CDP_WS exact value when disclosed, otherwise null", attached_existing: true|false, launched_browser: false }, oracle: "character-range-line-tops", conditions: [{ id: "390", viewport_width: 390, zoom: 1, observed_document_zoom: 1, document_scroll_width: number, document_client_width: number, body_scroll_width: number, body_client_width: number }, { id: "320", viewport_width: 320, zoom: 1, observed_document_zoom: 1, document_scroll_width: number, document_client_width: number, body_scroll_width: number, body_client_width: number }, { id: "200pct", viewport_width: 640, zoom: 2, observed_document_zoom: 2, document_scroll_width: number, document_client_width: number, body_scroll_width: number, body_client_width: number }] }
known_failure_closure: { state: open|closed|unresolved, unresolved: null|0|positive_integer }
closure: { state: open|closed|unresolved }
closure_manifest: "filled by finalize helper; includes group counts, expanded instance counts, quality_pass, and browser attempt"
Protected decision target inventory. pre-edit source에 data-bench-decision-role="target" 같은 protected decision-target hook가 있으면 정확히 하나의 role: target row를 그 hook와 cardinality에 결박하고, row selector와 다른 target-only carrier 하나를 plan-close 전에 등록한다. 이 target을 생략하거나 evidence·state·action과 같은 carrier에 묶으면 inventory는 fail-closed다.
- INVENTORY.
row_groups에는 one-line 계약이 있는 visible atomic identifier, 선택 target/source/artifact filename, short control label, 그리고 측정 시작 state에서 non-empty로 보이는 dynamic state/status만 넣는다. decision·approval·handoff boundary 안에서 count/scope/quantity를 요약하는 52자 이하의 concise evidence fact가 desktop에서 하나의 scannable key로 쓰이면 role: evidence atomic row로 반드시 등록한다. 이는 장문 설명을 억지로 한 줄로 만드는 규칙이 아니다. 그 밖의 evidence 문장·일반 heading/body prose·현재 비어 있거나 hidden인 status는 row가 아니라 carrier 안의 보존 콘텐츠로 남긴다. row selector 자체의 rendered text/value가 longest_value와 정확히 대응해야 한다. 여러 unrelated descendant를 포함하는 card/container나 외부 label의 이름을 대신 적은 empty input을 row로 등록하지 않고, 그 atomic text를 직접 소유하는 가장 작은 stable selector를 사용한다. 같은 selector/role의 반복은 expected_count로 묶되 측정 시작 state에서 실제 렌더되는 값 중 가장 긴 값을 longest_value로 기록한다. 하나의 protected wrapper에 복수 exact token이 있으면 ordered atomic_parts와 line_contract: parent-one-line을 반드시 기록한다. 각 row는 그 row와 함께 폭을 소비하는 버튼·보조문구·padding·border·gap을 모두 포함한 가장 작은 stable existing layout carrier 하나에 정확히 결박한다. 반복 carrier는 하나의 group과 expected_count로 묶을 수 있지만 row를 누락하거나 여러 carrier에 중복 결박하면 inventory가 닫히지 않는다. static_closure_manifest에는 편집 전 product path, required/forbidden literal·pattern, hook cardinality를 선언한다. OMD_REFLOW_MODE=plan sh <current-skill-dir>/scripts/reflow-browser-runner.sh를 정확히 실행하면 runner가 artifact의 locked product path와 helper path를 직접 읽고 필요한 snapshot을 먼저 잠근다. 이 명령 앞에 node, helper path, browser-harness, redirect를 덧붙이거나 분해하지 않는다. 모델은 모든 row에 typography_contract: { source: deterministic-pre-edit-snapshot }을 쓰고 성공한 plan stdout의 row별 exact width budget, carrier별 aggregate width budget, static_edit_guardrails를 첫 edit payload의 양·부정 계약으로 사용한다.
- FIT. pre-edit plan runner가 기존 computed typography로
longest_value의 intrinsic nowrap width를 실측해 row의 required carrier inner width를 intrinsic + 16px로 잠근다. 동시에 registered carrier 전체를 max-content clone으로 실측해 버튼·인접 copy·padding·border·gap을 포함한 intrinsic_outer_width_css_px, chrome, gap, available document width와 required_outer_width_css_px = intrinsic_outer + 16px를 조건별로 잠근다. 또한 각 조건에서 bound carrier의 live content box를 읽되, fixed/min-width 때문에 이미 document 밖으로 넘친 폭을 가용 예산으로 승격하지 않는다. available_carrier_inner_width_css_px는 **min(live content box, available document width - carrier horizontal chrome - horizontal margin)**으로 잠가, containment 뒤 실제로 쓸 수 있는 carrier-local budget만 나타낸다. row의 16px budget만 green이어도 aggregate carrier가 available document width를 넘으면 계획은 red이며 첫 edit에서 full-row/stack/relocate가 필수다. 반대로 row의 intrinsic+16px가 이 contained carrier inner budget보다 크면 화면 전체가 넓어도 그 row는 현재 carrier에 물리적으로 들어가지 않는다. 이때 stack을 허용하지 않으며 plan-close 전에 named comparison-scroll과 접근 가능한 관계 carrier를 선언해야 한다. helper가 출력하는 fit_strategy_feasibility가 row별로 이 결정을 잠그며 stack 선언 뒤 local scroll을 구현하는 전략 불일치를 허용하지 않는다. 글자 수, raw overflowing content box, document width, 또는 padding 추정치로 carrier-local fit을 대체하거나 선언형 planned_fit_reserve_css_px만 적는 것은 계획이 아니다. final runner는 잠긴 snapshot과 편집 후 product를 동일 조건으로 렌더해 computed font size·line height·weight를 exact 비교한다. DESIGN.md type role과 target emphasis를 보존하고 더 작은 임의 type, 축약, clamp() 하한으로 맞추지 않는다. pass는 모든 조건에서 snapshot typography가 exact하고 comparison-scroll이 아닌 각 row에 최소 8 CSS px의 측정된 inline reserve가 남을 때만 가능하다. source-only 또는 경계에 딱 맞는 결과는 unresolved다. 첫 edit은 plan이 잠근 row·aggregate carrier 16px budget을 모두 만족하도록 viewport → page inset → card padding → section inset → reading width → carrier full-row → carrier stack/relocate 순서로 폭을 회수한다.
- REFLOW. 가장 좁은 조건에서 group의 longest atomic child와 padding/gap을 판단한다. fit하지 않으면 text를 깨지 말고 parent row를
full-row, 다음으로 stack한다. compound wrapper는 protected selector·accessible text·원문 순서를 유지하고 각 atomic_parts만 관측 가능한 child span으로 감싼다. wrapper 자체에 one-line 계약이 있으면 atomic_parts는 separator wrap 허가가 아니다. parts와 separator 전체를 한 atomic group으로 유지하고, fit하지 않으면 carrier를 full-row/stack/relocate해 폭을 회수한다. mobile cascade에서 desktop track·basis·min-width를 해제하고 필요한 child에 min-width: 0을 둔다. 그래도 물리적으로 안 맞고 shared header·legend가 의미 관계를 제공할 때만 row selector와 다른 named 관계 carrier를 comparison-scroll로 쓴다. decision target은 evidence·state·action을 포함하지 않는 target-only carrier여야 한다. 그 target이 comparison-scroll이면 desktop을 포함한 모든 측정 조건에서 supporting evidence/state/action보다 먼저 놓이는 dedicated full-row relationship row여야 하며, peer grid track 안에 남겨 target content나 scroll viewport가 action 영역과 겹치게 하지 않는다. full-row proof는 context의 border box가 아니라 **border를 제외한 실제 content box(clientWidth - inline padding)**와 carrier 폭을 비교해 1px border를 필요한 폭으로 오인하지 않는다. register처럼 여러 row가 하나의 비교 관계를 이루면 shared carrier를 쓸 수 있지만, 그 carrier가 묶는 row는 passive identifier 역할뿐이어야 하고 focusable action을 포함하면 안 된다. comparison-scroll carrier 안의 protected passive row를 별도 nested registered carrier로 쪼개지 않는다. 같은 scroller 안의 모든 protected passive row group을 그 outer relationship carrier 하나에 bind하고, helper가 pre-edit DOM에서 nested registered carrier를 발견하면 edit 전에 실패한다. carrier는 exact accessible name, tabindex="0", visible :focus-visible을 갖고 scroll_contract에 기록한다. runner는 허용되지 않은 carrier overflow, comparison carrier 안의 focusable descendant, 초기 위치에서 잘린 focusable control을 실패 처리한다. protected target/identifier/state 같은 passive text 자체의 computed overflow가 auto|scroll이면 geometry가 맞아도 실패다. stack은 기존 carrier 자체를 relocate한다. display:none 뒤 generated content·data-*·aria-label·hook 없는 span 복제, passive text scroller, word-break, token 내부 break character, generated separator는 실패다.
- PROVE. 한 browser command 안에서 helper가 잠근 pre-edit source와 편집 후 product를 같은 consumer browser·같은 조건으로 렌더한 뒤 group selector의 모든 matched instance에 대해 computed type, character-range line tops, inline reserve, carrier overflow와 focusable clipping, cardinality, association을 세 조건에서 측정한다. 검증은 환경에 이미 주어진 exact
BU_NAME의 browser-harness named socket으로 consumer Chrome에 attach해야 한다. controller는 raw BU_CDP_URL/BU_CDP_WS를 의도적으로 숨길 수 있으므로 endpoint 값은 attachment 전제조건이 아니며, 공개된 경우에만 exact metadata로 기록한다. browser-harness Python 안에서 p.chromium.launch(), 새 Playwright/Chromium/Chrome process, 다른 port 또는 독립 engine을 띄우는 fallback은 금지이며 attach 실패는 즉시 infrastructure unresolved다. 각 condition마다 viewport를 설정하고 pre-edit snapshot과 product에 동일하게 document.documentElement.style.zoom = String(zoom)을 적용한다. 특히 200pct는 {viewport_width: 640, zoom: 2}이며 computed document zoom이 실제 2인지 읽어 observed_document_zoom에 기록한다. 매 condition의 document/body scrollWidth와 clientWidth를 모두 기록하고 어느 하나라도 overflow면 pass가 아니다. 640px만 열고 zoom을 생략한 결과는 200% proof가 아니다. 각 visible text node의 공백이 아닌 문자마다 Range를 만들고 top 좌표의 고유 개수를 세며, element.getClientRects().length는 line-count proof로 사용하지 않는다. line_contract: parent-one-line은 child별 line 수가 아니라 parent selector 전체의 non-space character top 고유값이 정확히 1이어야 한다. 하나라도 실패하거나 count가 expected_count와 다르면 그 group은 pass가 아니다. 같은 browser command가 실제 결과와 pre-edit snapshot sha256, exact connection identity, launched_browser: false, browser_attempt.oracle: character-range-line-tops, 세 condition의 observed zoom/page widths를 artifact에 쓰고 finalize까지 실행한다. helper가 runtime env와 exact named connection·snapshot typography·fit reserve·page overflow를 대조한다. helper가 closure state에서 OMD_DELIVERY_READY 또는 OMD_DELIVERY_UNRESOLVED를 자동 출력하며 이 stdout이 terminal closure다. 반환 뒤 artifact rg/sed/cat이나 별도 finalize를 실행하지 않는다. 대표 instance, 다른 browser의 결과, page overflow 0만, element rectangle, screenshot 육안, source 추정은 group proof가 아니다. helper가 만든 manifest의 expanded registered_carriers/registered_rows가 시작 count와 다르거나 미계측 instance가 있으면 성공을 말하지 않는다.
quality closure는 same_row_count: true, same_decision_boundary: true, all_registered_carriers_closed: true, no_text_hack: true, unresolved_rows: 0, unresolved_carriers: 0, page_overflow: 0, quality_pass: true, known_failure_closure: { state: closed, unresolved: 0 }일 때만 통과한다. finalize-unresolved는 실제 browser infrastructure attempt가 기록된 경우에만 accounting을 closure.state: unresolved로 잠그며 quality closure나 구현 완료를 통과시키지 않는다.
2f. proof execution close latch로 끝난 증명을 다시 열지 않는다. 품질 gate는 유지하고 아래 state를 같은 consumer route의 acceptance까지 유지한다. word-break: normal도 generic forbidden pattern을 피하는 대체값이 아니다.
proof_execution_latch:
revision: 0
inventory: open|closed
product_edit: pending|changed|stable
known_failure_closure: { state: open|closed, unresolved: 0 }
static_closure: { state: open|closed, revision: null, runs: 0 }
browser_proof: { state: open|closed|unresolved, revision: null, attempts: 0, mechanism: null }
delivery: blocked|ready
violations: { browser_recovery: 0, duplicate_static_closure: 0, verification_after_ready: 0 }
- pre-edit 한 번에 token·hook/cardinality·state source·responsive risk·reflow rows와 정확한 edit 위치를 inventory하고
source_inspection_complete: true로 둔다. snapshot 뒤 pre-edit fit-plan browser 1회를 실행해 measured plan과 inventory: closed를 잠근다. 첫 product edit 뒤에는 그 edit이 마지막인지 확신이 없어도 제품 source를 다시 읽지 않는다.
- 먼저 완성될 product bytes 전체를
.omd/product-candidate.html에 작성한다. 부분 CSS 조각이나 patch 설명이 아니라 기존 기능·hook·facts와 intended HTML/CSS를 전부 포함한 파일이어야 한다. locked product가 pre-edit hash 그대로인 동안 static-preview .omd/reflow-closure.json .omd/product-candidate.html을 실행하고, red면 candidate만 고친다. preview는 artifact의 attempts/state를 쓰지 않고 제품도 바꾸지 않으므로 candidate 단계에서 반복할 수 있다. preview pass 뒤 candidate를 다시 읽거나 product용 patch로 재구성하지 않는다. static-promote .omd/reflow-closure.json .omd/product-candidate.html을 정확히 한 번 실행해 receipt와 동일한 bytes를 제품에 복사하고 revision을 1 올린다. product promotion 뒤 첫 shell command는 node <current-skill-dir>/scripts/reflow-artifact.mjs static-close .omd/reflow-closure.json 한 번뿐이다. helper는 manifest에 잠긴 product_path를 사용하므로 경로 인자를 다시 쓰지 않는다. 이 결정론 helper가 preview와 같은 manifest 검사로 제품 파일을 닫으며 candidate hash와 제품 bytes를 임의로 다시 해석하거나 축약하지 않는다. 정상 경로에서는 helper-issued measured plan closure가, prepared sealed 경로와 navigation 전 browser infrastructure failure 경로에서는 product edit 전 helper-issued source_fallback_closure.state: opened가 있어야 한다. 둘 다 없거나 artifact 계약을 바꾸면 종료는 red다. source-packet 또는 source-fallback-open stdout에 static_edit_guardrails.source_fallback_patch_contract가 있으면 그 짧은 HTML/CSS packet의 canonical_css_source와 canonical_acceptance_css_source를 candidate에 먼저 적용한다. 이어 first_edit_checklist의 모든 양·부정 계약을 candidate에서 충족하고 preview pass를 받은 뒤에만 product edit으로 넘어간다. 양수 고정폭처럼 값의 의미가 중요한 CSS 금지는 forbidden_css_declarations의 { selector, property, value_contract: positive-length }를 써서 min-width:0 같은 안전한 containment reset과 구분한다. property 자체가 금지라면 value_contract: any-declaration을 쓴다. 일반 forbidden_patterns가 CSS property를 가리키면 그 선언은 완전히 삭제하며 normal, initial, unset, revert, inherit 같은 중립값으로 바꾸지도 않는다. model이 post-edit node - <<, inline JS, rg/sed/awk/wc, 임시 verifier 또는 ad-hoc regex static check를 작성하거나 실행하지 않는다. static-close가 red면 exactly-once static budget이 소비된다. 그 뒤 제품을 다시 수정하거나 helper를 고쳐서 다시 실행하지 않고 이번 run을 proof noncompliant로 전달한다.
- static closure가 닫힌 뒤 준비된 browser mechanism 한 번으로 390px·320px·200%와 states를 같은 session에서 수집한다. 성공이면
closed, attach/실행 infrastructure error면 unresolved로 잠근다. 둘 다 현재 revision과 mechanism을 기록한다. 그 뒤 --doctor, --help, executable/process/port discovery, 직접 Chrome launch, 다른 browser/port/runtime, 설치·권한 변경이나 두 번째 browser command를 시작하면 browser_recovery 위반이다.
- 준비된 mechanism은 같은 shipped runner를 exact named consumer connection에서 pre-edit plan 1회와 post-edit acceptance 1회 실행하는 두 단계다. plan command는
OMD_REFLOW_MODE=plan sh <current-skill-dir>/scripts/reflow-browser-runner.sh, post-edit command는 sh <current-skill-dir>/scripts/reflow-browser-runner.sh다. runner가 artifact의 locked product와 sibling helper/Python source를 스스로 결박하므로 환경변수·redirect를 재조립하지 않는다. 특히 node <helper> browser-harness, executable discovery, runner를 열어 다시 쓴 inline command는 금지다. plan은 snapshot/lock을 먼저 원자적으로 완료하고 row intrinsic width와 aggregate carrier outer width에 각각 +16px budget을 잠근 뒤 종료한다. OMD_PLAN_NOT_ATTEMPTED는 navigation 전 validation이라 plan browser 1회에 포함하지 않으며, artifact만 교정해 exact command를 다시 실행한다. 측정 뒤 semantic close만 실패하면 OMD_PLAN_MEASURED_RECONCILE_REQUIRED가 측정값을 보존하고 정확한 plan-packet 명령을 제공한다. packet은 모든 row/carrier 충돌과 complete patch를 한 번에 잠그며 artifact/diagnosis hash가 달라지면 fail-close한다. null accessible name만 명시적으로 채운 뒤 plan-apply 한 번으로 patch와 closure를 함께 적용하며, 이때 browser 재실행과 product edit은 금지된다. navigation 전 infrastructure error일 때만 위 source-fallback-open이 pending plan과 pre-edit source를 잠그며, 이 stamp도 없으면 static-close가 거부된다. 정상 measured 경로에서는 helper가 찍은 plan closure stamp와 불변 measured-plan hash가 계속 필수다. 둘 다 exact BU_NAME에 attach하며 새 browser나 fallback을 만들지 않는다.
- 검증 예산은
pre-edit fit-plan browser 1회 + pre-edit candidate static-preview(제품/계약 비변경) + task 전체 deterministic static-close helper 1회 + post-edit acceptance browser 1회다. candidate preview는 locked product가 unchanged인 동안만 허용되며 artifact의 closure attempt를 소비하지 않는다. plan browser는 제품 edit 전에만 실행한다. 편집 뒤에는 static-close 한 번과 post-edit acceptance browser 한 번만 실행하며, 결과가 red여도 제품을 다시 고치지 않고 unresolved를 전달한다.
- browser가 제품 결함을 찾아 실제 product edit이 필요할 때만 revision을 올리고
static_closure: open, browser_proof: unresolved로 다시 연다. corrective static closure는 한 번 수행할 수 있지만 browser attempt는 다시 열지 않는다. 제품 파일이 바뀌지 않았다면 어느 proof state도 reopen하지 않는다.
- 현재 revision의 static closure가
closed이고 browser proof가 closed|unresolved면 delivery: ready로 잠근다. 이 뒤 verification shell/browser command는 verification_after_ready 위반이다. 추가 탐색 대신 최소 완성 diff와 unresolved를 전달한다.