init: company-haness 설계

This commit is contained in:
DongHyeonka
2026-07-23 17:49:00 +09:00
parent 57d1bab894
commit f668d6a158
962 changed files with 98989 additions and 1 deletions
@@ -0,0 +1,191 @@
artifact-kind: visual-direction-concept
artifact-version: 1
identity:
concept-id: hyeonworks-vnext-v1-signal-trace
direction-id: signal-trace
representative-screen-id: dual-entry-shared-lab
producer-run-id: hyeonworks-vnext-v1-direction-divergence-signal-20260718T1135Z
basis:
context-package: hyeonworks/state/context-packages/hyeonworks-vnext-v1-direction/DES-VISUAL-20260718T113223Z.pkg.yaml
context-package-sha256: f2a89ef290b57ed34610b08e30b2a23d07e7244fec0991de81d61ca157d8ccf6
assigned-question: >-
두 세션의 신호와 값 흐름을 계측 rail처럼 추적하면 Lost Update의 시간적 인과를
더 빨리 이해하는가?
visual-thesis: >-
서로 다른 두 입력 단서를 밝은 계측 band로 분리해 시작하고, 명시적인 merge bus에서
하나의 Lab으로 수렴시킨 뒤, 같은 초기값을 읽고 150과 70을 순서대로 쓰는 과정을 단일
transaction trace로 읽게 한다. 화면의 개성은 terminal 표면이나 장식적 dashboard가 아니라
선로의 합류, 시간축 정렬, 숫자 probe, 충돌 hatch가 만드는 판독성에서 나온다. 두 진입의
차이는 orientation 메타데이터에만 남고, 합류 뒤에는 tx-lost-update-01과 공통 학습 루프를
오직 한 번만 렌더링한다.
reference-clusters:
- id: railway-junction-schematic
source-family: "railway signal diagram / interlocking schematic"
useful-signal: "독립된 입력선이 이름 붙은 junction에서 하나의 선로로 합류하는 구조"
translation:
- "CONCEPT와 SYMPTOM을 좌우 entry band로 분리한다."
- "band 아래 두 선은 SAME LAB 스탬프에서 합쳐지고 이후에는 한 선만 유지한다."
- "합류 전에는 두 band, 합류 후에는 하나의 trace table이라는 수량 규칙을 지킨다."
boundary: "실제 철도 관제 UI, 신호등 색 체계, 사실적인 선로 일러스트를 모사하지 않는다."
- id: logic-analyzer-timing-sheet
source-family: "logic analyzer trace / timing diagram"
useful-signal: "공통 시간축에 채널별 상태와 read/compute/write 선후관계를 정렬하는 방식"
translation:
- "01~04 행을 고정된 실행 순서로 두고 Session A와 B를 같은 행에서 비교한다."
- "현재 행동과 대기 상태를 같은 셀 문법으로 기록하며 장황한 서술 대신 동사+값을 쓴다."
- "마지막 overwrite 행 전체에 hatch를 적용해 충돌 구간의 범위를 드러낸다."
boundary: "파형, 네온 glow, 검은 console, 실행 중인 모니터링 제품처럼 보이는 chrome은 쓰지 않는다."
- id: calibration-instrument-panel
source-family: "industrial calibration panel / labeled value probe"
useful-signal: "숫자와 단위를 장식 없이 비교하고 각 값의 역할을 짧은 태그로 고정하는 방식"
translation:
- "Initial 100, Expected 120, Observed 70을 동일 크기의 value probe로 나란히 둔다."
- "Observed의 위험 신호는 red뿐 아니라 ALERT 문맥과 별도 probe 상태로 중복 부호화한다."
- "숫자는 tabular 형태의 monospace로 정렬하고 본문보다 강한 크기 대비를 준다."
boundary: "probe를 성과 지표 카드나 실시간 KPI로 늘리지 않고 이 시나리오의 세 값으로 제한한다."
- id: engineering-hatch-legend
source-family: "engineering drawing hatch / printed channel legend"
useful-signal: "색을 볼 수 없어도 패턴, 기호, 라벨로 채널과 예외 영역을 구분하는 방식"
translation:
- "Session A는 circle, Session B는 diamond와 반복 라벨로 구분한다."
- "Session B entry edge와 overwrite 구간에는 서로 다른 밀도의 diagonal hatch를 쓴다."
- "legend는 trace 바로 위에 놓아 기호를 기억해야 하는 거리를 줄인다."
boundary: "경찰 테이프나 incident severity 장식으로 읽힐 정도의 과한 경고 패턴은 피한다."
layout-grammar:
topology: "dual input bands → centered merge bus → single shared Lab → common learning loop → boundary"
desktop:
viewport-reference: "1280px 이상"
grid: "intro는 1.15fr/0.85fr, entry bands는 1fr/1fr, Lab은 0.65fr/1.35fr"
reading-order:
- "Atlas breadcrumb와 visual thesis"
- "GUIDED SCENARIO 경계"
- "두 entry band"
- "SAME LAB merge bus"
- "Lab metadata와 value probes"
- "transaction trace"
- "Predict → Observe → Compare → Explain → Transfer"
- "learning boundary"
density-rule: "trace는 중간~높은 밀도를 허용하되 entry 영역은 선택지 두 개 외의 카드를 만들지 않는다."
compact:
breakpoint-contract: "800px 이하에서 intro, bands, Lab을 한 열로 재배치하고 420px 이하에서 probe를 한 열로 쌓는다."
trace-reflow: "각 시간 행은 step 번호 다음 A 셀, B 셀 순으로 선형화하며 DOM 읽기 순서는 바꾸지 않는다."
merge-rule: "공간이 부족하면 장식용 두 번째 branch를 줄여도 SAME LAB 스탬프와 단일 도착점은 유지한다."
minimum-width: "360px에서 가로 스크롤 없이 boundary와 Transfer까지 도달한다."
spacing-rule: "rail 사이 수직 간격보다 섹션 전환 간격을 크게 해 입력·합류·실험의 위계를 보존한다."
interaction-grammar:
entry-selection:
control: "두 entry band 전체를 native link 또는 button으로 제공한다."
behavior: "어느 band를 선택해도 동일한 #shared Lab으로 이동하고 entryMode만 orientation 메타데이터로 기록한다."
state-boundary: "합류 후 scenarioId, reducer state, 단계 목록, 정답·설명 콘텐츠를 entry별로 복제하거나 분기하지 않는다."
keyboard: "Tab으로 각 band에 도달하고 Enter로 선택하며 focus-visible outline을 항상 표시한다."
trace-progression:
controls: "production 단계 제어는 native previous/next button을 사용하며 drag를 필수 입력으로 만들지 않는다."
announcement: "단계가 바뀌면 aria-live에 step 번호, session, action, value를 한 번 알리고 사용자의 focus는 이동시키지 않는다."
active-state: "현재 segment는 굵기+기호+짧은 pulse로 강조하며 완료·대기·충돌을 색만으로 구분하지 않는다."
learning-loop: "Predict를 완료한 뒤 Observe를 열고 Compare, Explain, Transfer 순서를 하나의 progress 구조로 유지한다."
prototype-scope: >-
현재 coded slice는 두 native anchor가 동일한 #shared DOM target을 참조해 shared-core 구조를
증명한다. runtime reducer 실행이나 실제 DB 연결을 시뮬레이션한다고 주장하지 않는다.
type-grammar:
display:
family: "precise grotesk sans; coded slice fallback은 Arial, Helvetica, sans-serif"
role: "한 화면의 질문과 Lab 목적만 크게 표시"
treatment: "큰 x-height, 단단한 800 weight, 음수 letter-spacing; 장식적 italic 금지"
body:
family: "same sans family"
role: "학습 의도, 경계, 짧은 인과 설명"
measure: "한 문단 최대 약 58ch"
signal-label:
family: "ui-monospace, monospace"
role: "scenario id, channel code, execution step, compact uppercase label"
treatment: "0.68~0.82rem, 700~800 weight, 제한된 letter-spacing"
numeric:
family: "ui-monospace, monospace"
treatment: "font-variant-numeric: tabular-nums; Initial/Expected/Observed의 baseline을 맞춘다."
language-rule: "한국어를 기본 설명 언어로 두고 SQL·transaction 동작과 compact signal label만 원어를 병기한다."
primitive-grammar:
entry-band:
anatomy: "channel code + 한국어 진입 라벨 + orientation 단서 + 방향 표시"
rule: "합류 전에 정확히 두 개만 존재하고 각 band의 edge treatment로 채널을 중복 부호화한다."
merge-bus:
anatomy: "두 branch line + centered SAME LAB stamp + scenario id"
rule: "두 진입이 별도 결과로 이어진다는 인상을 주는 분기선을 Lab 아래로 연장하지 않는다."
trace-rail:
anatomy: "step number + Session A cell + Session B cell"
rule: "행은 실제 실행 순서를 보존하고 read/compute/write 동사와 값을 함께 기록한다."
signal-stamp:
anatomy: "짧은 uppercase 상태 + 필요 시 scenario id"
rule: "merge와 경계처럼 의미가 고정된 지점에만 쓰며 badge cloud로 늘리지 않는다."
conflict-hatch:
anatomy: "diagonal pattern + overwrite text + Session B diamond"
rule: "충돌이 일어난 04행에만 적용하고 red 단독 의미 전달을 금지한다."
value-probe:
anatomy: "role label + tabular numeric value"
rule: "Initial/Expected/Observed 세 값의 동일 비교에만 사용한다."
semantic-encoding:
tokens:
background: { value: "#eef2f2", role: "밝은 계측 바탕" }
panel: { value: "#f9fbfa", role: "entry와 Lab 표면" }
ink: { value: "#102b31", role: "본문과 구조선" }
trace-blue: { value: "#176c8a", role: "Session A와 concept input", non-color-cue: "circle + CONCEPT/A label" }
trace-red: { value: "#a24a3f", role: "Session B와 overwrite", non-color-cue: "diamond + hatch + SYMPTOM/B label" }
merge-green: { value: "#165f54", role: "공통 Lab junction", restriction: "일반 CTA에는 사용하지 않음" }
focus: { value: "#7b2e95", role: "키보드 focus outline 전용" }
contrast-rule: "본문, signal label, control 상태는 panel/background 위 WCAG AA 대비를 유지한다."
status-rule: "모든 의미 상태는 text, symbol, shape, pattern 중 최소 하나를 color와 함께 제공한다."
motion-grammar:
allowed: "사용자가 실행한 현재 rail segment의 짧고 국소적인 pulse"
timing: "한 번의 180~240ms emphasis; 자동 반복과 배경 이동 금지"
reduced-motion: "prefers-reduced-motion에서는 pulse를 제거하고 3px line, pattern, CURRENT text로 대체"
prohibited: "glow, scanline, terminal cursor, 자동 재생 trace, parallax"
content-contract:
required-copy:
entries: ["개념을 알고 있어요", "증상만 알고 있어요"]
scenario-id: tx-lost-update-01
guided-label: GUIDED SCENARIO
values: { initial: 100, expected: 120, observed: 70, a-write: 150, b-write: 70 }
learning-loop: [Predict, Observe, Compare, Explain, Transfer]
atlas-path: [Concurrency, Transaction Isolation, Lost Update]
trust-boundary: >-
운영 로그를 분석하거나 실제 장애 원인을 확정하지 않으며, 고정된 조건에서 검증할 후보
메커니즘을 학습한다는 설명을 symptom 진입과 Lab 주변에 유지한다.
coded-slice-map:
entry-bands: "index.html #entry .bands; 두 href가 모두 #shared"
merge-junction: "index.html .bus .merge; same lab · tx-lost-update-01"
shared-lab: "index.html section#shared.lab; 문서에 단 한 번 존재"
transaction-trace: "index.html .trace [role=table]; 100 read → 150/70 write의 4단계"
common-loop: "index.html .loop; shared Lab 뒤에 단 한 번 존재"
guided-boundary: "index.html .readout과 .boundary"
accessibility-contract:
- "skip link는 두 entry band가 시작되는 #entry로 이동한다."
- "entry control과 향후 step control은 native semantics를 유지한다."
- "focus-visible은 배경과 분명히 구별되는 3px outline과 offset을 사용한다."
- "trace는 table/row/cell 의미를 제공하고 모바일에서도 step→A→B 읽기 순서를 유지한다."
- "360/768/1280px에서 가로 스크롤 없이 공통 학습 루프를 끝까지 읽고 조작할 수 있어야 한다."
- "색 외에 circle/diamond, label, hatch, text로 session과 충돌을 구분한다."
- "prefers-reduced-motion에서는 모든 비필수 transition과 animation을 제거한다."
do-not-use:
- neon terminal 또는 검은 hacker console
- glow, scanline, 실제 monitoring chrome
- dashboard KPI grid
- 색만으로 구분한 session과 충돌
- drag 전용 timeline
- 실제 장애 진단 또는 원격 DB 분석을 암시하는 카피
- 과장된 atlas map이나 준비 중 콘텐츠의 가짜 catalog
- 합류 후 entry별 Lab, reducer, progress, 설명 콘텐츠 복제
@@ -0,0 +1,37 @@
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>Hyeonworks — Signal Trace direction</title>
<style>
:root{--bg:#eef2f2;--panel:#f9fbfa;--ink:#102b31;--muted:#52686c;--line:#9bb0b2;--a:#176c8a;--b:#a24a3f;--merge:#165f54;--soft:#dcebea;--focus:#7b2e95}
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font-family:Arial,Helvetica,sans-serif;line-height:1.45}a{color:inherit}.skip{position:absolute;top:-5rem;left:1rem;background:var(--ink);color:white;padding:.7rem 1rem;z-index:5}.skip:focus{top:1rem}:focus-visible{outline:3px solid var(--focus);outline-offset:4px}
header{min-height:64px;display:grid;grid-template-columns:1fr auto 1fr;align-items:center;padding:0 clamp(1rem,4vw,4rem);border-bottom:1px solid var(--line);font-size:.72rem;text-transform:uppercase;letter-spacing:.1em}.brand{font-weight:900}.status{justify-self:center;display:flex;align-items:center;gap:.5rem}.status:before{content:"";width:.55rem;height:.55rem;border-radius:50%;background:var(--merge)}.scope{justify-self:end;color:var(--muted)}
main{max-width:1500px;margin:auto;padding:clamp(1.2rem,4vw,4rem)}.path{font:700 .72rem ui-monospace,monospace;color:var(--muted);letter-spacing:.06em;text-transform:uppercase}.path b{color:var(--ink)}
.intro{display:grid;grid-template-columns:1.15fr .85fr;gap:3rem;align-items:end;padding:clamp(2rem,5vw,5rem) 0 2rem}.intro h1{font-size:clamp(3rem,7vw,7.2rem);line-height:.86;letter-spacing:-.065em;margin:.8rem 0 1.4rem;font-weight:800;max-width:9ch}.intro p{max-width:58ch;color:#334e53;font-size:1.04rem}.readout{background:var(--ink);color:white;padding:1rem 1.1rem;display:grid;grid-template-columns:auto 1fr;gap:.8rem;font:600 .78rem ui-monospace,monospace}.readout b{color:#9fe0d5}.readout p{margin:0;color:#d7e2e1;font:400 .8rem/1.5 Arial,sans-serif}
.entry-label{display:flex;justify-content:space-between;gap:1rem;align-items:end;margin:2.2rem 0 .8rem}.entry-label h2{font-size:1rem;text-transform:uppercase;letter-spacing:.13em;margin:0}.entry-label span{font:700 .7rem ui-monospace,monospace;color:var(--muted)}
.bands{display:grid;grid-template-columns:1fr 1fr;gap:1rem}.band{position:relative;display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:1rem;background:var(--panel);border:1px solid var(--ink);padding:1.2rem;text-decoration:none;min-height:150px;overflow:hidden}.band:before{content:"";position:absolute;left:0;top:0;bottom:0;width:7px;background:var(--a)}.band.symptom:before{background:repeating-linear-gradient(135deg,var(--b) 0 5px,#f4c3bb 5px 10px)}.band:hover{background:var(--soft)}.code{font:800 .72rem ui-monospace,monospace;writing-mode:vertical-rl;transform:rotate(180deg);color:var(--a)}.symptom .code{color:var(--b)}.band h3{font-size:clamp(1.5rem,3vw,2.7rem);line-height:1;margin:.35rem 0}.band p{margin:.7rem 0 0;color:var(--muted);max-width:38ch}.go{font:800 1.5rem ui-monospace,monospace}
.bus{position:relative;height:110px;margin:0 12%;border-left:2px solid var(--a);border-right:2px solid var(--b)}.bus:before,.bus:after{content:"";position:absolute;top:54px;width:50%;border-top:2px solid}.bus:before{left:0;border-color:var(--a);transform:skewY(8deg);transform-origin:left}.bus:after{right:0;border-color:var(--b);transform:skewY(-8deg);transform-origin:right}.merge{position:absolute;left:50%;top:40px;transform:translateX(-50%);background:var(--merge);color:white;padding:.6rem .85rem;font:800 .7rem ui-monospace,monospace;letter-spacing:.08em;text-transform:uppercase;white-space:nowrap}
.lab{background:var(--panel);border:2px solid var(--ink);display:grid;grid-template-columns:minmax(240px,.65fr) 1.35fr}.lab-meta{padding:1.4rem;border-right:1px solid var(--line)}.lab-meta .id{font:700 .7rem ui-monospace,monospace;color:var(--merge)}.lab-meta h2{font-size:clamp(2rem,4vw,4rem);letter-spacing:-.04em;line-height:.95;margin:1rem 0}.measure{display:grid;grid-template-columns:repeat(3,1fr);gap:.5rem;margin-top:1.4rem}.probe{padding:.7rem;background:var(--bg);font:700 .68rem ui-monospace,monospace;text-transform:uppercase}.probe strong{display:block;font-size:1.45rem;color:var(--ink);margin-top:.2rem}.probe.alert strong{color:var(--b)}
.trace{padding:1.5rem;overflow:hidden}.trace-head{display:flex;justify-content:space-between;gap:1rem;font:700 .7rem ui-monospace,monospace;text-transform:uppercase;color:var(--muted);margin-bottom:1.2rem}.rail{display:grid;grid-template-columns:3.4rem 1fr 1fr;align-items:stretch;min-width:0}.rail>*{padding:.75rem .65rem;border-top:1px solid var(--line)}.rail .n{font:700 .7rem ui-monospace,monospace;color:var(--muted)}.cell{position:relative;font:700 .82rem ui-monospace,monospace}.cell:before{content:"";display:inline-block;width:.65rem;height:.65rem;border-radius:50%;background:var(--a);margin-right:.55rem}.cell.b:before{background:var(--b);border-radius:0;transform:rotate(45deg)}.cell.off{color:#879597}.cell.off:before{background:#b7c1c1}.rail.conflict{background:repeating-linear-gradient(135deg,transparent 0 8px,#f3e0dd 8px 16px)}
.loop{display:flex;gap:.55rem;flex-wrap:wrap;margin-top:1rem}.loop span{flex:1;min-width:120px;border:1px solid var(--line);padding:.75rem;font:700 .7rem ui-monospace,monospace;text-transform:uppercase}.loop b{color:var(--merge);margin-right:.4rem}
.boundary{display:flex;gap:1rem;margin-top:1rem;padding:.8rem 1rem;border-left:7px solid var(--merge);background:#d9e5df;font-size:.85rem}.boundary b{white-space:nowrap;text-transform:uppercase;font-size:.7rem;letter-spacing:.06em}
@media(max-width:800px){header{grid-template-columns:1fr auto}.status{display:none}.intro{grid-template-columns:1fr}.bands{grid-template-columns:1fr}.bus{height:80px;margin:0 20%;border-right:0}.bus:after{display:none}.merge{top:27px}.lab{grid-template-columns:1fr}.lab-meta{border-right:0;border-bottom:1px solid var(--line)}.measure{grid-template-columns:repeat(3,1fr)}.rail{grid-template-columns:2.5rem 1fr}.rail .cell:nth-child(3){grid-column:2}.rail .cell:nth-child(3){border-top:0;padding-top:0}.boundary{display:block}.boundary b{display:block;margin-bottom:.3rem}}
@media(max-width:420px){.scope{display:none}header{grid-template-columns:1fr}.measure{grid-template-columns:1fr}.intro h1{font-size:3.3rem}}
@media(prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}
</style>
</head>
<body><a href="#entry" class="skip">출발 신호로 건너뛰기</a>
<header><div class="brand">Hyeonworks / Signal Trace</div><div class="status">Shared core online</div><div class="scope">Scenario 01 / Lost Update</div></header>
<main><div class="path">Concurrency / Transaction Isolation / <b>Lost Update</b></div>
<section class="intro" aria-labelledby="title"><div><h1 id="title">두 단서가 하나의 원인으로 수렴합니다.</h1><p>개념을 추적하거나 증상을 추적하세요. 입구의 신호만 다르고, 두 경로는 같은 Lost Update 실험과 상태 전이에서 합류합니다.</p></div><aside class="readout"><b>GUIDED<br/>SCENARIO</b><p>운영 로그를 분석하거나 장애 원인을 확정하지 않습니다. 고정된 증상에서 검증할 메커니즘 후보를 배우는 시나리오입니다.</p></aside></section>
<section id="entry" aria-labelledby="entry-title"><div class="entry-label"><h2 id="entry-title">Input channels</h2><span>choose one · merge before experiment</span></div><div class="bands">
<a class="band" href="#shared"><span class="code">CONCEPT</span><span><strong>개념을 알고 있어요</strong><h3>Isolation → conflict</h3><p>격리와 읽기·쓰기 충돌의 관계를 따라갑니다.</p></span><span class="go"></span></a>
<a class="band symptom" href="#shared"><span class="code">SYMPTOM</span><span><strong>증상만 알고 있어요</strong><h3>Expected 120 → got 70</h3><p>성공한 두 요청과 예상 밖의 최종값에서 단서를 좁힙니다.</p></span><span class="go"></span></a>
</div><div class="bus" aria-hidden="true"><span class="merge">same lab · tx-lost-update-01</span></div></section>
<section id="shared" class="lab" aria-labelledby="lab-title"><div class="lab-meta"><div class="id">SHARED LAB / TX-LOST-UPDATE-01</div><h2 id="lab-title">값의 경로를 추적하세요.</h2><p>두 세션은 모두 성공했습니다. 같은 초기값을 읽은 뒤 각자 계산하고 순서대로 썼습니다.</p><div class="measure"><div class="probe">Initial<strong>100</strong></div><div class="probe">Expected<strong>120</strong></div><div class="probe alert">Observed<strong>70</strong></div></div></div>
<div class="trace"><div class="trace-head"><span>execution rail</span><span>A = circle · B = diamond</span></div><div role="table" aria-label="두 세션 실행 순서"><div class="rail" role="row"><div class="n" role="cell">01</div><div class="cell" role="cell">A reads 100</div><div class="cell off" role="cell">B waiting</div></div><div class="rail" role="row"><div class="n" role="cell">02</div><div class="cell off" role="cell">A holds 100</div><div class="cell b" role="cell">B reads 100</div></div><div class="rail" role="row"><div class="n" role="cell">03</div><div class="cell" role="cell">A writes 150</div><div class="cell off" role="cell">B computes 70</div></div><div class="rail conflict" role="row"><div class="n" role="cell">04</div><div class="cell off" role="cell">150 overwritten</div><div class="cell b" role="cell">B writes 70</div></div></div></div></section>
<section class="loop" aria-label="공통 학습 단계"><span><b>01</b>Predict</span><span><b>02</b>Observe</span><span><b>03</b>Compare</span><span><b>04</b>Explain</span><span><b>05</b>Transfer</span></section>
<aside class="boundary"><b>Learning boundary</b><span>이 trace는 결정론적 교육 모델이며 실제 데이터베이스의 모든 스케줄과 격리 구현을 대표하지 않습니다.</span></aside>
</main>
</body></html>