Lyotic · 개요

개발하기

애플리케이션이 소유한 업무 상태를 plan으로 변환하고, 컴포넌트가 허용된 표현을 렌더링합니다. 패키지는 일반 ES 모듈이며 사이트에 프레임워크 빌드 단계가 필요하지 않습니다.

패키지

패키지책임
@lyotic/core입력 해석, 결정론적 plan, 실행 시 재검증과 계약 검사
@lyotic/componentsplan의 정보와 제어를 DOM으로 표현
@lyotic/morph공간 전환, 콘텐츠 전환, 주의 요청과 포커스 동작
@lyotic/tokens기초 값, 의미 표현 역할, 상태와 시각 언어의 연결

책임의 경계

애플리케이션은 도메인 객체와 실행 경계를 소유한다. 어댑터는 외부 시스템이 제공하는 읽기, 쓰기, 버전, 결과 확인 능력을 명시한다. Lyotic의 계약 계층은 그 입력이 표현에 필요한 조건을 충족하는지 검사한다. Resolver는 적합한 표현과 필수 정보, 허용 제어를 결정한다. Renderer는 이를 접근 가능한 컴포넌트로 구현한다. 테스트 하네스는 같은 입력과 이벤트에서 계약이 유지되는지 검증한다. 이러한 역할 분리는 특정 데이터베이스나 프레임워크를 강제하지 않으면서도 책임이 사라지지 않게 한다.

원문에서 말한 deterministic executor도 외부 세계가 결정론적으로 변한다는 보장으로 해석하면 안 된다. 실행기는 허용된 요청을 정해진 규칙에 따라 전달할 수 있지만, 원격 시스템의 처리 지연이나 부분 실패까지 없앨 수는 없다. 따라서 "결정론적 실행기를 사용했으니 안전하다"는 주장은 부족하다. 어떤 요청을 전달했는지, 어떤 결과를 관찰할 수 있는지, 불확실한 경우 어떤 후속 행동을 막거나 허용하는지가 함께 필요하다.

Resolver

결정론적 변환은 모든 화면이 같은 모양이어야 한다는 뜻이 아니다. 같은 업무 상태, 같은 역할, 같은 정책 버전, 같은 표현 조건이 들어왔을 때 사실의 의미와 행동의 허용 범위가 임의로 달라져서는 안 된다는 뜻이다. 색상이나 레이아웃은 허용된 범위에서 달라질 수 있다. 그러나 모델이 바뀌었다는 이유로 미확정 결과가 완료로 바뀌거나, 읽기만 가능한 작업에 실행 버튼이 생겨서는 안 된다. 중요한 상태와 행동의 의미는 문장 생성의 우연성 밖에 놓여야 한다.

이 계약의 입력에는 Work Case와 그 revision, 관련 Work Object, 현재 역할과 권한, Observation과 Evidence, 미해결 Claim, 제안된 행동, 영향 범위, 실행 기록, 검증 결과, 사용자가 현재 보고 있는 표현, 접근성 요구가 포함된다. 출력은 자유로운 HTML이 아니라 Representation Plan이다. Representation Plan은 무엇을 중심 객체로 보여줄지, 어떤 정보를 반드시 노출할지, 어떤 관계를 설명할지, 어떤 행동을 제공할지, 어떤 완료 표현을 금지할지, 다음 전환의 조건은 무엇인지를 결정한다. 렌더러는 그 계획을 실제 인터페이스로 표현한다.

import { resolve, validateAtExecution } from '@lyotic/core';

const plan = resolve({
  case, objects, role, sources, observations, claims, interpretations,
  proposals, alternatives, decision, executions, verifications, preconditions,
  adapters, control, policy,
  view: { mode: 'compact', request: null },   // what the person is looking at now
});

plan.mode        // 'compact' | 'compare' | 'decide' | 'authorize' | 'execute' | 'verify' | 'recover' | 'handoff'
plan.systemMode  // what the work calls for, even when the person chose another mode
plan.attention   // { level: 'none' | 'requested', reason }
plan.mustShow    // disclosures the renderer may not omit
plan.controls    // [{ action, allowed, reason, boundVersion }], disallowed ones explained
plan.forbidden   // completion words not allowed now
plan.reason      // one sentence: why this form, tied to the state change
plan.announce    // [{ politeness, text }] for the live region

렌더링

동일한 WorkObjectShell 인스턴스를 유지하면서 새 입력과 plan을 전달합니다. 제어는 plan.controls에서만 가져오고, 전환을 시작한 주체와 키보드 여부를 명시합니다. 실행 이벤트를 받는 애플리케이션은 boundVersion을 현재 버전과 다시 대조해야 합니다.

React는 필수 의존성이 아닙니다. 컨테이너가 마운트될 때 셸을 만들고 상태 변경 때 렌더링하는 통합 예제입니다. 앱의 해제 시점에는 사용하는 모션 인스턴스와 관찰자, 이벤트 구독도 정리해야 합니다.

import { WorkObjectShell } from '@lyotic/components';

const shell = new WorkObjectShell(host);
shell.render(state, plan, { initiator: 'system' });   // background update: never moves focus
shell.render(state, plan, { initiator: 'person' });   // the person asked: focus follows them

host.addEventListener('ly-action', (e) => { /* e.detail: { action, boundVersion } */ });
host.addEventListener('ly-explore', (e) => { /* person asked to expand or collapse */ });
host.addEventListener('ly-mode', (e) => { /* { from, to, reason } */ });

React에서 연결하기

컨테이너 수명주기에 맞춰 셸을 생성하고 상태를 전달합니다. 데이터 구독·실행 권한·해제 처리는 애플리케이션이 소유합니다.

function WorkObject({ state, onAction }) {
  const ref = useRef(null);
  const shell = useRef(null);
  useEffect(() => { shell.current = new WorkObjectShell(ref.current); return () => shell.current.morph.destroy(); }, []);
  useEffect(() => { shell.current.render(state, resolve(state)); }, [state]);
  useEffect(() => {
    const on = (e) => onAction(e.detail);
    ref.current.addEventListener('ly-action', on);
    return () => ref.current?.removeEventListener('ly-action', on);
  }, [onAction]);
  return <div ref={ref} />;
}

불변 조건 검사

컴포넌트 검사와 사람 대상 평가 사이에도 구분이 있다. 스키마 검사는 필수 필드와 타입을 확인할 수 있다. 의미 검사는 해당 상태에서 완료 표시나 실행 제어가 허용되는지 확인한다. 시간에 따른 검사는 승인 이후 변경, 늦은 응답, 중지, 재접속 과정에서 계약이 유지되는지 본다. 사람 대상 평가는 그 표현을 사용자가 실제로 이해하고 적절히 개입하는지를 확인한다. 앞의 검사를 통과했다고 뒤의 효과까지 자동으로 성립하지 않는다.

Invariant 테스트는 구체적인 위반을 대상으로 해야 한다. 동일한 Case에 테마만 바꿨을 때 허용 행동이 달라지는지, 대상 revision이 바뀌었는데 이전 승인이 유지되는지, 성공 응답 뒤에 불일치가 관찰되었는데 완료 표시가 남는지, 복구 후 새 세션이 미확정 효과를 잊는지 검사한다. 잘못된 레코드 매칭, 오래된 근거, 숨은 추가 변경, 검증 출처의 부재도 포함한다. 테스트가 관찰할 수 없는 영역은 통과로 처리하지 않고 커버리지의 한계로 남긴다.

import { checkPlan, checkDom } from '@lyotic/core';

const a = checkPlan(state, plan);   // semantic: completion, authority, versions, partial outcomes…
const b = checkDom(host, plan);     // rendered: operable controls ⊆ plan, forbidden words, glyph + label, disclosures
[...a.violations, ...b.violations]  // [{ id: 'INV-03', message }]
[...a.limits, ...b.limits]          // what could not be checked

토큰

토큰 구조도 원문이 수정한 네 계층을 유지한다. Foundation Token은 색상, 타이포그래피, 간격, 모서리, 모션, 높이감, 밀도, 포커스 같은 기초 표현 값이다. Semantic Presentation Token은 Conflict, Proposed, Verified 같은 상태를 표현할 때 사용하는 역할이다. Interaction State Variable은 인식 상태, 권한, 영향, 실행, 검증, 최신성 같은 실제 런타임 데이터다. Policy Rule은 이 조합에서 어떤 행동과 표현이 허용되는지 결정한다. Risk와 Authority를 색상 토큰처럼 취급하면 정책과 표현의 책임이 섞인다.

node scripts/build-tokens.mjs   # writes packages/tokens/dist/lyotic.css and tokens.json

저장소 구성

packages/는 런타임 구현, docs/spec/는 정본, site/는 문서, demos/work-creates-interface/는 시각·모션 참조 영상입니다. tests/e2e/는 실제 브라우저의 문서·전환·포커스 검사를 담당합니다.

docs/spec/00-definition.ko.md   # the canonical definition (wins over everything)
docs/spec/01-model.md           # the buildable model: IDs every file cites
packages/tokens                 # layer 1 and 2
packages/core                   # layer 4: resolver, invariants, fixtures, tests
packages/morph                  # motion
packages/components             # semantic components
site/                           # this documentation, built from the packages