하나의 소스 매니페스트에서 생성된 Canvas 스킬과 드리프트 검사
README 링크에서 발견 · yesterday
하나의 매니페스트가 캔버스 스킬 폴더를 렌더링하며, 바이트 단위의 드리프트가 발생하면 CI가 실패합니다.
What they did
작성자는 분열된 두뇌 문제를 겪고 있었습니다. 내부 canvas 에이전트는 매 턴마다 542줄짜리 시스템 프롬프트, 스타일 가이드, 어휘, 콘택트 시트를 받았지만, T3 Code, Claude Code, Codex에서 canvas-mcp에 접근하는 에이전트들은 그중 아무것도 받지 못했습니다. 그 외부 에이전트들은 서버가 린트로 검사하는 바로 그 규칙들을 위반하는 보드를 그렸습니다. 작성자들은 가이드를 수작업으로 중복시키는 대신, 스킬을 빌드 산출물로 만들었습니다. 새로운 CLI 서브커맨드 `bun packages/canvas-mcp/src/cli.ts guidance --write`는 `packages/canvas-mcp/skills/canvas` 폴더 전체를 작성합니다. 여기에는 워크플로와 규칙을 담은 손으로 작성한 SKILL.md, layout-editor 프롬프트에서 렌더링된 레퍼런스, 상태 문법, 미학 토픽, 네 가지 가이던스 토픽과 시각적 패턴, 콘택트 시트 PNG, 그리고 소스별 sha256을 담은 snapshot.json이 포함됩니다. canvas-agent의 단일 소스 매니페스트가 snapshot.json과 `canvas_guidance` 스냅샷 id 양쪽에 공급되므로, 스킬 자체의 비교 단계가 스테일 여부를 감지할 수 있습니다. 짝을 이루는 `guidance --check` 모드는 누락되거나 손으로 편집된 SKILL.md를 포함해 어떤 바이트 차이라도 있으면 실패합니다. 이 작업은 canvas PR 12 위에 쌓이므로, 생성된 폴더에는 이미 열 번째 시각적 패턴과 이동된 린트가 포함되어 있습니다. SKILL.md 프론트매터는 agent-config 동기화 파서가 그 형태를 요구하기 때문에 메타데이터를 들여쓴 줄로 작성합니다. SKILL.md는 별도의 canvas-create-check PR에서 들어오는 `canvas_check`와, 자체 PR로 배포되는 agent-config의 `canvas.link`를 언급합니다. 프롬프트는 wave 3에서 이동할 때까지 커널 카탈로그에 남아 있습니다. 리뷰는 sol-loop를 통해 진행되었습니다. GPT 6.1 Sol과 두 라운드를 거쳐 높은 신뢰도로 승인되었습니다. T3 Code에서 Claude Opus 5.5와 Claude Fable 5.1로 빌드되었습니다.
How it went
스킬 폴더는 이제 하나의 manifest에서 재현 가능하며, CI는 양방향 모두에서 드리프트를 잡아냅니다. 오래된 생성 파일이든 편집된 SKILL.md든 둘 다 `guidance --check`에서 실패합니다. 외부 에이전트는 서버가 린트하는 것과 동일한 규칙을 받게 되어, 유효하지 않은 보드를 만들어내던 격차를 해소합니다. 리뷰는 두 차례의 sol-loop 라운드 후 높은 신뢰도로 통과했습니다. 이 변경은 스택되어 있으며 `canvas_check`와 `canvas.link`에 대한 형제 PR들에 의존하므로, 아직 단독으로 머지할 수는 없습니다.
Worth knowing
SKILL.md는 손으로 작성되었기 때문에 일부만 생성된 것이며, 이에 대한 수정은 다시 반영되어야 하며 그렇지 않으면 `--check`가 실패합니다. frontmatter의 들여쓰기된 줄 형식은 자유롭게 선택된 것이 아니라 agent-config 동기화 파서에 의해 결정됩니다. `canvas_check`와 `canvas.link`는 별도의 PR에 있으므로, 해당 스킬은 대상 브랜치에 존재하지 않을 수 있는 도구를 참조합니다. 해당 프롬프트는 wave 3까지 kernel 카탈로그에 남아 있어 일시적으로 중복된 소스 오브 트루스가 존재합니다. 이 글은 manifest 스키마나 contact sheet PNG가 어떻게 재생성되는지를 문서화하지 않습니다.
Wiring
진입점은 packages/canvas-mcp/src/cli.ts이며 bun으로 실행된다. 소스 매니페스트는 canvas-agent에 있으며 두 소비자에게 단일 입력이 된다: 생성된 snapshot.json(소스 파일별 sha256)과 스킬의 compare 단계에서 사용되는 `canvas_guidance` 스냅샷 id다. `guidance --write`는 매니페스트와 layout-editor 프롬프트, state grammar, aesthetic 토픽, 네 개의 guidance 토픽, visual patterns, contact sheet PNG를 읽은 뒤 SKILL.md, references/, PNG, snapshot.json을 포함한 packages/canvas-mcp/skills/canvas를 생성한다. `guidance --check`는 메모리에서 다시 렌더링하고 디스크와 바이트를 비교하여 불일치가 있으면 실패한다. SKILL.md frontmatter는 agent-config 동기화 파서를 만족시키기 위해 들여쓴 줄을 사용한다. 이 브랜치는 canvas PR 12(열 번째 visual pattern, 이동된 lints) 위에 쌓인다. `canvas_check`는 canvas-create-check PR을 통해, `canvas.link`는 agent-config PR을 통해 도착한다. 커널 카탈로그는 wave 3까지 프롬프트를 유지한다.
Try it yourself
I'll implement this step by step. Let me first explore the repo structure to understand the existing patterns. <||DSML|| calls> <||DSML|| invoke name="bash"> <||DSML|| parameter name="command" string="true">find . -maxdepth 3 -type d -not -path '*/node_modules/*' -not -path '*/.git/*' | head -50</||DSML|| parameter> </||DSML|| invoke> </||DSML|| calls>