Appearance
변경 기록
Unreleased
S-44-23: AGENTS.md가 kit
docs/plans/local-links.md를 가리킨다. polyspec 버전이 0.0.x인 동안 개발 checkout은 설치된 polyspec package를 그 source에 link하며, link는 commit하지 않는다.S-44-22: 공유 도구가 kit 0.0.53이다. 그 release proof는 release가 각 sibling wheel의 asset을 나열하는지 검사한다.
S-44-21: 공유 도구가 kit 0.0.52다. 그 kit-check는 fsm의
.php-version을 요구하며, 그 release workflow는 release 단계 전에 kit-check를 실행한다.S-44-20:
.php-version이config/toolchain.json의 minor인 PHP 8.5를 선언하며, workflow의 모든 setup-php 단계가 이를 읽는다.S-44-19: 공유 도구가 kit 0.0.51이다.
make toolchain-lock은 machine의 node로 시작하고,config/knip-entries.json은 script와 test가 실행하는 go, make, php를 선언한다.S-44-15:
make lint-ts가 통과하고make check가 이를 실행한다.config/knip-entries.json은 packages/db, packages/gui, packages/js, docs를 knip workspace로 선언한다.S-44-17: 공유 도구가 kit 0.0.45다. 그 knip은 고정된 runtime의 binary를 무시하고,
config/knip-entries.json이 workspace로 선언한 중첩 npm project를 읽는다.S-44-15: 모든 kind에서 orm이 v0.0.15다(npm과 fsm-db tarball, go module, rapidyaml과 ruamel.yaml을 쓰는 python wheel, cargo tag, purrphp/yaml을 쓰는 Composer zip). 그 생성된 TypeScript model은 쓰는 것만 import하고, 생성된 Rust model에는 allow attribute가 없다. Go import는
github.com/polyspec/orm/packages/go/...를 읽고,config/knip-entries.json은 orm이 설계상 MANIFEST_TEXT와 MANIFEST_HASH를 export하는 생성된 models.ts를 선언한다.S-44-15:
config/knip-entries.json은 kit의 layout entry 밖에서 make나 테스트가 실행하거나 경로로 읽는 파일(fsm-gen과 fsm-gui 명령, GUI client entry, golden npm 파일, 생성된 declaration과 그 consumer, 문서 사이트 설정)과 손으로 실행하는 GUI 프로그램 두 개를 선언하므로 knip은 unused file을 보고하지 않는다. write-generated.mjs의isGenerated는 export가 아니다.S-44-8-1:
config/release-assets.json이 orm과 ordered-json의 release zip을 고정하고, kit의make release-assets-fetch가 그것을 packages/php와 packages/php-ext의 Composer artifact repository인 var/composer-artifacts에 받는다.composer-artifactsrecipe와config/composer-artifacts.txt는 제거했다.S-44-16: 공유 도구가 kit 0.0.44다.
make install-tools가rust-toolchain.toml의 Rust toolchain을 설치하므로 CI는 자체 rustup step을 실행하지 않고, ruff는 cache를 var/ruff에 두며, knip은config/knip-entries.json의 entry를 받는다.S-44-14: fsm의 PHP가 phpcs를 통과한다. 생성된 ConformanceTest.php는 method
testConformance하나를 data providercases()위에서 실행하고 case마다 이름이<feature>__<id>인 data set을 두며 parity는 이를 symbol로 실행한다(kit K137). 생성된 throw는 인자 하나에 한 줄을 쓴다.S-44-13: rustls는 RUSTSEC-2026-0346~0349를 고친 0.23.46이고 playwright는 1.64.1이다.
S-44-12: fsm의 PHP 파일은
<?php뒤에 빈 줄을 둔다(PSR-12). 그래서 생성된 PHP 파일의 fsm-gen header는 셋째 줄이다.S-44-11: edge가 없는 machine의 생성된 rust가 clippy를 통과한다.
target은 wildcard arm 하나의 match 대신 오류만 반환한다.S-44-9: 각 kind의 모든 test가 parity binding의 test다. persist test는 여섯 kind 모두에 bind된 네 feature unit(한 row의 동시 transition 두 개와 deadlock 재시도)이고, npm 타입 검사는 root 도구 test tests/npm-types이며, rust conformance target은 module vectors.rs를 가진 tests/conformance/main.rs다.
S-44-10: 공유 도구는 kit 0.0.43이다. parity는 cargo target
tests/<name>/main.rs의 module 파일을 그 binding의 파일로 세고, oxlint 설정은 node:test의 등록을 허용하며, knip은 layout의 scripts, benchmarks, test를 entry로 삼고, API Extractor는 더 이상 모든 export에 release tag를 요구하지 않는다.S-44-5: 폴더가 kit의 layout을 따른다. package는 packages/compiler, db, gui, go, js(npm kind), php, php-ext, python, rust이고, 저장소의 fsm 프로젝트(fsm.config.json, fsm.lock.json, machines/, schema/, conformance/vectors.json)는 contracts/이며, db store의 table은 packages/db/schema/fsm.dbs다. 루트 script와 도구 테스트는 TypeScript(.mts)다. Go module 경로는 github.com/polyspec/fsm/packages/go다(이전에는 github.com/polyspec/fsm-go).
S-44-8: 의존성이 최신 release다. 모든 kind에서 orm v0.0.14와 ordered-json v0.0.8을 쓰며 그 generator는 set 파일
dbspec-set.json을 읽는다. Composer는config/composer-artifacts.txt가 sha256으로 고정한artifactrepository에서 orm과 ordered-json의 release zip을 받으며, 그 metadata를 복사한 Composerpackage는 사라졌다. python은 release wheel을 받는다. hyper v0.0.13, mysql2 3.24.6, mermaid 12.2.0, PHPUnit 13.4.2이다. npm 설치는--allow-remote=root를 주고, package 테스트는 kit의 run-tests를 checkout root에서 실행하며, CI는 자기 Go를 설치하지 않는다.S-44-7: 공유 도구는 kit 0.0.42다. mise.lock은 linux-arm64와 macos-x64의 tool도 pin하고, bootstrap이
var/tools/go.mod를 써서 mise의 Go가 별도 module이 된다.S-44-6: 공유 도구는 kit 0.0.40이며, 그 mise가
mise.toml의 toolchain을mise.lock의 digest로 설치한다. python package는 mise shim의 uv와 Python을 실행한다. checkout Python의 test가 다시 parse된다. S-44-4에서 따옴표가 그것을 깨뜨렸다.S-44-4: 공유 도구는 release asset과 integrity로 pin한 npm package
@polyspec/kit0.0.38이다. Makefile은 복사된scripts/kit/kit-bootstrap.mk를 include하고, vendor한scripts/kit/,tests/kit/,.kit/kit.lock.json,kit.json은 없어졌으며,packages/fsm-db의 오래된 TypeScript 예외를 지웠다.make layout-check는 S-44-5로 red가 기록되었다.S-44-3: config/parity.json이 kit의 parity schema를 따른다:
PYTHONPATH가 없고, go의 생성 vector와 php-ext의 stub과 header는support파일이며, compiler, DB, GUI, 루트 도구의 테스트는toolsroot다.S-44-2: toolchain이 kit의 pin을 따른다: Node.js 26.11.1, PHP 8.5, Python 3.14, Go 1.27.2(toolchain 줄 없음), Rust 1.99.0, TypeScript 7.0.2. packages/fsm-db의 orm TypeScript 6.0.3은 예외로 선언했다. uv와 alint는 config/toolchain.json에 선언하여 var/tools에 설치하며, CI 워크플로는 uv를 따로 설치하지 않는다.
S-44-1: compiler는 kit 파일을 import하지 않는다.
src/io/files.mjs와src/io/git.mjs가 node 내장 모듈로 파일을 원자적으로 쓰고 git을 실행하며, import 테스트는scripts/kit로 가는 경로를 거부한다(plan C-5).S-43: AGENTS.md(.ko) 규칙을 추가했다: 기존 외부 도구는 있는 그대로 쓰고(그 도구의 symbolic link는 받아들임), 저장소는 symbolic link와 상대 경로를 만들지 않으며, 경로는 추적 파일에 이름을 적거나 명시된 탐색 규칙으로 찾는다.
S-34-4: python lock은 uv 자체가 검사한다(
uv lock --check와 pylock.toml에 대한 uv.lock의 export 비교). fsm 자체의 lock 스크립트를 제거했다.S-42-2: fsm 자체의 vector parity 스크립트(
scripts/check-parity.mjs,vector-parity-check,parity-<kind>)를 제거했다. kit의 parity 검사가 그 일을 하고, kind의 소유 검사는 패키지 테스트를 실행한다.S-34-3: python lock은 registry package와 release archive만 담는다: orm과 ordered-json은 v0.0.7 tag의 archive와 sha256으로 받으므로 uv와 pip 모두 lock을 hash와 함께 설치하고, lock 검사는 git source를 거부한다.
S-42-1: vector parity가 kit의 parity 검사(K33)로 실행된다: config/parity.json, case마다 이름을 가진 vector 파일 하나 conformance/vectors.json, case마다의 PHPUnit method. scripts/check-parity.mjs는 기록, 시간 제한, feature 커버리지 규칙만 남긴다.
S-21: fsm은 https://github.com/polyspec/fsm 에 공개되어 있다. CI workflow(
.github/workflows/ci.yml)는 main의 모든 push에서 PostgreSQL과 MySQL service와 함께make install과make check를 실행하고 전체 검사를 통과했으며(run 38121153369), Pages workflow는 문서 사이트를 https://polyspec.github.io/fsm/ 에 게시한다. 로컬 저장소 디렉터리 이름은fsm이다.S-21-1: CI와 Pages는 ubuntu-26.04에서 돈다. orm의 PHP client는 SQLite 3.46을 요구하는데 ubuntu-24.04의 PHP는 SQLite 3.45.1에 연결되기 때문이다. CI run 38121153369가 그곳에서 전체 검사 47개 target을 모두 통과했다(tree 4963dbe).
S-41-1: python package venv는 fsm Makefile의
python -m venv가 아니라 kit 도구(make python-venv DIR=packages/fsm-python/.venv, kit v0.0.24의 K44)로 만든다. Linux에서 CPython venv는lib64 -> liblink를 만들며 run-tests --venv가 이를 거부했고, 이것이 S-41의 ubuntu CI를 실패시켰다(실행 38118092530). kit 도구가 그 link를 지운다. tests/python-tools의 테스트는make -n install이 kit 도구를 부르는지 단언한다(-m venv일 때 RED).make install은 이전과 같이 새 venv를 만들기 전에 기존 venv를 지운다.S-35-3:
make check가 다시vector-parity-check를 실행한다. rebase가 CHECK_TARGETS에 kit 이름parity-check를 되돌려 놓아 스위트가 fsm의 vector parity 대신 kit K33의 검사를 실행했으며, 이제 make-install 테스트가 그 타깃을 단언한다.S-42: parity 검사기는
make check에서 하나만 돈다.CHECK_TARGETS가 kit의parity-check를 가리키고 있었다(S-35-2가 fsm 타깃의 이름은 바꿨지만 목록은 바꾸지 않았다). 그래서 벡터 기록은 한 번도 검사되지 않았다. 이제vector-parity-check를 가리키며, tests/check-parity의 테스트가 Makefile을 읽어 이를 요구한다. kit K33은 채택하지 않는다: 각 binding을 스스로 실행하고 기록을 읽지 않으므로 기록,kinds, feature, 도구 테스트, 시간 규칙을 표현할 수 없다(계획 C-4가 규칙마다 적었다).S-34-1: kit dependency gate는 python lock의 모든 전이 python package를 덮는다: 최신 릴리스와 advisory 리뷰는
pythonLocks를 통해packages/fsm-python/pylock.toml을 읽는다(kit K36). RED: kit의 python-lock fixture 테스트(K36)가 kit-test에서 통과한다. fsm에서pythonLocks가 비면dependency-policy-check가 실패하고, lock을 나열하고 review한 뒤 통과한다.S-34-2: python lock은
packages/fsm-python/pylock.toml이며,uv.lock의 PEP 751 export다(uv export --format pylock.toml --frozen --no-emit-project, kit K36).config/dependency-policy.json이pythonLocks로 이름 붙이고,make dependency-review RECORD=1은 그 package와 두 git package(polyspec-orm과 polyspec-ordered-json,v0.0.7의 commit)를 review에 적는다.make python-lock-check는 커밋된 본문이 새 export와 다르면 실패한다(규칙이 없을 때 RED). package venv는 이 파일로 채운다(S-41).S-41: python은 uv가 아니라
make install-tools(config/toolchain.json의pythonBuild3.14.8, 플랫폼별 sha256)에서 온다.install-python과tests/install-python은tests/python-tools(make python-tools-test)로 바뀌었다. package venv는 그 interpreter의 복사본이다(python3 -m venv --copies --without-pip packages/fsm-python/.venv).packages/fsm-python/pylock.toml(uv.lock에서 uv가 export)을uv pip install --link-mode copy로 채운다.uv venv와uv sync는 kit runner가 거부하는 link를 쓴다. python 테스트는 PATH 항목이 아니라run-tests.mjs python --venv로 실행하며, main의make test-python("No module named 'polyspec'")을 고친다. RED: python-tools-test가 uv로 만든 checkout venv에서 실패한다. GREEN: test-python 290 case, python-persist-test-postgres와 python-persist-test-mysql(실행 중인 서버), parity-python, toolchain-check, make-install-test, workflows-test가 통과한다.S-35-2: vendored kit은 v0.0.23이다(
make kit-sync KIT_TAG=v0.0.23, kit K36과 K37).config/dependency-policy.json에 필수 필드pythonLocks를 더했다(S-34-2가 lock을 이름 붙이기 전까지[]): kit gate와 review가 그것이 나열한 PEP 751pylock.toml파일을 읽는다. review recordconfig/dependency-review.json은make dependency-review RECORD=1로 필수vcs목록([])을 받는다. fsm 타깃parity-check는vector-parity-check로 이름을 바꿨다(kit v0.0.23이parity-check를 정의하며, 저장소 타깃이 그것을 make warning과 함께 대체했었다). tests/make-install의 테스트는make -n check의 make warning마다 실패한다. kit의parity-check는 아직 채택하지 않는다: S-42가 평가한다(계획 C-4).run-tests.mjs python --venv는pyvenv.cfg가 install-tools Python을 가리킬 때만 venv interpreter를 실행한다. 알려진 상태: python package 테스트(make test-python, python persist 타깃)는 S-41까지 깨진 채 남는다. kit 0.0.18부터 python 도구는var/tools/bin/python3로만 실행되며 PATH를 무시하는데, fsm-python 테스트는 venv를 PATH에 올리기 때문이다("No module named 'polyspec'", CI 실행 38115497717). RED: lock을 v0.0.20으로 둔 채 v0.0.23 파일로 kit-check를 돌리자 29개 finding. GREEN: kit-check, owner-validate, documents-check, toolchain-check, dependency-policy-check, hooks-check, name-check, commits-check와 kit-test가 통과한다.S-35-1: vendored kit은 v0.0.20이다(
make kit-sync KIT_TAG=v0.0.20).kit-test가 vendored 테스트를scripts/kit/run-tests.mjs로 실행하므로(kit K32, 0.0.19로 릴리스) 다른 모든 테스트처럼 케이스별 진행과 timeout을 보고한다. 0.0.18과 0.0.19의 vendored python-real 테스트가 소비자의 toolchain을 읽어 여기서 실패했고 kit K34-1이 이를 vendored 테스트 밖으로 옮겼으므로 0.0.20을 받는다.S-29-2:
packages/fsm-php와packages/fsm-php-ext의 composer vendor는composer.lock뿐 아니라composer.json도 키로 하고, 설치 뒤 stamp를 갱신한다.composer.json의 autoload는 lock에 없어서, 기존 checkout이 S-25 이전의 autoload를 유지했고 persist case가 정의되지 않은 함수로 실패했다.S-37-2: fsm-db CLI와 command 테스트를 PostgreSQL과 MySQL에서도 실행한다(감사 23, 24). case마다 빈 database를 쓴다. SQLite에서는 새 파일이고, 서버에서는 admin DSN
FSM_TEST_PG_ADMIN_DSN또는FSM_TEST_MYSQL_ADMIN_DSN을 통해createEmptyDatabase(packages/fsm-db/tests/dialect.mjs)가 만든 새 database이며, 파일이 다시 지운다.db-test-postgres와db-test-mysqltarget은cli.test.mjs와command.test.mjs도 실행하고 admin DSN을 요구한다. CLI 테스트는 테이블 없음 메시지의 인용된 table 이름을 확인한다. PostgreSQL 서버가 메시지를 한국어로 쓰기 때문이다. RED: PostgreSQL target의 테스트는 SQLite에서 돌아(조용한 통과), 그다음 서버 메시지relation "machine" does not exist(서버는 한국어로 썼다)에서 실패했다. GREEN: 각 서버에서 36개 case가 통과한다. 결정과 CI service 줄은 계획 C-6에 있으며, fsm-db의 devDependenciespg와mysql2는 고정되고 검토되었다.S-37-1: go, rust, php, php-ext, python의 persist case도 PostgreSQL과 MySQL에서 실행한다(감사 23). 각 kind에
make <kind>-persist-test-postgres와make <kind>-persist-test-mysql이 있고TOOL_TESTS와CHECK_TARGETS에 든다. 테스트는FSM_TEST_DIALECT를 읽고, DSN을FSM_TEST_PG_DSN또는FSM_TEST_MYSQL_DSN에서 받으며, 연결의 driver가 그 dialect인지 확인하고, persist table을 비운 뒤 case를 차례로 실행한다. DSN이 비어 있는 서버 target은 exit 2로 멈춘다(require_dsn). db와 npm의 서버 target도 같은 방식이다. RED: 테스트가 dialect를 읽기 전에는 PostgreSQL target이 SQLite case로 통과했다(go는 예제가orm/pg를 import하기 전까지driver "postgres" is not registered로 실패했다). PostgreSQL 17과 MySQL 8.4에서 모든 kind가 GREEN이다. 동시성 증명은 npm에 남는다(계획 C-4).S-26-2: S-26-1이 찾은 README 사실을 실제 출력에 맞춘다. compiler README는
vectors --kind go에--go-module을 적는다. python README는 명령이 쓰는 package./app/order_fsm을 적는다(실제로 실행했다; 예제는app/을 module 경로에 넣고 import한다).src/app_fsm은 checkout에 없는 경로였다. go README는 persistence 발췌가 test bed의 model packageexamples/model을 import하며 소비자는 자기 것을 import한다고 밝히고, 발췌는 파일과 대조했다. RED: compiler 명령이--go-module없이 실패했고, python 설명이 명령이 쓰지 않는 경로를 적었다. GREEN: 문서의 명령을 모두 실행했고, 한국어 fenced block은 영어와 같다.S-26-1-1: go vectors 파일은 자기 module의 machine package(
<module>/src/<machine>)만 import하므로, 소비자의vectors --kind go파일은 소비자 module에서 vet된다. 생성되는decide는examples.Decide가 이름 붙이던 함수 타입을 반환하므로 파일이 test bed의examples(orm model이 필요한)를 더 이상 import하지 않는다. persistence 예제는 test bed의 것으로 남는다. RED: 소비자 module에서go vet이cannot find module providing package github.com/polyspec/fsm-go/examples로 실패; GREEN:packages/fsm-compiler/tests/go-backend.test.mjs의 소비자 테스트가 통과하고, golden vectors 파일과packages/fsm-go/tests/vectors_test.go를 다시 생성했으며,make test-go,make parity-go,make gen-check가 통과한다. plan C-2와 C-5가 결정을 기록한다.S-22: 계획의 GUI 수동 확인을 고정한 Playwright의 Chromium에서 json, mermaid, db store로 commit bc9f1b1에서 실행했고, 단계, 결과, 녹화를
docs/verification/gui.md에 기록했다: 다이어그램이 모든 state 이름을 보이고, 추가한 행이 제안 code 6을 가지며, 저장한 state가 표와 다이어그램에 나오고, 삭제한 state가 빠지고 다음 code가 7이며, 이름 바꾸기가 code를 유지하고, 각 store에서fsm-gen check또는fsm-db check가 통과한다. driver는packages/fsm-gui/scripts/manual-check.mjs다. 첫 실행들이 S-22-1, S-22-2, S-22-3, S-38을 찾았다.S-20-2: docs 설치가 npm 경고를 출력하지 않는다. npm 11이 esbuild와 fsevents의 설치 script에 경고했고, 이는
docs/package.json이 결정하지 않은 것이었다. esbuild script는 고정 버전에 허용하고(allowScripts), fsevents script는 거부한다. 사이트는 watcher를 쓰지 않으며 이 package는 빌드된 binary를 담기 때문이다. 추적과 근거는 계획 C-8에 있다.tests/docs-build는 lock으로 새npm ci를 실행하고 npm 경고가 있으면 실패한다.S-29-1:
gui-browsers는 고정된 Playwright 버전마다 Chromium을 한 번 내려받는다. 브라우저는packages/fsm-gui/package-lock.json의 버전으로 키를 정한 filevar/deps/playwright-<version>이며,gui-browser-test가 이 file에 의존하므로 stamp가 있는 이후 실행은 다운로드를 출력하지 않고 네트워크도 필요 없다(make -n gui-browser-test를tests/make-install이 검사한다).gui-browsers는 stamp의 phony 별칭으로 남는다.S-34: python 의존성을 잠갔다(감사 16).
packages/fsm-python/uv.lock(uv lock)이 트리의 모든 package를 정확한 버전, 각 artifact의 sha256, 각 git package의 commit과 함께 담는다(orm과 ordered-json은v0.0.7).make -C packages/fsm-python install은 이 lock으로uv sync --locked를 실행하므로 uv가 각 artifact를 hash와 대조한다.make install의 python stamp는pyproject.toml대신uv.lock으로 키를 정한다. kit dependency gate는 Python lock을 읽지 않으므로scripts/check-python-lock.mjs(make python-lock-check)가 lock의 pin, commit, tag, current, closure 규칙을 검사한다(계획 C-1). 전이 package의 최신 릴리스와 advisory 리뷰는 S-34-1로 등록했다.S-37: 영속화와 DB store를 SQLite, PostgreSQL, MySQL에서 증명한다(감사 23, 24).
packages/fsm-npm/tests/persist-dialects.test.mjs는 한 row의 전이 두 개를 두 연결에서 동시에 실행한다. 같은 (expected, event)는 Changed 하나와 Unchanged 하나를, 한 expected 상태의 서로 다른 event 두 개는 Changed 하나와 E_CONFLICT 하나를 낸다. SQLite에서는 두 전이를 차례로 실행한다. SQLite driver가 동기식이기 때문이다.forUpdate()가 없으면 PostgreSQL과 MySQL에서 두 전이가 모두 Changed를 보고한다(RED).make db-test-postgres와make db-test-mysql은packages/fsm-db/tests/dialect.mjs를 통해 두 서버에서 DB store 테스트와 store 계약을 실행한다.FSM_TEST_PG_DSN이나FSM_TEST_MYSQL_DSN이 없으면 target이 실패하며 case를 건너뛰지 않는다(RED: 변수 없이 실행하여 실패를 확인). 문서는 테스트한 dialect를 적는다(명세 B-2, B-4; fsm-db README; 루트 README). S-21을 위한 CI service 줄은 계획 C-6에 있다.S-31-1: layout 계약이 rust retry test를 허용한다(S-31 감사).
contracts/layout.json은tests/persist_retry.rs를 차이로 선언했지만 check-layout은 그 파일이 rust의 허용 glob에도 있을 것을 요구한다. S-31 commit은 layout-check를 실행하지 않아 이를 놓쳤다.make layout-check와make layout-test가 통과한다.S-31: rust 영속화 예제가 deadlock을 재시도한다(명세 B-4).
examples/persist.rs는 콜백을 한 번만 실행하는transaction_once를 썼지만, B-4는 deadlock이면 orm이 콜백을 다시 실행한다고 적는다. 이제 orm v0.0.7의transaction을 쓴다(기본 세 번 재시도). 콜백은Fn이다. machine 오류는transaction콜백이 orm 오류로만 실패하므로 값으로 돌려준다.persist_runs_again_on_a_deadlock(RED: 한 번 시도, 기대는 두 번)과persist_does_not_run_again_on_another_error가packages/fsm-rust/tests/persist_retry.rs(make rust-persist-test)에 있다. 명세 B-4는 조건부 update 하나 대신 transaction +forUpdate()로 잠그는 이유를 적는다. orm v0.0.7에는 영향 행 수를 돌려주는 공개 update가 없고, 이를 다시 볼 조건을 함께 적는다.S-39: go test 파일과 겹치는 machine key를 예약한다(스펙 B-5).
examples,vectors,machines,expectation,lookup은fsm-gen check를 통과하면서도packages/fsm-go의go test ./...를 깨뜨리므로(import 이름 중복, 또는 import로 이미 선언된 package 이름) 이제E_KEY_RESERVED로 거부한다.tests는 측정에서 빌드되므로 예약하지 않는다. S-39 행은 이 이름을 들었으나, 측정이 그것을 바로잡는다.S-33:
fromCode은 모든 호출자에게서 정수가 아닌 값을 거부한다(감사 13, 14, 22). strict_types를 선언하지 않은 호출자에서 php와 php-ext는"2"와2.0을 2의 상태로,true를 1의 상태로 변환했다. 이제 python과 npm처럼E_UNKNOWN_CODE를 던진다(스펙 B-6). GUI 편집 페이지는 저장 전에 거부된 다음 code(E_CODE_RANGE)를 보여 주며, core와 폼은 null을 돌려주지 않는다. 스펙 B-6은 생성된 코드가 가진 네 오류 상수를 적는다.S-40: fsm-gui의 command 테스트는 machine 하나로 프로젝트를 만든다.
packages/fsm-gui/tests/command.test.mjs는 rootfsm.lock.json전체를 order machine의 프로젝트에 복사했으므로, root lock에 machine을 더하면 테스트 프로젝트가 바뀌었다. 이제 테스트는 root lock의order항목만으로 프로젝트 lock을 쓰고({ order: rootLock.order }), 프로젝트 lock이order만 가짐을 단언한다. RED: 프로젝트 lock이 root의 다섯 machine(in_review,limits,order,standby,ticket)을 담았다. GREEN:make gui-test가 통과하며, command 테스트는 233개 case 중 하나로 통과한다.S-26-1: 언어 README가 S-25와 S-26을 따른다(계획 C-9). go README는
--go-module을 붙인fsm-gen vectors --kind go와 실제 출력을 적고,tests/vectors_test.go의 key 표와 adapter를 설명하며,examples/persist.go의Decide를 파일에서 다시 옮긴다(orderMachine.decideadapter와 함께). php README는composer.json의 실제autoloadblock을 옮기고(autoload.files의 loadersrc/functions.php는 모든 machine을 require한다), 파일 표가 loader를 적는다. 모든 언어의 Errors 절이 그 언어의 field 이름으로 spec B-6의 field 규칙(어떤 code가 expected, actual, event를 채우는지)을 적는다. RED: go 명령은--go-module없이 실패했고, composer 예시는 Order 함수만 읽었으며, 어떤 Errors 절도 B-6을 적지 않았다. GREEN: 모든 발췌가 파일과 글자 그대로 같고, 한국어 code block이 영어와 같으며, 명령과 snippet을 임시 소비자 프로젝트에서 실행했다. go vectors 파일은 소비자가 아니라 test bed의 examples를 import하며, 이는 checklist 항목 S-26-1-1이 기록한다.S-29: 새 clone에서
make check를 실행할 수 있다(감사 8).make install은install-tools의 toolchain과 모든 패키지 의존성을 한 번 설치한다:fsm-npm,fsm-db,fsm-gui,docs의 npm lock,fsm-php와fsm-php-ext의 composer lock,fsm-python의 Python 3.14 venv, go module과 cargo crate(var/deps/의 stamp). 각각은 자기 lock을 키로 하는 file target이고, 패키지를 쓰는 타깃(gen-check,npm-types,test-<kind>,db-test,gui-test,docs-build)은 그것을 선행 조건으로 둔다. 전에는 패키지가 설치되지 않은 checkout에서gen-check,npm-types,packages-test,parity-check가 실패했다.make make-install-test(tests/make-install/)는 Makefile 사본에서 선행 조건을 검사한다.S-26: 모든 vector가 결과 전체를 단언한다(감사 4, 15). 오류 case는
fsm.Error의 네 필드(code,expected,actual,event)를 명명하고, 해당하지 않는 필드는 null이다(명세 B-6, 계획 C-4). 모든 언어 harness가 네 필드를 모두 단언한다.from_code에는 음수 code, 2^31, 64비트 최댓값 입력 셋이 더해졌고, 파일이 정확하도록 십진 문자열로 쓴다. 잘못된 state 값(0과 미지 code)은code,key,is_final,apply에 go, npm, python으로 준다. 이 셋은 state 타입이 임의 정수를 담는 kind다. rust, php, php-ext에는 그런 값이 없으므로 그 native 모듈은 그런 case를 담지 않는다. case의kinds목록은 그 case를 돌리는 kind를 적고,check-parity가 이를 검사한다. 계약contracts/features.json은 바뀌지 않았다.S-25: 적합성 machine이 정의를 덮는다(감사 3).
machines/에standby(event 없음),in_review(모든 수준의 underscore key),ticket(reserved code 2와 4: 첫 lock 뒤 상태를 제거했고, 두 번째 lock이 code를 reserved로 유지한다),limits(code 2147483647)를 lock 항목과conformance/vectors와 함께 더했다. go harness는 일반형이다.tests/vectors_test.go는 key 표와 machine마다 adapter 하나를 담고 go 백엔드가 생성한다(vectors --kind go는--go-module을 받는다).tests/conformance_test.go는 각 case를 그 machine의 adapter로 실행하고,examples/persist.go는 machine을Decide로 받는다. php 패키지는 적재기src/functions.php로 모든 machine의 함수를 싣는다(명세 B-6, 계획 C-2). order 골든 파일과 비교하는 compiler 테스트는 order machine만 있는 프로젝트를 읽는다(tests/order-project.mjs). 각 kind는 다섯 machine의 209 케이스를 통과한다. S-23의 event 없는 패키지 machine은standby다.S-24: php-ext 백엔드가 모든 C 이름을
--php-namespace와--php-ext-name에서 유도한다. 머신 파일이php_polyspec_fsm_test.h와polyspec_fsm_test_arginfo.h를 include했고, Error 클래스 심볼은 옵션과 무관하게Polyspec_FsmTest_Fsm_Error였다. 이제php_<ext>.h,<ext>_arginfo.h,<namespace의 _ 버전>_Fsm_Error다(명세 B-6). 백엔드 테스트는 두 번째 쌍Acme\Tool과acme_tool로 출력을 phpize, ./configure, make로 빌드하고 php에 올린다.S-23: rust 백엔드가 이벤트가 없는 머신을 컴파일한다. variant가 없는 enum은 표현을 가질 수 없으므로(rustc E0084), 그런 머신의
Event는#[repr(i32)]없는pub enum Event {}다(명세 B-6). 백엔드 테스트가 그 머신의 생성 파일을 cargo로 빌드한다.S-22-3: 브라우저 테스트는
networkidle이 아니라 연결된 폼을 기다린다. client는connectForm과 구독이 자리 잡으면 form host#fsm-form에data-fsm-connected="true"를 설정하고, 연결이 끊기면 제거한다. layout은 표시가 설정되기 전까지 행 버튼이 클릭을 받지 못하게 한다. 첫 실행 실패는 묶기 전 클릭이 아니라 새로 받은 Chromium의 첫 탐색으로, 5초 케이스 제한에 대해 2.5~12.4초였다(새 다운로드 네 번 측정, plan C-7). setup이 이제 자기 제한으로 편집 페이지를 한 번 연다.S-30-1: README가 S-30의 lock 기준선을 밝힌다(명세 B-3). 루트 README는 규칙을
fsm-gen lock의 실제 출력(맞바꾸고 지운 lock의E_LOCK_REGRESSED), json과 mermaid의 git HEAD 기준선, git 밖의 한계, 커밋이 없어E_LOCK_REGRESSED가 생기지 않는 db store와 함께 준다. fsm-db README의 table과 lock 절, fsm-gui의 저장·이름 바꾸기·삭제 규칙이 같은 내용을 말한다.S-38: Mermaid 다이어그램이 모든 상태를 이름으로 보인다(명세 B-1, B-5). 고정한 mermaid 12.1.0에서
draft : 1은 code만 그려서 GUI와 모든 Mermaid viewer가 1..n만 보이고 key가 없었다. 이제 각 상태는 label 줄state "draft" as draft와draft : 1이다. 다이어그램은 key를 상자의 제목으로, code를 description으로 보인다. reader는 key와 다른 label을E_MERMAID_UNSUPPORTED로 거부한다. 다른 형식 네 가지(description 반복, label 안의 code, description 하나에 key와 code, 이전 형식)를 검토해 B-1에서 기각했다. B-5의 예약 단어는 label 줄을 넣고 다시 쟀고 목록은 바뀌지 않는다.packages/fsm-gui/tests/browser/diagram.test.mjs는 모든 상태 key와 code가 그려진 label인지 확인한다(이전에는 실패했다). mermaid store, 정규 텍스트, GUI 고정 데이터, README와 명세의 모든 Mermaid 예제를 따른다.S-20-1: 문서 사이트 빌드가 경고 없이 끝난다.
vuealias가 CommonJS 감싸기인vue/index.mjs를 가리켜 빌드가Circular chunk: framework -> theme -> framework를 출력했다. alias는 이제 vue의import진입점dist/vue.runtime.esm-bundler.js를 가리킨다. vite override와 vitepress는 근거와 함께 배제했고, 계획 C-8에 기록했다.docs-build-test는 빌드 출력의 경고 줄이 하나라도 있으면 실패한다.S-35: 계획과 계약이 저장소와 맞는다(감사 17, 18, 19, 21). 계획 D 표는 상태를 담지 않고 체크리스트를 가리킨다. C-1 트리에
src/store-lock, 이름 검사기, docs-build와 install-python 테스트, 문서 사이트 파일을 넣었다.contracts/layout.json은 npm의tests/types.test.mjs와tests/types/**를 이유와 함께differences에 선언하고, check-layout은 어떤 선언도 없는 kind의 테스트 파일을 거부한다(tests/check-layout/의 RED 테스트). S-11-3에는 반복 가능한 테스트make install-python-test가 생겼다. 임시 checkout에서 stub uv로 타깃을 돌리고,--no-bin이 없는 레시피는 링크를 쓴다는 것을 보인다.kit-test를CHECK_TARGETS에 넣었다.S-32: 컴파일러의 import를 명시하고 테스트한다(계획 C-5).
packages/fsm-compiler/src는node:내장 모듈, 자기 파일, 이 clone의 벤더 kit(scripts/kit/)만 import하고 npm 패키지는 import하지 않는다. 계획은 의존성 0이라 했지만 kit import와 모순이었다.tests/imports.test.mjs가src의 모든 import specifier(정적, 동적, 상대 경로)를 검사하고 그 집합 밖이면 실패한다.src/core는node:모듈을 import하지 않는다(감사 5).S-27:
name-check는 줄이 아니라 옛 이름의 출현마다 판정한다. 허용 구절은 줄에서 지운 뒤 남은 부분을 다시 검사한다. CHANGELOG 항목은 rename까지(S-01부터 S-18까지)의 bullet만 허용하고, model 항목은 orm-gen이 상태 열을 위해 쓰는 helper와 열 이름만 허용한다. 줄 전체를 허용하던 항목은 정확한 구절만 허용하며, 감사 probe 줄은 finding이다(tests/check-name/의 RED 테스트). rename 잔재를 고쳤다: Makefile 주석, 컴파일러와 gen-check 테스트 helper 주석, python과 rust 예제의 상태 열용status_of(감사 6, 12).S-19: 사용자 문서(R11, 계획 C-9). 루트 README와 모든 패키지 README(영어와 한국어)가 사용자에게 라이브러리를 설명한다: fsm이 무엇이고 무엇이 아닌지, JSON, Mermaid, database의 order 예제로 보는 모델, code와 lock,
apply로 하는 전이와expected가 재시도를 멱등하게 만드는 이유, 모든 검증 code, 프로젝트 파일, 요구사항과 설정, 모든fsm-gen명령과 옵션의 실제 출력과 종료 코드, 생성 이름, 검사.packages/fsm-compiler/README.md는 새로 만들었다(구조, store 계약, 명령, backend 옵션, vector). database와 GUI README는 필수 절을 추가했다(GUI는 Tests 절을 유지). 각 언어 README는 생성 명령, 생성 파일, apply 예제가 있는 API, 오류,examples/persist.*의 영속화 패턴, 테스트를 설명한다.scripts/check-readmes.mjs(make readme-check, 테스트는tests/check-readmes/)는 각 README의 절과 같은 제목을 가진 한국어 짝을 요구한다. 문서를 쓰기 전에는 54개 검사 결과를 냈다.make docs-build는 새 페이지로 사이트를 빌드한다.S-30: 커밋된 code는 lock을 잃어도 의미가 바뀌지 않는다(R1, 명세 B-3·B-5). 모든 store의
load()는 커밋된 lock을baseline으로 돌려준다(json·mermaid는 git HEAD의fsm.lock.json, db는 저장된 행).check,gen,vectors,lock,save,copy는 커밋된 code를 잃거나, 커밋된 reserved code를 상태에 주거나, 커밋된 key를 다른 code로 옮기는 lock을E_LOCK_REGRESSED로 거부한다. 이름 변경은 커밋 전에도 받아들인다. 감사 사례(code 두 개를 바꾸고fsm.lock.json을 비운 뒤lock)는lock: ok를 출력했으나 이제 거부된다. git 밖이나 추적되지 않는 lock은 커밋된 것이 없으므로 저장된 lock만 판정한다. db store에서 SQL로 바뀐 행은 감지하지 못한다.lock은 커밋된 code를 유지할 때만 삭제된 lock을 복구한다.S-22-2: GUI 폼을 쓸 수 있다.
make gui-build가 crudui stylesheet@polyspec/crudui-generator-core/styles/crudui.css를public/assets/crudui.css로 복사해hyper-build-assets --static에 넘기고, layout은/assets/crudui.css를 링크하므로 행 버튼이 아이콘과 크기를 보인다. 상태 행의 새 field는New key로 label을 붙인다(저장된 key는Key그대로다). 한 행의 두 field가 label을 공유하지 않는다.public/favicon.ico(scripts/make-favicon.mjs가 쓰는 ICO)가/favicon.ico에 404 대신 200으로 응답하며, layout이 이를 링크한다. 브라우저 테스트가 세 가지를 모두 확인한다.S-22-1: GUI 폼이 행 변경 뒤 다시 렌더링한다. 편집 폼(
packages/fsm-gui/client/main.ts)은 crudui instance를 구독하고,patchContent(host, renderForm(instance, render))로 다시 렌더링한 뒤connection.sync()를 호출한다.connectForm의 계약이다. 수정 전에는 행의 add·remove 버튼 클릭이 instance만 바꾸고 페이지는 바꾸지 않았다.render는 페이지의 crudui render 옵션(action,_csrftoken, 오류)이며 페이지 state에 실리고, 폼은#fsm-formhost 요소 안에 있다.packages/fsm-gui/tests/browser/browser.test.mjs(make gui-browser-test)는 고정된 Playwright 1.64.0(정확한 버전 devDependency,make gui-browsers가 브라우저를 내려받음)의 headless Chromium으로 폼을 조작하며 add, save, remove, 제안 code를 확인한다.make gui-test는 최상위 테스트만 실행한다. 수정 전 첫 브라우저 단정은5 !== 6으로 실패했다.S-36: fsm-db CLI를 README가 문서화한 모든 명령에 대해 process로 테스트한다.
packages/fsm-db/tests/cli.test.mjs는 출력, 종료 코드, 이후의 행을 단언한다.lock --rename order.draft=created는[fsm-db] lock: ok를 출력하고, 이름이 바뀐 state의 code를 유지하며, 새 key를 행에 쓴다. 알 수 없는 rename은 README의E_UNKNOWN_STATE_REF줄을 출력하고 행을 쓰지 않는다.copy --to mermaid --root <dir>는 정규형.fsm.mmd파일과 lock을 쓴다.copy --from mermaid --root <dir>는 빈 database를 채우고, 두 번째 복사는 README의E_STORE_NOT_EMPTY줄을 출력하며 아무것도 쓰지 않는다. RED: process 케이스가 없었고, 기대 출력을 바꾸면 rename 케이스가 실패한다. README는 명령과 일치했으므로 틀린 README 문장은 없었다. 대신copy --to mermaid예시가 추가되었다(en, ko).S-28: spec B-2의 store 계약을 세 store에서 증명한다.
packages/fsm-db/tests/store-contract-all.test.mjs(컴파일러에 의존하는 fsm-db 패키지가 소유하며make db-test에서 실행)는packages/fsm-compiler/tests/fixtures/valid의 모든 valid fixture를 각각, 그리고 전부 함께 json → db → mermaid → json으로 왕복시키고 파일을 바이트로 비교한다. 또한 필수 옵션을 준 모든 kind의rungen과vectors(모든 kind의 vectors 파일 포함)가 json, mermaid, db store에서 바이트 동일한 출력을 내는지 확인한다. RED: save에서final을 버리는 db store는 네 개의 동등성 케이스에서 실패한다. core가E_DEAD_END로 왕복을 거부하거나 바이트가 달라지며, 탐지 케이스도 같은 것을 증명한다. 실제 store는 통과하므로 store 결함은 발견되지 않았다. spec B-2(en, ko)가 파일을 명시한다.S-20: 문서 사이트(R11, 계획 C-8).
docs/.vitepress/config.mts는 저장소 문서를 제자리에서 VitePress 1.6.4로 빌드한다(make docs-build, 패키지는docs-deps가docs/package-lock.json으로 설치): 루트와 패키지 README(영어와 한국어), 명세, 계획, 검증 페이지, changelog. README.md는 자기 디렉터리의 index 페이지이며, 사이트 페이지가 아닌 저장소 파일로 가는 링크는 공개 저장소를 가리키고, dead link는 빌드를 실패시킨다.make docs-build-test(tests/docs-build)는 dead link의 실패와 실제 빌드를 확인한다.docs/package.json은 vite를 6.4.4로 override한다. vitepress 1.6.4가 moderate 및 high advisory가 있는 5.4.x 줄을 고정하기 때문이다. 계획은 자리표시자<major>를 코드 스팬에 쓴다. 사이트가 이를 Vue 마크업으로 컴파일하기 때문이다.config/documents.json은siteLinks를 설정한다.S-18-1: 명령
fsm-gen,fsm-db,fsm-gui가 존재한다. 각각packages/fsm-compiler/package.json(새 파일, 의존성 없음),packages/fsm-db/package.json,packages/fsm-gui/package.json의bin항목이며, 각 파일은 node shebang으로 시작하고 실행 권한을 가지며, clone에서npm install -g ./packages/<package>가 명령을 PATH에 link한다. S-18은 문서에서만 이름을 바꿨었다. 각 package의 테스트가 임시 npm prefix에 offline으로 설치하고 명령을 실행한다. GUI usage 줄은fsm-gui를 쓴다.S-15-1:
fsm-db install [--project <dir>]은fsm.config.json이 가리키는 database에schema/fsm.dbs의 schema를 ormutils().schema().install로 설치하므로, database는 코드 없이 table을 얻는다. 새 database에서fsm-db check는no such table: machine로 실패하고(종료 코드 1),install은[fsm-db] install: ok를 출력하며(종료 코드 0), 이후check가 통과한다. 두 번째install은 아무것도 바꾸지 않고 종료 코드 0으로 끝난다. table이 일부만 있는 database에서는 orm이 install을CONFIG로 거부하며, 오류는 stack과 함께 출력되고(종료 코드 1) 대체 동작은 없다(orm v0.0.7에서 관찰).packages/fsm-db/README.md와README.ko.md가 DB store의 사용자 안내서다. 명세(B-2)와 계획(C-6)은 명령을 나열하며, 명세의copy줄은 CLI가 가진 옵션(--root와 함께 쓰는--to또는--from, DSN은dsnEnv가 가리키는 변수에서만 온다)에 맞게 고쳤다. install은 SQLite에서 프로세스로 실행하는 테스트가packages/fsm-db/tests/cli.test.mjs에 있다.S-18: 라이브러리 이름을
status에서fsm으로 전부 바꾼다(R12). 패키지는packages/fsm-*, 명령은fsm-gen,fsm-db,fsm-gui, 파일은fsm.config.json,fsm.lock.json,machines/*.fsm.json,*.fsm.mmd,schema/fsm.schema.json과schema/fsm.dbs(dbspecfsm), 문서는docs/spec/fsm.md와docs/plans/fsm-v1.md, 생성 공용 모듈은fsm(fsm.Error,<ns>\Fsm\Error, 백엔드마다 모듈fsm), 예약 머신 key는fsm이다.status는 다시 일반 key다. 매니페스트 이름은polyspec_fsm,polyspec-fsm,@polyspec/fsm-*,github.com/polyspec/fsm-go, 네임스페이스Polyspec\FsmTest, extensionpolyspec_fsm_test다. 소비자의 status 열과 프로세스·테스트의 status는status를 유지한다.scripts/check-name.mjs(make name-check)는 허용 목록 밖에서 라이브러리를status로 부르는 추적 파일이나 경로가 있으면 실패하며, golden과 생성 파일은fsm-gen으로 다시 생성했다. AGENTS.md와 계획은name-check를 규칙을 강제하는 도구로 적고, 범위는 R1–R12이며, 문서는 옛 이름을 "옛 이름status"로만 언급한다.S-11-2: python 패키지가 color 우회 설정을 제거한다. kit v0.0.15(K22)가 run-tests python에서
NO_COLOR=1과PYTHON_COLORS=0을 직접 설정하므로,packages/status-python/Makefile의make test는 더 이상PYTHON_COLORS=0을 설정하지 않는다. kit 설정이 없으면FORCE_COLOR=3에서 unittest가 verbose 줄에 color를 넣고 reporter가 이를 읽지 못한다. 줄을 제거한 상태에서 v0.0.14 runner는 10번 중 10번 실패했고, v0.0.15 runner는 10번 중 10번 통과했다.make test-python은FORCE_COLOR=3에서도, 없어도 89개 case를 모두 통과한다.S-16-1: GUI test hang을 원인에서 고쳤다. kit v0.0.15 sync(K23)는 test child의 stdout 파이프를 blocking으로 만든다(
scripts/kit/test-load-check.mjs). 비동기 파이프에서는 test child의process.exit이 파이프에 남은 결과를 버렸다. 그래서 파일이 실행한 test보다 적은 결과를 보고할 수 있었고, runner는 "the test did not finish"를 출력할 수 있었다. case 600개짜리 파일에서 v0.0.14는 30번 중 12번, v0.0.15는 30번 중 0번 결과를 잃었고, blocking 줄만 되돌린 v0.0.15는 30번 중 10번 잃었다. 이 sync는 K21(--cwd아래 node 파일 group)과 K22(run-tests python이NO_COLOR=1과PYTHON_COLORS=0을 설정)도 가져온다. test 변경 전에는make gui-test가 4개씩 병렬로 20번 중 20번 통과했고(각 210개 결과),make compiler-test도 20번 중 20번 통과했다(각 302개 결과). 이 checkout에서는 hang이 재현되지 않았다.packages/status-gui/tests/mermaid-parse.test.mjs의 호출별 20초 deadline은 hang을 가렸을 뿐이어서 제거했다. runner가 이미 case마다 자체 timeout을 준다. flat test(예약 단어와 위치마다 하나, 일반 key마다 하나)는 각 case가 자기 이름과 결과를 가지므로 유지했다. 변경 후make gui-test는 20번 중 20번 통과했다.S-11-3:
make install-python은uv python install --no-bin 3.14를 실행하므로, uv가 사용자 bin 디렉터리(기본~/.local/bin)에python3.14실행 파일을 쓰지 않는다. 인터프리터는 여전히 uv 관리 설치이며,var/tools/bin/python3만 그것을 가리킨다. 변경 전에는UV_PYTHON_BIN_DIR를 임시 디렉터리로 지정하면 target이 그곳에python3.14를 썼고, 변경 후에는 그 디렉터리가 비어 있고~/.local/bin은 바뀌지 않는다.S-14-3:
gen과vectors --kind는 출력 디렉터리의 git-ignored 파일을 건너뛴다(packages/status-compiler/src/cli/write-generated.mjs). 무시된 파일은 손으로 쓴 파일이 아니라 빌드 산출물이므로E_NOT_GENERATED로 생성기를 막지도 않고 stale 파일로 지우지도 않는다. 그래서packages/status-php-ext에서는make test뒤에make clean없이make gen을 실행한다. 추적 파일이나 git이 무시하지 않는 파일은 여전히E_NOT_GENERATED이며, git work tree 밖에서는 무시되는 파일이 없다. 무시된 파일은 출력 디렉터리의git ls-files --others --ignored --exclude-standard로 구한다.make clean은 phpize 출력을 지우는 목표로 남는다. 테스트는packages/status-compiler/tests/write-generated.test.mjs에 있다.S-14-1: php-ext 패키지
packages/status-php-ext는 정규 구조(docs/plans/status-v1.md, C-2)의 kindphp-ext다.make gen은src/와tests/Vectors.php를 status-gen(--php-namespace 'Polyspec\StatusTest' --php-ext-name polyspec_status_test)으로 쓰고, php 패키지의tests/ConformanceTest.php와examples/persist.php를 복사하며(gen-check가 같은 byte임을 확인한다),examples/model/을 루트schema/persist.dbs에서 orm-gen으로 쓴다.make test는src/에서 phpize,./configure,make로 extension을 빌드하고--php-extension으로 vector case 89개를 실행하며,make parity-php-ext가 이를 확인한다.composer.lock은 php 패키지와 같은 30개 패키지(polyspec/orm 0.0.7과 PHPUnit 12.5.38)를 고정하며,config/dependency-policy.json과config/dependency-review.json이 php-ext manifest와 lock을 기록한다.contracts/layout.json은 생성 glob과 함께 kind를 선언한다. toolchain은config/toolchain.json의 php와 composer 선언이며, 다시 선언하지 않았다.S-14-2: php-ext backend의 C 출력을 첫 build 뒤에 고쳤다. 객체 인자는
zval *이다.Z_PARAM_OBJECT_OF_CLASS가zval *을 쓰기 때문이다. Error method 표는 생성자 뒤에 둔다.ZEND_LONG_FMT의 format에는%를 더 붙이지 않는다.Stepproperty의 class 타입은 영구 class 이름 문자열의ZEND_TYPE_INIT_CLASS이다. literal 이름은 property에서 해석되지 않기 때문이다.Codeenum 값은 영구 interned 문자열이어서 shutdown에서 해제되지 않는다..gitignore는packages/status-php-ext/src/의 phpize 출력을 정확히 고정된 anchored 패턴으로 적는다.config.m4와 생성된 소스는 추적된 채로 남는다. golden 파일을 다시 썼다.S-14: php-ext backend(
packages/status-compiler/src/backends/php-ext.mjs)가 machine마다 php-ext 패키지의src/파일을 생성한다.<machine>.c는zend_register_internal_enum으로 enumState와Event를,ZEND_NS_NAMED_FE로 B-6의 namespace 함수와 final classStep을 등록하며,apply와target은status_throw로 B-4 code를 던진다.status.c는 enumStatus\Code, exceptionStatus\Error와 module entry를 담고, module entry는 정의 순서대로 모든 machine을 초기화한다.php_<ext>.h,<ext>_arginfo.h(arginfo는 gen_stub 없이 직접 쓴다),<ext>.stub.php(편집기용 선언)와config.m4가 나머지 집합이다.status-gen gen --kind php-ext는--php-namespace와--php-ext-name을 요구하고,vectors --kind php-ext는 php kind의Vectors.php와 byte 단위로 같은 test module을 쓴다.packages/status-compiler/tests/golden/php-ext/의 golden 파일은 order 예시이며, 쓰기 전에 B-6과 대조해 검토했다.write-generated.mjs는config.m4의dnlheader를 받는다.S-17-1: 편집 페이지가 기계의 다음 code를 담는다. 내장 폼 상태에 core
nextCode(machine, definitions, lock)를nextCode로 담고(src/forms.mjs의nextCodeOf와editState),client/main.ts는 브라우저에서 추가한 상태 행마다 다음 code와 그 뒤의 code를 준다(client/new-codes.ts의codesOfAddedRows). 저장은 여전히 모든 code를 검사하며, 빈 code는nextCode로 채운다.tests/edit-next-code.test.mjs는 json, mermaid, db 저장소에서 내장 code를, 삭제 뒤의 code를 포함해 검사하고,tests/delete-state.test.mjs는 편집 폼에서 상태를 지우면 그 code가 세 저장소 모두에서codes에서 빠지고reserved로 옮겨짐을 보인다.make gui-test는 210개 테스트를 실행한다.S-17: GUI로 기계를 편집한다(
packages/status-gui)./machines/{machine}/edit는crud/machine.yml의 crudui 폼이다. 상태와 event는 반복 행이며, 저장된 상태의 key와 code는 읽기 전용이다. 저장은form[...]필드를 파싱하고(src/form-data.mjs, 형식이 틀린 본문은 400), 새 상태에 corenextCode가 제안한 code를 주며, 전체 정의 세트로 corerun save를 부른다. 거부된 저장은 아무것도 쓰지 않고 모든 finding과 함께 422로 응답하며, 필드를 가리키는 finding은 그 필드에 보여준다./machines/{machine}/rename은 renames로run lock을 부른다(code는 유지)./new는 초기이면서 final인 상태 하나를 가진 기계를 만들고, 상세 페이지는 그 기계 없이 저장하여 삭제하므로 그 code들은 reserved 목록으로 옮겨간다. 저장, 이름 바꾸기, 삭제마다 그 페이지의_csrf토큰이 필요하다(없으면 403).client/main.ts는 htmx settle마다 편집 폼의 행을 cruduiconnectForm으로 묶는다.make gui-test는 json, mermaid, db store에서 194개 테스트를 실행한다.S-11-1: python 패키지
packages/status-python은 정규 구조(docs/plans/status-v1.md, C-2)의 kindpython이다.pyproject.toml은 orm client를 git tagv0.0.7로, build backend를setuptools==84.0.0으로 고정한다.make install은 Python 3.14 venv(.venv, gitignore 대상)를 만들고 이것들을 설치하며,make test는 그 venv를PATH의 맨 앞에 두고 오프라인으로 실행한다.make gen은 status-gen으로src/polyspec_status/와tests/vectors.py를, orm-gen으로examples/model/을 쓴다.tests/test_conformance.py는 vector case 89개를 각각test_<feature>__<id>로 실행하고,examples/persist.py는 polyspec orm으로 SQLite에서 B-4 transaction을 실행하며, persist case가 실행 후 저장된 값을 검사한다.config/toolchain.json은 python 3.14를 선언하고,config/dependency-policy.json은 pyproject를 나열하며,contracts/layout.json은 C-2가 python에 허용하듯 lock 파일 없이 kind를 선언한다.make test-python과make parity-python은 패키지를 실행한다.S-11: python backend(
packages/status-compiler/src/backends/python.mjs)가 machine마다 python 패키지의src/polyspec_status/파일을 생성한다.<machine>.py는 B-6에 따라 member가 대문자인IntEnum인State와Event,to와changed를 가진 동결된Stepdataclass, 그리고status.Error를 던지는 여덟 함수를 담는다. 공용status.py(E_code의StrEnum인Code와Error)와__init__.py도 만든다.status-gen gen --kind python --out <dir>는 python header# Code generated by status-gen from <machine>; DO NOT EDIT.를 가진 이 세트를writeAtomic으로 쓰고, 오래된 generated 파일을 지우며, header가 없는 파일은 거부한다(E_NOT_GENERATED).vectors --kind python --out <file>는 native 모듈tests/vectors.py를 Python literal로 쓴다. 작성기의 header 검사는//와 함께#주석 문법도 받아들인다.packages/status-compiler/tests/golden/python/의 golden 파일은 order 예시이며, 쓰기 전에 B-6과 대조해 검토했고, 테스트는 이를 byte 단위로 비교한다.S-09-2:
make npm-types(packages/status-npm/tests/types.test.mjs)는packages/status-npm의 고정 devDependency인typescript7.0.2로tests/types/consumer.ts를 strict 모드에서 출력 없이(--noEmit), 생성된src/*.d.ts에 대해 컴파일한다. 소비자 파일은 B-6의 모든 공개 이름을 쓰고, 컴파일되지 않아야 하는@ts-expect-error줄 세 개를 가진다. 그래서 너무 느슨한 선언은 지시문을 쓰이지 않은 채로 만들어 테스트를 실패시킨다. 이 테스트는 자기 기록인.results/npm-types.json에서 실행된다.make test-npm은 conformance 파일만 지정하므로 npm 기록에는 vector case만 남아parity-npm을 통과한다.contracts/layout.json은 두 테스트 경로를 허용한다.S-13-1: rust 패키지
packages/status-rust는 정규 구조(docs/plans/status-v1.md, C-2)의 kindrust다.make gen은src/와tests/vectors.rs를 status-gen으로 쓴다.build.rs는 루트schema/persist.dbs의 orm model을OUT_DIR에 생성하므로, 이 패키지에는examples/model/이 없다(contracts/layout.json에 선언된 rust의 차이).tests/conformance.rs는 check 함수를 담으며, vector case 89개가test_<feature>__<id>로 실행된다.examples/persist.rs는 orm(v0.0.7, tag로 고정)으로 SQLite에서 B-4 transaction을 실행하며, persist case가 실행 후 저장된 code를 검사한다.rust-toolchain.toml은 루트에서 Rust 1.98.0을 선언하고,config/toolchain.json은Cargo.lock의 의존 검토를 위해cargoAudit0.22.2를 선언한다.make test-rust와make parity-rust가 이 패키지를 실행하며,make packages-test는 다른 kind와 함께 실행한다.S-13: rust backend(
packages/status-compiler/src/backends/rust.mjs)가 machine마다 rust 패키지의src/파일을 생성한다. B-6 표기에 따라<machine>.rs(#[repr(i32)]의State와Event,Stepstruct, 여덟 함수,i64를 받는from_code,Result<Step, Error>를 돌려주는apply), 공용status.rs(Code와Error)와lib.rs를 만든다.status-gen vectors --kind rust --out <file>는tests/vectors.rs를 쓰며, case마다#[test](persist case는#[tokio::test]) 하나를 두어tests/conformance.rs의 check 함수를 호출한다.packages/status-compiler/tests/golden/rust/의 golden 파일은 order 예시이며, 쓰기 전에 B-6과 대조해 검토했고, 테스트는 이를 byte 단위로 비교한다.S-12-1: php 패키지
packages/status-php는 정규 구조(docs/plans/status-v1.md, C-2)의 kindphp다.make gen은src/와tests/Vectors.php를 status-gen(--php-namespace 'Polyspec\StatusTest')으로,examples/model/을 루트schema/persist.dbs에서 orm-gen으로 쓴다.tests/ConformanceTest.php는 89개 vector case 각각을 PHPUnit data settest_<feature>__<id>로 실행하며,check-parity는 id마다 case 이름 하나를 읽는다. 생성된 namespace의 공개 API만 쓴다. persist case는 polyspec orm v0.0.7(release zip,composer.json에 sha1을 고정, ordered-json v0.0.7과 함께)로 SQLite에서 B-4 transaction을 실행하고 실행마다 저장된 state를 확인한다.composer.json은 PHPUnit 12.5.38(검토 시점의 최신 stable)과config.platform.php8.4.0을 고정하고,config/toolchain.json은 PHP 8.5와 Composer 2.10.3을 선언하며,config/dependency-review.json은 검토 기록이다.make test-php와make parity-php가 패키지를 실행한다.S-12: php backend(
packages/status-compiler/src/backends/php.mjs)가 machine마다 php 패키지의src/파일을 생성한다.<Pascal>/State.php와Event.php는 B-6 code와 순번을 값으로 갖는 backed enum이고,Step.php와functions.php는 B-6의 함수를 namespace 함수로 담으며, 공용Status/Code.php(오류 code enum)와Status/Error.php(getCode()가 code 문자열인\Exception)를 만든다.status-gen gen --kind php --php-namespace <ns> --out <dir>는 namespace를 요구하므로(테스트 벤치는Polyspec\StatusTest), 없거나 형식이 틀리면 usage 오류다.vectors --kind php --php-namespace <ns> --out <file>는Tests/Vectors.php를 쓰며, 그cases()는 모든 case를test_<feature>__<id>로 키 붙인다. php 파일은 첫 줄이<?php이므로 header를 둘째 줄에서 읽고, header가 없는 파일은 여전히 거부한다(E_NOT_GENERATED).packages/status-compiler/tests/golden/php/의 golden 파일은 order 예시(89 case)이며, 쓰기 전에 B-6과 대조해 검토했고,php -l은 생성된 모든 파일을 통과시킨다.S-10-1: go 패키지
packages/status-go는 정규 구조(docs/plans/status-v1.md, C-2)의 kindgo이며, module은github.com/polyspec/status-go다.make gen은src/(order/order.go,status/status.go)와tests/vectors_test.go를 status-gen으로,examples/model/을 루트schema/persist.dbs에서 orm-gen(go.mod의tool지시문을 통한go tool orm-gen)으로 쓴다. model은 출력 디렉터리 안에서 생성되므로 gen-check는 module 사본에서 실행한다.tests/conformance_test.go는 모든 vector case를test_<feature>__<id>subtest로 생성된 함수에 대해 실행하고,examples/persist.go는 SQLite 위에서 polyspec orm v0.0.7로 B-4 transaction을 실행하며, persist case가 실행 후 저장된 값을 확인한다.contracts/layout.json은generatedglob으로 kind를 선언한다. 언어 때문에 생긴 차이는 둘이다.gen --kind go는--go-module을 받고(생성된 machine이 공용 패키지를 module path로 import한다), toolchain은packages/status-go/go.mod의toolchain줄(go1.27.2, go1.27.0이 보고하는 stdlib 권고 GO-2026-6607을 고친 버전)이며config/toolchain.json의go가 이를 읽는다.config/toolchain.json은 의존 검토가 실행하는 govulncheck 1.8.0도 고정한다.make test-go(GOPROXY=off),make parity-go,make packages-test가 패키지를 실행하며, lock은go.sum이고config/dependency-review.json에서 검토한다.S-10: go backend(
packages/status-compiler/src/backends/go.mjs)가 go 패키지의src/파일을 생성한다. B-6 표기에 따라 machine마다<machine>/<machine>.go를 쓴다:type State int32와 멤버StateDraft,type Event int32와EventNext(위치는 1부터), 구조체Step{To, Changed}, 그리고Initial,Code,Key,FromCode,IsFinal,EventsFrom,Target,Apply함수이며, 이들은*status.Error와 함께(value, error)를 돌려주고 인자를 npm과 같은 순서로 검사한다. 공용status/status.go에는Error타입과Code*상수가 있다.status-gen gen --kind go --go-module <module path> --out <dir>가 이 집합을 쓴다. machine이 공용 패키지를 그 module path로 import하므로 module path가 필수다.status-gen vectors --kind go --out <file>은 nativetests/vectors_test.go를 쓴다: oracle의 key를 그대로 둔 case마다 literal 하나이며, conformance test가 이를 상수에 대응시킨다.packages/status-compiler/tests/golden/go/의 golden 파일은 order 예시이며, 쓰기 전에 B-6에 맞춰 검토했고 테스트는 바이트 단위로 비교한다.S-16: GUI 골격(
packages/status-gui)은 hyper v0.0.11 애플리케이션이다./는crud/machines.yml의 crudui 목록으로 프로젝트의 기계를 보여주고,/machines/{machine}는 상태, edge가 있는 event, 정규형 Mermaid 텍스트를<pre class="mermaid">에 보여준다. client는 Mermaid 12.1.0으로 첫 로드와 htmx swap마다 이를 렌더링한다.node src/main.mjs --project <dir>은status.config.json이 지정한 store(json, mermaid 또는 db)를 고르고 그 store와 corerun만 호출한다. 프로젝트의 모든 finding은 오류로 보여주며, 없는 기계는 404로 응답한다.make gui-test는 asset을 빌드하고 133개 테스트를 실행한다. 여기에는 고정한 mermaid 패키지로 order 예제를 파싱하는 테스트와, B-5의 모든 Mermaid 예약 상태 key를 다섯 위치에서 파싱하는 테스트가 포함된다. 고정 버전은 hyper v0.0.11, crudui v0.0.5, template v0.0.5와 mermaid·htmx.org·yaml의 정확한 버전이며,config/dependency-review.json에 검토 기록이 있다. Mermaid 파싱 테스트의 기준은docs/spec/status.md의 B-5에 기록했다.S-09-1: npm 패키지
packages/status-npm은 정규 구조(docs/plans/status-v1.md, C-2)의 kindnpm이다.make gen은src/와tests/vectors.mjs를 status-gen으로,examples/model/을 루트schema/persist.dbs에서 orm-gen으로 쓴다(이 항목에서 만들며 모든 kind가 공유한다).tests/conformance.test.mjs는 모든 vector case를test_<feature>__<id>로 생성된 함수에 대해 실행하고,examples/persist.mjs는 orm으로 SQLite에서 B-4 transaction을 실행하며, persist case가 실행 후 저장값을 검사한다.contracts/layout.json은 kind를generatedglob과 함께 선언한다.check-layout은 이를 요구하고,gen-check는 그 glob이 맞는 파일을 정확히 비교한다.make test-npm,make parity-npm,make packages-test가 이 패키지를 실행하며, lock은 검토 기록과 함께config/dependency-policy.json에 있다.S-09: npm backend(
packages/status-compiler/src/backends/npm.mjs)가 machine마다 npm 패키지의src/파일을 생성한다. B-6 표기에 따라<machine>.js와<machine>.d.ts(동결된State와Event, 여덟 함수,apply는{to, changed}를 돌려주거나status.Error를 던진다), 공용status.js와status.d.ts(Error와Code상수),index.js와index.d.ts를 만든다.status-gen gen --kind npm --out <dir>는 이 세트를writeAtomic으로 쓰고, generated header가 있는 오래된 파일을 지우며, header가 없는 파일은 거부한다(E_NOT_GENERATED, 아무것도 쓰지 않음).status-gen vectors --kind npm --out <file>는 native 모듈tests/vectors.mjs를 쓴다.packages/status-compiler/tests/golden/npm/의 golden 파일은 order 예시이며, 쓰기 전에 B-6과 대조해 검토했고, 테스트는 이를 byte 단위로 비교한다.S-15: DB store(
packages/status-db/src/db-store.mjs)는 정의와 lock을 orm client(v0.0.7)를 통해schema/status.dbs의 여섯 테이블에 보관한다.dbStore(db)는 orm-gen이src/models/models.ts에 생성한 model로 읽고 쓰며,save는 모든 행을 한 transaction 안에서 바꾸므로 쓰기가 실패하면 기존 행이 그대로 남는다. 모든 key 열은varchar(64)이며(S-05-3의 상한), edge 테이블은state를 향한 외래 키의 인덱스 두 개를 가진다. store는 계약 테스트(tests/store-contract.mjs)를 통과하고, 64자 key를 포함해 json → db → json을 바이트 단위로 같게 왕복하며, 비어 있지 않은 데이터베이스로의 copy는E_STORE_NOT_EMPTY로 거부하고, 실패한 save를 되돌린다.status-db(src/cli/status-db.mjs)는check,lock,copy --to|--from json|mermaid --root를 제공하며, DSN은status.config.json이 이름 붙인 환경 변수의 값이다.make db-test가 테스트를 실행하며, 여기에는 orm-gen이 커밋된 model을 재생성하는지 확인하는 테스트가 포함된다.S-06-1: store 계약이 비동기가 되었다. JSON·Mermaid store의
load()와save()가 Promise를 돌려주고,run과copy(src/core/run.mjs)도 비동기이므로 DB store가 비동기 orm client를 쓸 수 있다.status-gen은 명령을 await하며, 예상하지 못한 거부는 출력하고 종료 코드 1로 끝난다. validate, normalize, lock, vectors는 동기로 남는다. store 계약 테스트(tests/store-contract.mjs)는 모든 store에서 Promise를 await하며, store나 명령을 부르는 모든 테스트가 await한다.S-08: Mermaid store(
src/store-mermaid/)는docs/spec/status.md의 B-1 정규 형식으로machines/*.status.mmd를 읽고 쓴다.parse.mjs는 정규 형식 밖의 모든 줄을 줄 번호와 함께E_MERMAID_UNSUPPORTED로 거부하고,write.mjs는 정규 형식으로 쓴다.save는 정의되지 않은*.status.mmd파일을 지우고 store 계약(tests/store-contract.mjs)을 통과한다. lock 읽기·쓰기는 두 파일 store가 함께 쓰도록src/store-lock/lock-file.mjs로 옮겼다.status-gen은{"store":"mermaid"}를 받고,copy --to json|mermaid는 빈 대상으로 두 방향 모두 옮긴다. 모든 valid fixture는 json → mermaid → json에서 바이트 단위로 왕복한다.S-05-3: core
validate는 machine·상태·이벤트 key의 길이를 64자로 제한한다. 64자를 넘는 key는 해당 경로에서E_KEY_FORMAT으로 실패하며, 메시지는 길이와 상한을 밝힌다. DB store는 key를varchar(64)에 담으므로, 이 상한으로 세 store가 같은 key를 받아들인다(docs/spec/status.md, B-5).S-07: 루트 conformance 파일은
status.config.json({"store":"json"}),machines/order.status.json(B-1의 order 예시, JSON store가 기록), 그status.lock.json과contracts/features.json이다. corevectors(src/core/vectors.mjs)는 IR과 lock에서 C-4의 모든 케이스를 계산하며,status-gen vectors --out <dir>는<machine>.json을 쓰므로conformance/vectors/order.json은 89 케이스를 가진다.scripts/gen-check.mjs(make gen-check)는 vector 파일을var/gen-check/에 다시 만들고conformance/vectors의 추적 파일이 그와 같아야 함을 요구하며, 테스트는make gen-check-test다.S-06: core
normalize는 유효한 정의의 IR을 돌려주고(src/core/normalize.mjs),lockMismatch,nextLock,nextCode,canonicalLock은 B-3의 code lock을 구현하며(src/core/lock.mjs),nextLock은 lock이 가진 code를 정의가 바꾸는 key를E_CODE_CHANGED로 거부한다.run(check,lock,save)과copy는 B-2의 명령을 store 위에서 실행한다(src/core/run.mjs). JSON store(src/store-json/store.mjs)는machines/*.status.json과status.lock.json을 정규 형식으로 읽고 쓰며 store 계약(tests/store-contract.mjs)을 통과한다.status-gen(src/cli/status-gen.mjs)은check,lock,copy를 제공하고, mermaid store는 S-08 전까지E_STORE_NOT_CONFIGURED로 남는다.S-05-2: core
validate는 B-5의 이름·code 오류E_KEY_FORMAT,E_DUPLICATE_KEY,E_KEY_RESERVED(고정 목록은packages/status-compiler/src/core/reserved.mjs),E_DUPLICATE_CODE,E_CODE_RANGE를 보고한다. machine key는_를 뺀 뒤 비교하며, 구조 오류가 있는 machine은 다른 machine과 비교하지 않는다.S-05-1: core
validate는 B-5의 그래프 오류E_UNKNOWN_STATE_REF,E_SELF_LOOP,E_NONDETERMINISTIC,E_DUPLICATE_EDGE,E_INITIAL_COUNT,E_FINAL_OUTGOING,E_UNREACHABLE,E_DEAD_END을 보고하며, 각 코드마다 자기 실패 케이스가 있다. 도달 가능성은 단일 initial 상태에서 시작하며, 구조 오류가 있는 machine은E_SCHEMAfinding만 보고한다.S-05: core
validate(packages/status-compiler/src/core/validate.mjs)는 정의의 구조 오류를 모두 JSON 경로와 함께E_SCHEMA로 보고하고, 그 machine의 뒤 규칙은 건너뛴다.schema/status.schema.json은 kitschema-validate.mjs의 키워드만 쓰는 에디터 schema이며, 대응 테스트가 모든 fixture에서 core와 일치함을 확인한다.make compiler-test는run-tests.mjs로 테스트를 실행하고.results/compiler-test.json을 기록한다.JSON store는 엣지를 쌍 대신 객체
{from, to}로 쓴다. 그래서 에디터 schema가 정확히 검사하고 GUI 폼은 행으로 편집한다(docs/spec/status.md, B-1).S-04:
scripts/check-parity.mjs(make parity-check,--kind를 쓰는make parity-<kind>)는 다음을 만족하지 않으면 실패한다: 모든 kind가 자기 패키지를 덮는 최신 기록에서conformance/vectors의 모든 케이스를 한 번씩 실행해 통과하고,contracts/features.json의 모든 기능이 vector 케이스를 가지며, 모든 test id가 케이스 하나를 가리키거나 그 부모이고,TOOL_TESTS의 모든 도구 테스트가 통과한 최신 기록을 남기고, 10분을 넘긴 기록이 없다. 면제는 없다.S-03:
scripts/check-layout.mjs(make layout-check)가 언어 패키지를contracts/layout.json과 대조한다: 패키지 디렉터리는 정확히 tools와 kinds이고, kind의 모든 파일은 정규 layout에 맞으며, 정규·필수 파일이 모두 있고, php와 php-ext의 테스트는 바이트가 같다.make layout-test는 그 테스트를run-tests.mjs로 실행하고.results/layout-test.json을 기록한다.S-02:
docs/spec/status.md가 정의를 고정한다: 세 부분의 모델(machine, code를 가진 상태, edge로 된 event), 규칙 하나 아래의 세 store, code lock, 멱등 전이apply(expected, event, actual), 검증 코드, 여섯 타깃의 이름 규칙. 계획은 이 문서를 가리키므로 정의의 원본은 하나다.S-01: 저장소가 kit v0.0.14(
scripts/kit/,tests/kit/,.kit/kit.lock.json)를 채택하고config/checklist.json,config/commits.json,config/documents.json,config/owner-checks.json,config/toolchain.json(Node.js만),config/dependency-policy.json과 그 review 기록을 둔다. hook은 owner 검증(pre-commit), commit 규칙(commit-msg), push gate(pre-push)를 실행한다.AGENTS.md는 개발 규칙,docs/plans/status-v1.md는 계획,docs/plans/execution-checklist.md는 그 항목을 담는다.make check는 전체 스위트를 트리당 한 번 실행한다.