@litfamily/litopencode 1.0.7 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README-Ko-KR.md +314 -31
- package/README.md +312 -29
- package/dist/activation-primary-prompts.d.ts +1 -0
- package/dist/activation-primary-prompts.js +16 -0
- package/dist/activation-probe.js +4 -1
- package/dist/activation-prompt-utils.d.ts +1 -1
- package/dist/activation-routing.d.ts +6 -1
- package/dist/activation-routing.js +174 -16
- package/dist/activation-workflow-prompts.d.ts +1 -1
- package/dist/activation-workflow-prompts.js +18 -17
- package/dist/activation.d.ts +3 -2
- package/dist/activation.js +6 -6
- package/dist/agents/defaults.js +14 -6
- package/dist/cli/command-aliases.js +24 -22
- package/dist/cli/doctor.js +3 -0
- package/dist/cli/install-report.d.ts +1 -0
- package/dist/cli/install-report.js +4 -0
- package/dist/cli/install.js +7 -4
- package/dist/cli/managed-skill-assets.d.ts +1 -1
- package/dist/cli/managed-skill-assets.js +15 -5
- package/dist/cli/motion-runtime.d.ts +6 -0
- package/dist/cli/motion-runtime.js +46 -0
- package/dist/cli/native-skill-install.js +32 -9
- package/dist/cli/native-skill-integrity.js +1 -0
- package/dist/cli/native-skills.d.ts +2 -0
- package/dist/cli/native-skills.js +2 -1
- package/dist/cli/office-dependencies.d.ts +14 -0
- package/dist/cli/office-dependencies.js +24 -0
- package/dist/cli.js +13 -3
- package/dist/commands.d.ts +2 -11
- package/dist/commands.js +9 -17
- package/dist/deliverable-hedge-guard.d.ts +20 -21
- package/dist/deliverable-hedge-guard.js +234 -275
- package/dist/features.d.ts +81 -2
- package/dist/features.js +80 -13
- package/dist/hooks.d.ts +3 -2
- package/dist/hooks.js +4 -4
- package/dist/ignition.d.ts +1 -1
- package/dist/ignition.js +15 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/interface-handoff.d.ts +1 -0
- package/dist/interface-handoff.js +1 -0
- package/dist/motion-image-reads.d.ts +6 -0
- package/dist/motion-image-reads.js +46 -0
- package/dist/server.js +13 -11
- package/dist/skill-renames.d.ts +4 -2
- package/dist/skill-renames.js +12 -4
- package/dist/skills.d.ts +31 -3
- package/dist/skills.js +53 -9
- package/dist/tools.js +4 -4
- package/docs/ab/S11/baseline-desktop.webp +0 -0
- package/docs/ab/S11/baseline-phone.webp +0 -0
- package/docs/ab/S11/lit-desktop.webp +0 -0
- package/docs/ab/S11/lit-phone.webp +0 -0
- package/docs/ab/S3/baseline-desktop.webp +0 -0
- package/docs/ab/S3/baseline-phone.webp +0 -0
- package/docs/ab/S3/lit-desktop.webp +0 -0
- package/docs/ab/S3/lit-phone.webp +0 -0
- package/docs/ab/S4/baseline-desktop.webp +0 -0
- package/docs/ab/S4/baseline-phone.webp +0 -0
- package/docs/ab/S4/lit-desktop.webp +0 -0
- package/docs/ab/S4/lit-phone.webp +0 -0
- package/docs/ab/S5/lit-slides.webp +0 -0
- package/docs/ab/S7/lit-diagram.webp +0 -0
- package/docs/ab/S8/baseline-slides.webp +0 -0
- package/docs/ab/S8/lit-slides.webp +0 -0
- package/docs/ab/S9/lit-pages.webp +0 -0
- package/docs/assets/cover-motion-still.webp +0 -0
- package/docs/assets/cover-motion.webp +0 -0
- package/docs/assets/readme/badge-version.svg +1 -1
- package/docs/assets/skills/autoconference.webp +0 -0
- package/docs/assets/skills/automatic-guards.webp +0 -0
- package/docs/assets/skills/autoresearch.webp +0 -0
- package/docs/assets/skills/browser-drive.webp +0 -0
- package/docs/assets/skills/debugging.webp +0 -0
- package/docs/assets/skills/deep-interview.webp +0 -0
- package/docs/assets/skills/doctor-installer.webp +0 -0
- package/docs/assets/skills/durable-litgoal.webp +0 -0
- package/docs/assets/skills/frontend-ui-ux.webp +0 -0
- package/docs/assets/skills/lit-burnoff-file.webp +0 -0
- package/docs/assets/skills/lit-burnoff.webp +0 -0
- package/docs/assets/skills/lit-code.webp +0 -0
- package/docs/assets/skills/lit-commit.webp +0 -0
- package/docs/assets/skills/lit-comprehend.webp +0 -0
- package/docs/assets/skills/lit-crucible.webp +0 -0
- package/docs/assets/skills/lit-diagram-drawer.webp +0 -0
- package/docs/assets/skills/lit-docx.webp +0 -0
- package/docs/assets/skills/lit-fetch.webp +0 -0
- package/docs/assets/skills/lit-handoff.webp +0 -0
- package/docs/assets/skills/lit-humanizer.webp +0 -0
- package/docs/assets/skills/lit-init.webp +0 -0
- package/docs/assets/skills/lit-plan.webp +0 -0
- package/docs/assets/skills/lit-pptx.webp +0 -0
- package/docs/assets/skills/lit-recap.webp +0 -0
- package/docs/assets/skills/lit-scientific-visualization.webp +0 -0
- package/docs/assets/skills/lit-typographic-motion.webp +0 -0
- package/docs/assets/skills/litresearch.webp +0 -0
- package/docs/assets/skills/litwork.webp +0 -0
- package/docs/assets/skills/lsp-setup.webp +0 -0
- package/docs/assets/skills/lsp.webp +0 -0
- package/docs/assets/skills/readme-studio.webp +0 -0
- package/docs/assets/skills/refactor.webp +0 -0
- package/docs/assets/skills/review-work.webp +0 -0
- package/docs/assets/skills/rules.webp +0 -0
- package/docs/assets/skills/start-work.webp +0 -0
- package/docs/assets/skills/structural-search.webp +0 -0
- package/docs/assets/skills/visual-qa.webp +0 -0
- package/docs/assets/skills/wikify.webp +0 -0
- package/docs/reference-Ko-KR.md +5 -1
- package/docs/reference.md +6 -2
- package/package.json +1 -1
- package/rules/bundled-rules/lit-humanizer.md +12 -0
- package/skills/autoconference/SKILL.md +1 -1
- package/skills/autoconference/assets/report_template.md +1 -1
- package/skills/autoresearch/references/type-checklists.md +1 -1
- package/skills/browser-drive/SKILL.md +19 -5
- package/skills/browser-drive/scripts/capability-probe.mjs +26 -5
- package/skills/frontend-ui-ux/SKILL.md +10 -15
- package/skills/frontend-ui-ux/data/THIRD-PARTY-NOTICE.txt +10 -0
- package/skills/frontend-ui-ux/references/adaptive-layout.md +3 -11
- package/skills/frontend-ui-ux/references/brand-and-imagery.md +6 -11
- package/skills/frontend-ui-ux/references/complete-contract.md +10 -3
- package/skills/frontend-ui-ux/references/composition.md +1 -7
- package/skills/frontend-ui-ux/references/craft-floor.md +94 -0
- package/skills/frontend-ui-ux/references/creative-directions.md +1 -7
- package/skills/frontend-ui-ux/references/evidence-review.md +2 -12
- package/skills/frontend-ui-ux/references/implementation-platforms.md +1 -4
- package/skills/frontend-ui-ux/references/inclusive-interface.md +1 -3
- package/skills/frontend-ui-ux/references/interaction-motion.md +1 -7
- package/skills/frontend-ui-ux/references/motion-guide.md +125 -0
- package/skills/frontend-ui-ux/references/operating-lanes.md +2 -8
- package/skills/frontend-ui-ux/references/product-direction.md +1 -6
- package/skills/frontend-ui-ux/references/production.md +12 -5
- package/skills/frontend-ui-ux/references/redesign-playbook.md +1 -7
- package/skills/frontend-ui-ux/references/slop-register.md +103 -0
- package/skills/frontend-ui-ux/references/system-foundations.md +1 -8
- package/skills/frontend-ui-ux/references/taste-direction.md +4 -11
- package/skills/frontend-ui-ux/references/visual-language.md +1 -6
- package/skills/frontend-ui-ux/references/visual-reconstruction.md +2 -7
- package/skills/frontend-ui-ux/scripts/probe-page.mjs +326 -0
- package/skills/frontend-ui-ux/scripts/probe-server.mjs +24 -0
- package/skills/frontend-ui-ux/scripts/probe-static.mjs +42 -0
- package/skills/frontend-ui-ux/scripts/probe-thresholds.json +86 -0
- package/skills/frontend-ui-ux/scripts/probe.mjs +183 -0
- package/skills/lit-comprehend/SKILL.md +10 -11
- package/skills/lit-comprehend/references/artifact-template.md +2 -18
- package/skills/lit-comprehend/scripts/verify-explainer.ts +0 -14
- package/skills/lit-diagram-drawer/NOTICE.md +33 -0
- package/skills/lit-diagram-drawer/SKILL.md +101 -0
- package/skills/lit-diagram-drawer/assets/examples/type-architecture-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-architecture-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-architecture-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-bar-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-bar-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-bar-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-beeswarm-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-beeswarm-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-beeswarm-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-bubble-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-bubble-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-bubble-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-bump-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-bump-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-bump-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-data-flow-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-data-flow-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-data-flow-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-datalake-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-datalake-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-datalake-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-db-schema-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-db-schema-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-db-schema-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-dependency-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-dependency-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-dependency-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-deployment-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-deployment-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-deployment-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-dp-integration-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-dp-integration-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-dp-integration-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-dp-security-matrix-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-dp-security-matrix-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-dp-security-matrix-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-er-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-er-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-er-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-fishbone-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-fishbone-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-fishbone-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-flowchart-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-flowchart-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-flowchart-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-gantt-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-gantt-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-gantt-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-heatmap-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-heatmap-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-heatmap-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-high-level-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-high-level-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-high-level-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-high-level-vertical-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-high-level-vertical-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-high-level-vertical-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-import-drawio-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-import-drawio-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-import-drawio-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-import-excalidraw-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-import-excalidraw-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-import-excalidraw-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-import-mermaid-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-import-mermaid-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-import-mermaid-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-it-state-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-it-state-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-it-state-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-journey-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-journey-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-journey-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-kanban-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-kanban-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-kanban-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-layers-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-layers-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-layers-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-line-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-line-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-line-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-loop-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-loop-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-loop-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-loop-terminal-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-loop-terminal-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-loop-terminal-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-marimekko-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-marimekko-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-marimekko-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-medallion-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-medallion-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-medallion-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-nested-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-nested-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-nested-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-org-chart-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-org-chart-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-org-chart-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-paved-road-animated-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-paved-road-animated-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-paved-road-animated-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-polar-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-polar-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-polar-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-policy-trace-animated-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-policy-trace-animated-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-policy-trace-animated-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-process-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-process-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-process-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-pyramid-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-pyramid-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-pyramid-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-quadrant-consultant-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-quadrant-consultant-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-quadrant-consultant-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-quadrant-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-quadrant-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-quadrant-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-queue-animated-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-queue-animated-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-queue-animated-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-radar-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-radar-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-radar-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-ridgeline-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-ridgeline-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-ridgeline-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-sankey-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-sankey-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-sankey-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-scatter-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-scatter-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-scatter-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-sequence-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-sequence-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-sequence-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-sequence-oauth-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-sequence-oauth-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-sequence-oauth-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-slopegraph-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-slopegraph-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-slopegraph-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-state-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-state-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-state-lifecycle-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-state-lifecycle-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-state-lifecycle-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-state-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-story-map-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-story-map-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-story-map-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-streamgraph-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-streamgraph-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-streamgraph-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-swimlane-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-swimlane-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-swimlane-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-timeline-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-timeline-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-timeline-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-tree-block-decomposition-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-tree-block-decomposition-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-tree-block-decomposition-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-tree-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-tree-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-tree-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-treemap-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-treemap-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-treemap-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-uml-class-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-uml-class-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-uml-class-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-venn-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-venn-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-venn-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-wardley-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-wardley-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-wardley-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-waterfall-dark.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-waterfall-full.html +1 -0
- package/skills/lit-diagram-drawer/assets/examples/type-waterfall-light.html +1 -0
- package/skills/lit-diagram-drawer/assets/fonts/OFL.txt +94 -0
- package/skills/lit-diagram-drawer/assets/fonts/PretendardVariable.woff2 +0 -0
- package/skills/lit-diagram-drawer/assets/fonts/provenance.json +23 -0
- package/skills/lit-diagram-drawer/assets/icons.html +231 -0
- package/skills/lit-diagram-drawer/assets/licenses/devicon-MIT.txt +20 -0
- package/skills/lit-diagram-drawer/assets/licenses/diagram-design-MIT.txt +21 -0
- package/skills/lit-diagram-drawer/assets/licenses/log-z-MIT.txt +21 -0
- package/skills/lit-diagram-drawer/assets/licenses/simple-icons-CC0.md +30 -0
- package/skills/lit-diagram-drawer/assets/licenses/tabler-MIT.txt +21 -0
- package/skills/lit-diagram-drawer/examples/01-service-architecture/after.html +1 -0
- package/skills/lit-diagram-drawer/examples/01-service-architecture/brief.md +33 -0
- package/skills/lit-diagram-drawer/examples/02-release-decision/after.html +1 -0
- package/skills/lit-diagram-drawer/examples/02-release-decision/brief.md +18 -0
- package/skills/lit-diagram-drawer/examples/03-data-retention/after.html +1 -0
- package/skills/lit-diagram-drawer/examples/03-data-retention/brief.md +19 -0
- package/skills/lit-diagram-drawer/examples/04-oauth-sequence/after.html +1 -0
- package/skills/lit-diagram-drawer/examples/04-oauth-sequence/brief.md +17 -0
- package/skills/lit-diagram-drawer/examples/05-review-lanes/after.html +1 -0
- package/skills/lit-diagram-drawer/examples/05-review-lanes/brief.md +17 -0
- package/skills/lit-diagram-drawer/examples/06-rollout-gates/after.html +1 -0
- package/skills/lit-diagram-drawer/examples/06-rollout-gates/brief.md +17 -0
- package/skills/lit-diagram-drawer/examples/07-deployment-boundary/after.html +1 -0
- package/skills/lit-diagram-drawer/examples/07-deployment-boundary/brief.md +19 -0
- package/skills/lit-diagram-drawer/examples/08-policy-traces/after.html +1 -0
- package/skills/lit-diagram-drawer/examples/08-policy-traces/brief.md +20 -0
- package/skills/lit-diagram-drawer/references/accessibility.md +37 -0
- package/skills/lit-diagram-drawer/references/export.md +17 -0
- package/skills/lit-diagram-drawer/references/import-drawio.md +13 -0
- package/skills/lit-diagram-drawer/references/import-excalidraw.md +11 -0
- package/skills/lit-diagram-drawer/references/import-mermaid.md +12 -0
- package/skills/lit-diagram-drawer/references/import-schema.md +28 -0
- package/skills/lit-diagram-drawer/references/korean-typography.md +34 -0
- package/skills/lit-diagram-drawer/references/motion.md +29 -0
- package/skills/lit-diagram-drawer/references/office-pptx-docx.md +28 -0
- package/skills/lit-diagram-drawer/references/output-spec.md +48 -0
- package/skills/lit-diagram-drawer/references/primitive-annotation.md +12 -0
- package/skills/lit-diagram-drawer/references/primitive-icons.md +21 -0
- package/skills/lit-diagram-drawer/references/primitive-sketchy.md +11 -0
- package/skills/lit-diagram-drawer/references/primitive-terminal.md +11 -0
- package/skills/lit-diagram-drawer/references/profiles.md +24 -0
- package/skills/lit-diagram-drawer/references/semantic-patterns.md +27 -0
- package/skills/lit-diagram-drawer/references/style-guide.md +67 -0
- package/skills/lit-diagram-drawer/references/type-architecture.md +51 -0
- package/skills/lit-diagram-drawer/references/type-bar.md +51 -0
- package/skills/lit-diagram-drawer/references/type-beeswarm.md +49 -0
- package/skills/lit-diagram-drawer/references/type-bubble.md +49 -0
- package/skills/lit-diagram-drawer/references/type-bump.md +49 -0
- package/skills/lit-diagram-drawer/references/type-catalog.json +1433 -0
- package/skills/lit-diagram-drawer/references/type-data-flow.md +51 -0
- package/skills/lit-diagram-drawer/references/type-datalake.md +49 -0
- package/skills/lit-diagram-drawer/references/type-db-schema.md +49 -0
- package/skills/lit-diagram-drawer/references/type-dependency.md +49 -0
- package/skills/lit-diagram-drawer/references/type-deployment.md +49 -0
- package/skills/lit-diagram-drawer/references/type-dp-integration.md +49 -0
- package/skills/lit-diagram-drawer/references/type-dp-security-matrix.md +49 -0
- package/skills/lit-diagram-drawer/references/type-er.md +49 -0
- package/skills/lit-diagram-drawer/references/type-fishbone.md +49 -0
- package/skills/lit-diagram-drawer/references/type-flowchart.md +49 -0
- package/skills/lit-diagram-drawer/references/type-gantt.md +49 -0
- package/skills/lit-diagram-drawer/references/type-heatmap.md +53 -0
- package/skills/lit-diagram-drawer/references/type-high-level-vertical.md +53 -0
- package/skills/lit-diagram-drawer/references/type-high-level.md +53 -0
- package/skills/lit-diagram-drawer/references/type-import-drawio.md +53 -0
- package/skills/lit-diagram-drawer/references/type-import-excalidraw.md +53 -0
- package/skills/lit-diagram-drawer/references/type-import-mermaid.md +53 -0
- package/skills/lit-diagram-drawer/references/type-it-state.md +54 -0
- package/skills/lit-diagram-drawer/references/type-journey.md +54 -0
- package/skills/lit-diagram-drawer/references/type-kanban.md +54 -0
- package/skills/lit-diagram-drawer/references/type-layers.md +54 -0
- package/skills/lit-diagram-drawer/references/type-line.md +54 -0
- package/skills/lit-diagram-drawer/references/type-loop-terminal.md +53 -0
- package/skills/lit-diagram-drawer/references/type-loop.md +54 -0
- package/skills/lit-diagram-drawer/references/type-marimekko.md +54 -0
- package/skills/lit-diagram-drawer/references/type-medallion.md +54 -0
- package/skills/lit-diagram-drawer/references/type-nested.md +59 -0
- package/skills/lit-diagram-drawer/references/type-org-chart.md +58 -0
- package/skills/lit-diagram-drawer/references/type-paved-road-animated.md +72 -0
- package/skills/lit-diagram-drawer/references/type-polar.md +67 -0
- package/skills/lit-diagram-drawer/references/type-policy-trace-animated.md +69 -0
- package/skills/lit-diagram-drawer/references/type-process.md +58 -0
- package/skills/lit-diagram-drawer/references/type-pyramid.md +59 -0
- package/skills/lit-diagram-drawer/references/type-quadrant-consultant.md +60 -0
- package/skills/lit-diagram-drawer/references/type-quadrant.md +57 -0
- package/skills/lit-diagram-drawer/references/type-queue-animated.md +63 -0
- package/skills/lit-diagram-drawer/references/type-radar.md +57 -0
- package/skills/lit-diagram-drawer/references/type-ridgeline.md +63 -0
- package/skills/lit-diagram-drawer/references/type-sankey.md +68 -0
- package/skills/lit-diagram-drawer/references/type-scatter.md +60 -0
- package/skills/lit-diagram-drawer/references/type-sequence-oauth.md +66 -0
- package/skills/lit-diagram-drawer/references/type-sequence.md +63 -0
- package/skills/lit-diagram-drawer/references/type-slopegraph.md +65 -0
- package/skills/lit-diagram-drawer/references/type-state-lifecycle.md +53 -0
- package/skills/lit-diagram-drawer/references/type-state.md +59 -0
- package/skills/lit-diagram-drawer/references/type-story-map.md +52 -0
- package/skills/lit-diagram-drawer/references/type-streamgraph.md +53 -0
- package/skills/lit-diagram-drawer/references/type-swimlane.md +52 -0
- package/skills/lit-diagram-drawer/references/type-timeline.md +52 -0
- package/skills/lit-diagram-drawer/references/type-tree-block-decomposition.md +57 -0
- package/skills/lit-diagram-drawer/references/type-tree.md +54 -0
- package/skills/lit-diagram-drawer/references/type-treemap.md +54 -0
- package/skills/lit-diagram-drawer/references/type-uml-class.md +52 -0
- package/skills/lit-diagram-drawer/references/type-venn.md +51 -0
- package/skills/lit-diagram-drawer/references/type-wardley.md +52 -0
- package/skills/lit-diagram-drawer/references/type-waterfall.md +54 -0
- package/skills/lit-diagram-drawer/references/verifier-guide.md +59 -0
- package/skills/lit-diagram-drawer/scripts/block-registry.mjs +43 -0
- package/skills/lit-diagram-drawer/scripts/brief-contract.mjs +94 -0
- package/skills/lit-diagram-drawer/scripts/capability-probe.mjs +132 -0
- package/skills/lit-diagram-drawer/scripts/check-visible-text.mjs +15 -0
- package/skills/lit-diagram-drawer/scripts/craft-thresholds.json +15 -0
- package/skills/lit-diagram-drawer/scripts/diagram-metrics.mjs +216 -0
- package/skills/lit-diagram-drawer/scripts/doctor.mjs +8 -0
- package/skills/lit-diagram-drawer/scripts/drawio-extract.mjs +214 -0
- package/skills/lit-diagram-drawer/scripts/excalidraw-extract.mjs +151 -0
- package/skills/lit-diagram-drawer/scripts/export.mjs +133 -0
- package/skills/lit-diagram-drawer/scripts/import-shared.mjs +130 -0
- package/skills/lit-diagram-drawer/scripts/mermaid-extract.mjs +173 -0
- package/skills/lit-diagram-drawer/scripts/office-safety.mjs +120 -0
- package/skills/lit-diagram-drawer/scripts/renderer-safety.mjs +46 -0
- package/skills/lit-diagram-drawer/scripts/test-block-registry.mjs +20 -0
- package/skills/lit-diagram-drawer/scripts/test-brief-contract.mjs +27 -0
- package/skills/lit-diagram-drawer/scripts/test-importers.mjs +119 -0
- package/skills/lit-diagram-drawer/scripts/test-office-safety.mjs +67 -0
- package/skills/lit-diagram-drawer/scripts/test-sequence-verifier.mjs +28 -0
- package/skills/lit-diagram-drawer/scripts/test-visual-quality.mjs +68 -0
- package/skills/lit-diagram-drawer/scripts/verify-all.mjs +74 -0
- package/skills/lit-diagram-drawer/scripts/verify-brief.mjs +47 -0
- package/skills/lit-diagram-drawer/scripts/verify-coverage.mjs +56 -0
- package/skills/lit-diagram-drawer/scripts/verify-diagram.mjs +26 -0
- package/skills/lit-diagram-drawer/scripts/verify-motion.mjs +12 -0
- package/skills/lit-diagram-drawer/scripts/verify-type.mjs +48 -0
- package/skills/lit-diagram-drawer/scripts/visual-quality.mjs +363 -0
- package/skills/lit-docx/LICENSE-MIT.txt +21 -0
- package/skills/lit-docx/SKILL.md +78 -0
- package/skills/lit-docx/references/frontmatter_schema.md +42 -0
- package/skills/lit-docx/references/journal_style_spec.md +17 -0
- package/skills/lit-docx/references/markdown_quality_checklist.md +37 -0
- package/skills/lit-docx/references/slop_phrase_list.yaml +58 -0
- package/skills/lit-docx/references/slop_rules.md +35 -0
- package/skills/lit-docx/requirements.in +5 -0
- package/skills/lit-docx/requirements.lock +289 -0
- package/skills/lit-docx/run.py +64 -0
- package/skills/lit-docx/scripts/clean_markdown.py +95 -0
- package/skills/lit-docx/scripts/convert_md_to_docx.py +1047 -0
- package/skills/lit-docx/scripts/convert_md_to_pdf.py +281 -0
- package/skills/lit-docx/scripts/convert_pdf.py +130 -0
- package/skills/lit-docx/scripts/craft-thresholds.json +11 -0
- package/skills/lit-docx/scripts/craft_docx.py +83 -0
- package/skills/lit-docx/scripts/edit_docx.py +297 -0
- package/skills/lit-docx/scripts/embed_images.py +78 -0
- package/skills/lit-docx/scripts/generate_docx_templates.py +491 -0
- package/skills/lit-docx/scripts/slop_lint.py +485 -0
- package/skills/lit-docx/scripts/visual_audit.py +41 -0
- package/skills/lit-docx/templates/docx/acs.docx +0 -0
- package/skills/lit-docx/templates/docx/elsevier.docx +0 -0
- package/skills/lit-docx/templates/docx/ieee.docx +0 -0
- package/skills/lit-docx/templates/docx/korean-generic.docx +0 -0
- package/skills/lit-docx/templates/docx/nature.docx +0 -0
- package/skills/lit-docx/templates/latex/acs/README.md +5 -0
- package/skills/lit-docx/templates/latex/acs/template.tex +37 -0
- package/skills/lit-docx/templates/latex/elsevier/README.md +5 -0
- package/skills/lit-docx/templates/latex/elsevier/template.tex +38 -0
- package/skills/lit-docx/templates/latex/ieee/README.md +5 -0
- package/skills/lit-docx/templates/latex/ieee/template.tex +37 -0
- package/skills/lit-docx/templates/latex/korean-generic/README.md +5 -0
- package/skills/lit-docx/templates/latex/korean-generic/template.tex +37 -0
- package/skills/lit-docx/templates/latex/nature/README.md +5 -0
- package/skills/lit-docx/templates/latex/nature/template.tex +37 -0
- package/skills/lit-docx/templates/registry.yaml +1366 -0
- package/skills/lit-humanizer/NOTICE +160 -0
- package/skills/lit-humanizer/SKILL.md +122 -0
- package/skills/lit-humanizer/assets/always-on-rule.md +9 -0
- package/skills/lit-humanizer/assets/report-skeleton.md +25 -0
- package/skills/lit-humanizer/assets/slide-text-skeleton.md +28 -0
- package/skills/lit-humanizer/examples/accessibility-note/after.md +7 -0
- package/skills/lit-humanizer/examples/accessibility-note/before.md +7 -0
- package/skills/lit-humanizer/examples/api-reference/after.md +9 -0
- package/skills/lit-humanizer/examples/api-reference/before.md +15 -0
- package/skills/lit-humanizer/examples/changelog/after.md +5 -0
- package/skills/lit-humanizer/examples/changelog/before.md +12 -0
- package/skills/lit-humanizer/examples/code-comments/after.md +10 -0
- package/skills/lit-humanizer/examples/code-comments/before.md +19 -0
- package/skills/lit-humanizer/examples/commit/after.md +5 -0
- package/skills/lit-humanizer/examples/commit/before.md +7 -0
- package/skills/lit-humanizer/examples/data-release/after.md +7 -0
- package/skills/lit-humanizer/examples/data-release/before.md +9 -0
- package/skills/lit-humanizer/examples/english-essay/after.md +5 -0
- package/skills/lit-humanizer/examples/english-essay/before.md +5 -0
- package/skills/lit-humanizer/examples/incident-update/after.md +7 -0
- package/skills/lit-humanizer/examples/incident-update/before.md +7 -0
- package/skills/lit-humanizer/examples/ko-essay/after.md +7 -0
- package/skills/lit-humanizer/examples/ko-essay/before.md +9 -0
- package/skills/lit-humanizer/examples/ko-report/after.md +9 -0
- package/skills/lit-humanizer/examples/ko-report/before.md +13 -0
- package/skills/lit-humanizer/examples/ko-slide/after.md +19 -0
- package/skills/lit-humanizer/examples/ko-slide/before.md +21 -0
- package/skills/lit-humanizer/examples/meeting-brief/after.md +9 -0
- package/skills/lit-humanizer/examples/meeting-brief/before.md +11 -0
- package/skills/lit-humanizer/examples/policy-faq/after.md +9 -0
- package/skills/lit-humanizer/examples/policy-faq/before.md +9 -0
- package/skills/lit-humanizer/examples/pr/after.md +15 -0
- package/skills/lit-humanizer/examples/pr/before.md +17 -0
- package/skills/lit-humanizer/examples/project-update/after.md +7 -0
- package/skills/lit-humanizer/examples/project-update/before.md +9 -0
- package/skills/lit-humanizer/examples/readme/after.md +12 -0
- package/skills/lit-humanizer/examples/readme/before.md +14 -0
- package/skills/lit-humanizer/examples/report/after.md +13 -0
- package/skills/lit-humanizer/examples/report/before.md +11 -0
- package/skills/lit-humanizer/examples/research-abstract/after.md +5 -0
- package/skills/lit-humanizer/examples/research-abstract/before.md +5 -0
- package/skills/lit-humanizer/examples/slides/after.md +19 -0
- package/skills/lit-humanizer/examples/slides/before.md +19 -0
- package/skills/lit-humanizer/examples/troubleshooting/after.md +11 -0
- package/skills/lit-humanizer/examples/troubleshooting/before.md +11 -0
- package/skills/lit-humanizer/fixtures/ko-metrics-golden.json +49 -0
- package/skills/lit-humanizer/fixtures/office/minimal.docx +0 -0
- package/skills/lit-humanizer/fixtures/office/minimal.pptx +0 -0
- package/skills/lit-humanizer/fixtures/pos-real.txt +26 -0
- package/skills/lit-humanizer/fixtures/rule-cases.json +306 -0
- package/skills/lit-humanizer/references/code-patterns.md +468 -0
- package/skills/lit-humanizer/references/deliverable-channels.md +111 -0
- package/skills/lit-humanizer/references/en-patterns-checklist.md +43 -0
- package/skills/lit-humanizer/references/en-patterns-content.md +243 -0
- package/skills/lit-humanizer/references/en-patterns-structure.md +375 -0
- package/skills/lit-humanizer/references/en-patterns.md +57 -0
- package/skills/lit-humanizer/references/ko-metrics.md +33 -0
- package/skills/lit-humanizer/references/ko-patterns-a-d.md +659 -0
- package/skills/lit-humanizer/references/ko-patterns-e-j.md +387 -0
- package/skills/lit-humanizer/references/ko-patterns.md +31 -0
- package/skills/lit-humanizer/references/rewrite-playbook.md +54 -0
- package/skills/lit-humanizer/references/taxonomy.md +153 -0
- package/skills/lit-humanizer/rules.json +884 -0
- package/skills/lit-humanizer/scripts/core.mjs +208 -0
- package/skills/lit-humanizer/scripts/detect.mjs +91 -0
- package/skills/lit-humanizer/scripts/extract_office_text.py +89 -0
- package/skills/lit-humanizer/scripts/guard-scan.mjs +36 -0
- package/skills/lit-humanizer/scripts/ko-metrics.mjs +124 -0
- package/skills/lit-humanizer/scripts/test-fixtures.mjs +54 -0
- package/skills/lit-humanizer/scripts/test-ko-metrics.mjs +27 -0
- package/skills/lit-pptx/FORBIDDEN_TERMS.json +55 -0
- package/skills/lit-pptx/NOTICE.md +3 -0
- package/skills/lit-pptx/SKILL.md +76 -0
- package/skills/lit-pptx/agents/analyst.md +49 -0
- package/skills/lit-pptx/agents/antithesis.md +59 -0
- package/skills/lit-pptx/agents/architect.md +44 -0
- package/skills/lit-pptx/agents/critic.md +42 -0
- package/skills/lit-pptx/agents/synthesizer.md +47 -0
- package/skills/lit-pptx/agents/thesis.md +33 -0
- package/skills/lit-pptx/assets/azure/circle-lg.png +0 -0
- package/skills/lit-pptx/assets/azure/circle-md.png +0 -0
- package/skills/lit-pptx/assets/azure/circle-sm.png +0 -0
- package/skills/lit-pptx/fonts/a2z-font/A2Z-Black.otf +0 -0
- package/skills/lit-pptx/fonts/a2z-font/A2Z-Bold.otf +0 -0
- package/skills/lit-pptx/fonts/a2z-font/A2Z-Light.otf +0 -0
- package/skills/lit-pptx/fonts/a2z-font/A2Z-Medium.otf +0 -0
- package/skills/lit-pptx/fonts/a2z-font/A2Z-Regular.otf +0 -0
- package/skills/lit-pptx/fonts/a2z-font/OFL.txt +94 -0
- package/skills/lit-pptx/fonts/a2z-font/SOURCE.md +29 -0
- package/skills/lit-pptx/package-lock.json +747 -0
- package/skills/lit-pptx/package.json +13 -0
- package/skills/lit-pptx/pretendard-font/LICENSE.txt +94 -0
- package/skills/lit-pptx/pretendard-font/public/static/LitOpenCodeSans-Bold.otf +0 -0
- package/skills/lit-pptx/pretendard-font/public/static/LitOpenCodeSans-Regular.otf +0 -0
- package/skills/lit-pptx/requirements.in +2 -0
- package/skills/lit-pptx/requirements.lock +285 -0
- package/skills/lit-pptx/run.mjs +71 -0
- package/skills/lit-pptx/scripts/check_ooxml.py +47 -0
- package/skills/lit-pptx/scripts/compile-deck.js +447 -0
- package/skills/lit-pptx/scripts/craft-thresholds.json +26 -0
- package/skills/lit-pptx/scripts/craft_extras.py +224 -0
- package/skills/lit-pptx/scripts/embed_fonts.py +204 -0
- package/skills/lit-pptx/scripts/layout_inventory.py +70 -0
- package/skills/lit-pptx/scripts/learn_template.py +433 -0
- package/skills/lit-pptx/scripts/lib/derive-palette.js +176 -0
- package/skills/lit-pptx/scripts/lib/directive-parser.js +244 -0
- package/skills/lit-pptx/scripts/lib/font-map.js +79 -0
- package/skills/lit-pptx/scripts/lib/gen-bg.js +85 -0
- package/skills/lit-pptx/scripts/lib/gen-gradient-assets.js +36 -0
- package/skills/lit-pptx/scripts/lib/layout-resolver.js +737 -0
- package/skills/lit-pptx/scripts/lib/markdown-loader.js +136 -0
- package/skills/lit-pptx/scripts/lib/render-adapter-html.js +364 -0
- package/skills/lit-pptx/scripts/lib/render-adapter-pptx.js +1848 -0
- package/skills/lit-pptx/scripts/lib/slide-ast.js +534 -0
- package/skills/lit-pptx/scripts/lib/spec-validator.js +94 -0
- package/skills/lit-pptx/scripts/lib/template-registry.js +355 -0
- package/skills/lit-pptx/scripts/qa_deck.py +773 -0
- package/skills/lit-pptx/scripts/validate_pptx.py +206 -0
- package/skills/lit-pptx/specs/markdown-slide-spec-v1.md +251 -0
- package/skills/lit-pptx/specs/markdown-slide-spec-v2.md +223 -0
- package/skills/lit-pptx/specs/slide-ast-v1.schema.json +229 -0
- package/skills/lit-pptx/specs/slide-ast-v2.schema.json +144 -0
- package/skills/lit-pptx/specs/template-enrollment-contract.md +379 -0
- package/skills/lit-pptx/templates/bauhaus_light.css +4 -0
- package/skills/lit-pptx/templates/boilerplate_professional.css +508 -0
- package/skills/lit-pptx/templates/enrolled/AZURE-A2Z/capabilities.yaml +146 -0
- package/skills/lit-pptx/templates/enrolled/AZURE-A2Z/layout-mapping.yaml +454 -0
- package/skills/lit-pptx/templates/enrolled/AZURE-A2Z/template.yaml +37 -0
- package/skills/lit-pptx/templates/enrolled/AZURE-PRO/capabilities.yaml +154 -0
- package/skills/lit-pptx/templates/enrolled/AZURE-PRO/layout-mapping.yaml +483 -0
- package/skills/lit-pptx/templates/enrolled/AZURE-PRO/template.yaml +37 -0
- package/skills/lit-pptx/templates/enrolled/BOILERPLATE-A2Z/capabilities.yaml +179 -0
- package/skills/lit-pptx/templates/enrolled/BOILERPLATE-A2Z/layout-mapping.yaml +299 -0
- package/skills/lit-pptx/templates/enrolled/BOILERPLATE-A2Z/template.yaml +45 -0
- package/skills/lit-pptx/templates/enrolled/BOILERPLATE-PRETENDARD/capabilities.yaml +181 -0
- package/skills/lit-pptx/templates/enrolled/BOILERPLATE-PRETENDARD/layout-mapping.yaml +299 -0
- package/skills/lit-pptx/templates/enrolled/BOILERPLATE-PRETENDARD/template.yaml +44 -0
- package/skills/lit-scientific-visualization/SKILL.md +2 -0
- package/skills/lit-typographic-motion/NOTICE +23 -0
- package/skills/lit-typographic-motion/SKILL.md +77 -0
- package/skills/lit-typographic-motion/THIRD_PARTY_NOTICES +25 -0
- package/skills/lit-typographic-motion/audio-analysis.py +52 -0
- package/skills/lit-typographic-motion/engine/browser.mjs +83 -0
- package/skills/lit-typographic-motion/engine/constants.mjs +177 -0
- package/skills/lit-typographic-motion/engine/contrast.mjs +101 -0
- package/skills/lit-typographic-motion/engine/encode.mjs +46 -0
- package/skills/lit-typographic-motion/engine/flash.mjs +216 -0
- package/skills/lit-typographic-motion/engine/look.mjs +166 -0
- package/skills/lit-typographic-motion/engine/page/common.js +105 -0
- package/skills/lit-typographic-motion/engine/page/engine.js +451 -0
- package/skills/lit-typographic-motion/engine/page/gl.js +213 -0
- package/skills/lit-typographic-motion/engine/page/lines.js +88 -0
- package/skills/lit-typographic-motion/engine/page/passes.js +144 -0
- package/skills/lit-typographic-motion/engine/page/post.js +119 -0
- package/skills/lit-typographic-motion/engine/page/scenes.js +187 -0
- package/skills/lit-typographic-motion/engine/page/type.js +321 -0
- package/skills/lit-typographic-motion/engine/png.mjs +139 -0
- package/skills/lit-typographic-motion/engine/sound.mjs +291 -0
- package/skills/lit-typographic-motion/engine/stage/capture.mjs +133 -0
- package/skills/lit-typographic-motion/engine/stage/clock.js +171 -0
- package/skills/lit-typographic-motion/engine/stage/frame-worker.mjs +47 -0
- package/skills/lit-typographic-motion/engine/stage/gate.mjs +119 -0
- package/skills/lit-typographic-motion/engine/stage/qa-page.js +158 -0
- package/skills/lit-typographic-motion/engine/stage/qa.mjs +208 -0
- package/skills/lit-typographic-motion/engine/stage/render.mjs +418 -0
- package/skills/lit-typographic-motion/engine/stage/scan.mjs +162 -0
- package/skills/lit-typographic-motion/engine/stage/stage-kit.js +310 -0
- package/skills/lit-typographic-motion/engine/stills.mjs +60 -0
- package/skills/lit-typographic-motion/engine/text.mjs +126 -0
- package/skills/lit-typographic-motion/engine/timing.mjs +289 -0
- package/skills/lit-typographic-motion/engine/treatment.mjs +254 -0
- package/skills/lit-typographic-motion/fonts/Archivo-100-400.ttf +0 -0
- package/skills/lit-typographic-motion/fonts/Archivo-100-700.ttf +0 -0
- package/skills/lit-typographic-motion/fonts/Archivo-100-900.ttf +0 -0
- package/skills/lit-typographic-motion/fonts/Archivo-125-400.ttf +0 -0
- package/skills/lit-typographic-motion/fonts/Archivo-125-700.ttf +0 -0
- package/skills/lit-typographic-motion/fonts/Archivo-125-900.ttf +0 -0
- package/skills/lit-typographic-motion/fonts/Archivo-75-400.ttf +0 -0
- package/skills/lit-typographic-motion/fonts/Archivo-75-700.ttf +0 -0
- package/skills/lit-typographic-motion/fonts/Archivo-75-900.ttf +0 -0
- package/skills/lit-typographic-motion/fonts/Archivo-OFL.txt +93 -0
- package/skills/lit-typographic-motion/fonts/Silkscreen-Bold.ttf +0 -0
- package/skills/lit-typographic-motion/fonts/Silkscreen-OFL.txt +93 -0
- package/skills/lit-typographic-motion/fonts/Silkscreen-Regular.ttf +0 -0
- package/skills/lit-typographic-motion/fonts/VT323-OFL.txt +93 -0
- package/skills/lit-typographic-motion/fonts/VT323-Regular.ttf +0 -0
- package/skills/lit-typographic-motion/fonts/pins.json +44 -0
- package/skills/lit-typographic-motion/fonts/stroke/CREDITS +52 -0
- package/skills/lit-typographic-motion/fonts/stroke/EMSAllure.svg +216 -0
- package/skills/lit-typographic-motion/fonts/stroke/EMSFelix.svg +216 -0
- package/skills/lit-typographic-motion/fonts/stroke/EMSOsmotron.svg +216 -0
- package/skills/lit-typographic-motion/fonts/stroke/EMSReadability.svg +216 -0
- package/skills/lit-typographic-motion/fonts/stroke/EMSTech.svg +217 -0
- package/skills/lit-typographic-motion/fonts/stroke/OFL.txt +85 -0
- package/skills/lit-typographic-motion/gate.mjs +593 -0
- package/skills/lit-typographic-motion/package-lock.json +78 -0
- package/skills/lit-typographic-motion/package.json +11 -0
- package/skills/lit-typographic-motion/probe.mjs +74 -0
- package/skills/lit-typographic-motion/references/authoring.md +110 -0
- package/skills/lit-typographic-motion/references/craft-loop.md +110 -0
- package/skills/lit-typographic-motion/references/runtime.md +88 -0
- package/skills/lit-typographic-motion/references/stage.md +164 -0
- package/skills/lit-typographic-motion/references/style-bibles.md +88 -0
- package/skills/lit-typographic-motion/references/treatment.md +143 -0
- package/skills/lit-typographic-motion/references/type-craft.md +55 -0
- package/skills/lit-typographic-motion/render.mjs +654 -0
- package/skills/lit-typographic-motion/requirements-audio.in +1 -0
- package/skills/lit-typographic-motion/requirements-audio.txt +790 -0
- package/skills/lit-typographic-motion/runtime.mjs +259 -0
- package/skills/lit-typographic-motion/word-timing-models.json +25 -0
- package/skills/litresearch/SKILL.md +41 -12
- package/skills/managed-skill-manifest.json +2367 -63
- package/skills/skill-rename-aliases.json +6 -1
- package/tools/check-pack-payload.mjs +22 -1
- package/tools/check-payload-substance.mjs +2 -2
- package/tools/payload-reference-exemptions.json +4 -0
- package/tools/payload-substance-allowlist.json +2 -3
- package/tools/payload-substance-parity.json +7 -7
- package/tools/version-manifests.json +4 -4
- package/skills/lit-korean/SKILL.md +0 -231
|
@@ -0,0 +1,593 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// lit-typographic-motion numeric QA gate (spec Section C, MO-D-02..04, and every measurable HARD or
|
|
3
|
+
// CAP row of the rule index). It reads a finished output directory: manifest.json, render.jsonl (per
|
|
4
|
+
// frame pass lines, frame lines and the preview audit records), the .run/ records (brief copy,
|
|
5
|
+
// pre-flight, contrast samples, determinism, perf) and the exports. It writes gate-report.txt, then
|
|
6
|
+
// promotes or withholds the exports (MO-C-16).
|
|
7
|
+
import { spawnSync } from "node:child_process";
|
|
8
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
import { EXIT, FRAME, chromeFlagRungs, flashEventThreshold, isSoftwareRenderer, limits, postOverrideTable, provisionalRules, presets } from "./engine/constants.mjs";
|
|
12
|
+
import { countFlashes } from "./engine/flash.mjs";
|
|
13
|
+
import { bigEnough, boxContrast, hueClusters, warpMask } from "./engine/contrast.mjs";
|
|
14
|
+
import { decodePng } from "./engine/png.mjs";
|
|
15
|
+
import { eojeols, passSeed, readingFloor, selectPreset, unitCounts } from "./engine/text.mjs";
|
|
16
|
+
import { pinnedSampleTimes } from "./engine/timing.mjs";
|
|
17
|
+
import { checkStageRun, formatStageReport } from "./engine/stage/gate.mjs";
|
|
18
|
+
import { soundChecks } from "./engine/sound.mjs";
|
|
19
|
+
import { doneState, formatDone } from "./engine/look.mjs";
|
|
20
|
+
|
|
21
|
+
export const exportNames = ["film.mp4", "preview.webp", "preview.gif", "poster.png", "reduced-motion.png"];
|
|
22
|
+
const isShot = (entry) => entry.kind === "line" || entry.kind === "scene";
|
|
23
|
+
const pass = (detail = "") => ({ pass: true, detail });
|
|
24
|
+
const fail = (detail) => ({ pass: false, detail });
|
|
25
|
+
const eps = 1e-6;
|
|
26
|
+
|
|
27
|
+
export function readRun(dir) {
|
|
28
|
+
const root = path.resolve(dir);
|
|
29
|
+
const manifest = JSON.parse(readFileSync(path.join(root, "manifest.json"), "utf8"));
|
|
30
|
+
const lines = existsSync(path.join(root, "render.jsonl")) ? readFileSync(path.join(root, "render.jsonl"), "utf8").split("\n").filter(Boolean).map((line) => JSON.parse(line)) : [];
|
|
31
|
+
const json = (name) => (existsSync(path.join(root, ".run", name)) ? JSON.parse(readFileSync(path.join(root, ".run", name), "utf8")) : null);
|
|
32
|
+
return {
|
|
33
|
+
root, manifest,
|
|
34
|
+
passLines: lines.filter((line) => line.stream === undefined && line.pass !== null && line.pass !== undefined),
|
|
35
|
+
frameLines: lines.filter((line) => line.stream === undefined && line.pass === null),
|
|
36
|
+
previewLines: lines.filter((line) => line.stream === "preview"),
|
|
37
|
+
brief: json("brief.json"), preflight: json("preflight.json"), samples: json("samples.json"),
|
|
38
|
+
determinism: json("determinism.json"), perf: json("perf.json"), still: json("still.json")
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ---- timeline rules, shared with the renderer's pre-flight (MO-C-07/08, MO-A-11..16, MO-A-41a) ----
|
|
43
|
+
function gridBeats(manifest) {
|
|
44
|
+
if (Array.isArray(manifest.beatGrid) && manifest.beatGrid.length >= 2) {
|
|
45
|
+
const intervals = manifest.beatGrid.slice(1).map((t, i) => t - manifest.beatGrid[i]).sort((a, b) => a - b);
|
|
46
|
+
return { beat: intervals[Math.floor(intervals.length / 2)], onGrid: (t) => t < eps || manifest.beatGrid.some((b) => Math.abs(b - t) <= 1 / manifest.fps + eps) || t > manifest.beatGrid.at(-1) };
|
|
47
|
+
}
|
|
48
|
+
const beat = 60 / manifest.bpm;
|
|
49
|
+
return { beat, onGrid: (t) => Math.abs(t / beat - Math.round(t / beat)) * beat <= 1 / manifest.fps + eps };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function timelineChecks(manifest) {
|
|
53
|
+
const checks = {};
|
|
54
|
+
const timeline = manifest.timeline ?? [];
|
|
55
|
+
const fps = manifest.fps;
|
|
56
|
+
const required = ["id", "sceneId", "shotIndex", "start", "end", "holdSec", "kind", "text", "script", "beatSec"];
|
|
57
|
+
const malformed = timeline.find((entry) => required.some((key) => entry[key] === undefined) || Math.abs(entry.holdSec - (entry.end - entry.start)) > 1e-4 || !["line", "word", "reveal", "scene"].includes(entry.kind));
|
|
58
|
+
checks["MO-A-41a"] = timeline.length && !malformed ? pass(`${timeline.length} entries`) : fail(malformed ? `malformed entry ${malformed.id ?? "?"}` : "empty timeline");
|
|
59
|
+
let tightest = null;
|
|
60
|
+
const under = [];
|
|
61
|
+
const cps = [];
|
|
62
|
+
for (const unit of timeline) {
|
|
63
|
+
const floor = readingFloor(unit.text, unit.kind);
|
|
64
|
+
const slack = unit.holdSec - floor;
|
|
65
|
+
if (!tightest || slack < tightest.slack) tightest = { unit, floor, slack };
|
|
66
|
+
if (unit.holdSec < floor - 1 / fps) under.push(`"${unit.text}" (${unit.kind}) ${unit.holdSec.toFixed(2)}s < ${floor.toFixed(2)}s`);
|
|
67
|
+
if ((unit.kind === "line" || unit.kind === "scene") && unit.holdSec > 0 && unitCounts(unit.text).C / unit.holdSec > limits.latinCharsPerSecond + eps) cps.push(`"${unit.text}" ${(unitCounts(unit.text).C / unit.holdSec).toFixed(1)} cps`);
|
|
68
|
+
}
|
|
69
|
+
const detail = tightest ? `tightest unit "${tightest.unit.text.replace(/\n/gu, " / ")}" (${tightest.unit.kind}) hold ${tightest.unit.holdSec.toFixed(2)}s vs floor ${tightest.floor.toFixed(2)}s` : "no timeline";
|
|
70
|
+
checks["MO-C-07/08"] = under.length || cps.length ? fail(`${[...under, ...cps].slice(0, 3).join("; ")} — ${detail}`) : pass(detail);
|
|
71
|
+
const { beat, onGrid } = gridBeats(manifest);
|
|
72
|
+
const offBeat = timeline.filter((entry) => Math.abs(entry.start - entry.beatSec) > 1 / fps + eps || !onGrid(entry.beatSec));
|
|
73
|
+
checks["MO-A-15"] = offBeat.length ? fail(`${offBeat[0].id} starts ${(Math.abs(offBeat[0].start - offBeat[0].beatSec) * 1000).toFixed(1)} ms from its beat`) : pass(`every start within 1 frame of a grid beat (beat ${beat.toFixed(3)}s)`);
|
|
74
|
+
const short = timeline.filter(isShot).filter((shot) => shot.holdSec < limits.minSceneBeats * beat - 1 / fps);
|
|
75
|
+
checks["MO-A-16"] = short.length ? fail(`${short[0].id} holds ${short[0].holdSec.toFixed(2)}s < ${limits.minSceneBeats} beats`) : pass(`every shot >= ${limits.minSceneBeats} beats`);
|
|
76
|
+
const split = [];
|
|
77
|
+
for (const shot of timeline.filter(isShot)) {
|
|
78
|
+
if (shot.kind !== "line") continue;
|
|
79
|
+
const tokens = eojeols(shot.text);
|
|
80
|
+
const reveals = timeline.filter((entry) => entry.kind === "reveal" && entry.sceneId === shot.sceneId && entry.shotIndex === shot.shotIndex);
|
|
81
|
+
let cursor = 0;
|
|
82
|
+
for (const reveal of reveals) {
|
|
83
|
+
const parts = eojeols(reveal.text);
|
|
84
|
+
const expected = tokens.slice(cursor, cursor + parts.length);
|
|
85
|
+
if (parts.join(" ") !== expected.join(" ")) split.push(`${reveal.id} "${reveal.text}"`);
|
|
86
|
+
cursor += parts.length;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
checks["MO-A-13"] = split.length ? fail(`reveal step splits a 어절: ${split[0]}`) : pass("reveal steps and breaks fall on 어절 boundaries");
|
|
90
|
+
return checks;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ---- helpers over the log ----
|
|
94
|
+
function eventsFromLog(frameLines) {
|
|
95
|
+
const events = [];
|
|
96
|
+
let flashHigh = false, invert = false;
|
|
97
|
+
for (const line of frameLines) {
|
|
98
|
+
for (const event of line.events ?? []) events.push({ ...event, frame: line.frame });
|
|
99
|
+
const p = line.post ?? {};
|
|
100
|
+
if ((p.flash ?? 0) > flashEventThreshold && !flashHigh) events.push({ kind: "flash", frame: line.frame, sceneId: line.sceneId, shotIndex: line.shotIndex });
|
|
101
|
+
flashHigh = (p.flash ?? 0) > flashEventThreshold;
|
|
102
|
+
if (Boolean(p.invert) !== invert && line.frame > 0) events.push({ kind: "invert", frame: line.frame, sceneId: line.sceneId, shotIndex: line.shotIndex });
|
|
103
|
+
invert = Boolean(p.invert);
|
|
104
|
+
}
|
|
105
|
+
return events;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function sampleTimesOk(line, manifest) {
|
|
109
|
+
const expected = pinnedSampleTimes(line.frame, manifest.samples, manifest.shutter, manifest.fps);
|
|
110
|
+
return Array.isArray(line.sampleTimes) && line.sampleTimes.length === expected.length && line.sampleTimes.every((t, i) => Math.abs(t - expected[i]) < 1e-9);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function probeVideo(file) {
|
|
114
|
+
const probe = spawnSync("ffprobe", ["-v", "error", "-select_streams", "v:0", "-show_entries",
|
|
115
|
+
"stream=codec_name,width,height,r_frame_rate,pix_fmt,color_range,color_space,color_transfer,color_primaries:format=duration", "-of", "json", file], { encoding: "utf8" });
|
|
116
|
+
if (probe.status !== 0) return { error: (probe.stderr || probe.error?.message || "ffprobe failed").trim() };
|
|
117
|
+
const json = JSON.parse(probe.stdout);
|
|
118
|
+
return { ...json.streams?.[0], duration: Number(json.format?.duration) };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function locateExport(root, name) {
|
|
122
|
+
for (const candidate of [path.join(root, name), path.join(root, ".run", name), path.join(root, "withheld", name)]) if (existsSync(candidate)) return candidate;
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ---- the full gate ----
|
|
127
|
+
export function checkRun(dir, options = {}) {
|
|
128
|
+
const run = readRun(dir);
|
|
129
|
+
const { manifest, passLines, frameLines, previewLines } = run;
|
|
130
|
+
const fps = manifest.fps;
|
|
131
|
+
const frameCount = Math.round(manifest.durationSec * fps);
|
|
132
|
+
const timeline = manifest.timeline ?? [];
|
|
133
|
+
const shots = timeline.filter(isShot);
|
|
134
|
+
const look = presets[manifest.presetId];
|
|
135
|
+
const checks = {};
|
|
136
|
+
const warnings = [...(manifest.warnings ?? [])];
|
|
137
|
+
const byFramePass = new Map(passLines.map((line) => [`${line.frame}:${line.pass}`, line]));
|
|
138
|
+
const frameByIndex = new Map(frameLines.map((line) => [line.frame, line]));
|
|
139
|
+
const shotOfFrame = (frame) => shots.find((shot) => frame >= Math.round(shot.start * fps) && frame < Math.round(shot.end * fps)) ?? shots.at(-1);
|
|
140
|
+
|
|
141
|
+
// MO-C-01 / MO-SH-00 / 00a / 00b: every range has a log line on every frame, and the union of
|
|
142
|
+
// ranges that actually drew covers every output frame.
|
|
143
|
+
const covered = new Uint8Array(frameCount);
|
|
144
|
+
let continuity = null;
|
|
145
|
+
for (const range of manifest.passRanges ?? []) {
|
|
146
|
+
for (let frame = range.frameStart; frame <= range.frameEnd; frame++) {
|
|
147
|
+
const line = byFramePass.get(`${frame}:${range.pass}`);
|
|
148
|
+
if (!line) { continuity ??= `${range.pass}@${range.sceneId}#${range.shotIndex} has no log line at frame ${frame}`; continue; }
|
|
149
|
+
if (line.draws >= 1 && frame < frameCount) covered[frame] = 1;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const gap = covered.findIndex((value) => !value);
|
|
153
|
+
const missingFrameLine = Array.from({ length: frameCount }, (_, i) => i).find((i) => !frameByIndex.has(i));
|
|
154
|
+
checks["MO-C-01"] = !(manifest.passRanges ?? []).length ? fail("manifest lists no GLSL pass")
|
|
155
|
+
: continuity ? fail(continuity)
|
|
156
|
+
: gap >= 0 ? fail(`frame ${gap} has no look-library pass with draws >= 1`)
|
|
157
|
+
: missingFrameLine !== undefined ? fail(`frame ${missingFrameLine} has no frame line`)
|
|
158
|
+
: pass(`${frameCount}/${frameCount} frames covered by a pass that drew`);
|
|
159
|
+
|
|
160
|
+
const seedErrors = [];
|
|
161
|
+
for (const range of manifest.passRanges ?? []) {
|
|
162
|
+
const expected = range.pass === "swiss-grid" ? null : passSeed(manifest.seed, range.sceneId, range.shotIndex, range.pass);
|
|
163
|
+
if (range.seed !== expected) { seedErrors.push(`${range.pass}@${range.sceneId}#${range.shotIndex} seed ${range.seed} != ${expected}`); continue; }
|
|
164
|
+
if (expected === null) continue;
|
|
165
|
+
for (let frame = range.frameStart; frame <= range.frameEnd; frame++) {
|
|
166
|
+
const logged = byFramePass.get(`${frame}:${range.pass}`)?.uniforms?.u_seed;
|
|
167
|
+
if (logged !== undefined && logged !== expected) { seedErrors.push(`${range.pass} frame ${frame} u_seed ${logged} != ${expected}`); break; }
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
checks["MO-SH-01"] = seedErrors.length ? fail(seedErrors[0]) : pass("every pass seed = fnv1a32(runSeed:sceneId:shotIndex:pass)");
|
|
171
|
+
|
|
172
|
+
// MO-C-02 / MO-A-04 / MO-SH-09 / MO-A-51
|
|
173
|
+
const software = isSoftwareRenderer(manifest.renderer);
|
|
174
|
+
const labelled = (manifest.warnings ?? []).some((w) => /software-rendered, --samples lowered/u.test(w));
|
|
175
|
+
checks["MO-C-02"] = !manifest.renderer || /NO_WEBGL2/u.test(manifest.renderer) ? fail("webgl2: false")
|
|
176
|
+
: manifest.softwareRenderer !== software ? fail(`softwareRenderer flag ${manifest.softwareRenderer} disagrees with renderer "${manifest.renderer}"`)
|
|
177
|
+
: software && !labelled ? fail(`software renderer "${manifest.renderer}" is not labelled`)
|
|
178
|
+
: pass(`${software ? "software (labelled)" : "hardware"} — ${manifest.renderer}`);
|
|
179
|
+
checks["MO-A-04"] = software && manifest.samples !== 1 ? fail(`software GL with --samples ${manifest.samples}`) : pass(software ? "software GL, samples 1" : "hardware GL");
|
|
180
|
+
const downgradeErrors = [];
|
|
181
|
+
for (const range of manifest.passRanges ?? []) {
|
|
182
|
+
const affected = range.pass === "tidal-gradient" || range.pass === "crt";
|
|
183
|
+
if (software && affected && !range.downgraded) downgradeErrors.push(`${range.pass}@${range.sceneId} not marked downgraded`);
|
|
184
|
+
if (software && range.pass === "tidal-gradient" && range.params.octaves !== Math.max(3, Math.floor(presets.tidal.params["tidal-gradient"].octaves / 2))) downgradeErrors.push(`tidal-gradient octaves ${range.params.octaves}`);
|
|
185
|
+
if (software && range.pass === "crt" && range.params.persistenceEnabled) downgradeErrors.push("crt persistence left on under software GL");
|
|
186
|
+
if (!software && range.downgraded) downgradeErrors.push(`${range.pass}@${range.sceneId} downgraded on a hardware renderer`);
|
|
187
|
+
}
|
|
188
|
+
checks["MO-SH-09"] = downgradeErrors.length ? fail(downgradeErrors[0]) : pass(software ? "downgrade formula applied" : "no downgrade needed");
|
|
189
|
+
const rungs = chromeFlagRungs().map((rung) => rung.flags.join(" "));
|
|
190
|
+
checks["MO-A-51"] = rungs.includes((manifest.chromeFlags ?? []).join(" ")) ? pass(manifest.chromeFlags.join(" ")) : fail(`chromeFlags is not a ladder rung: ${(manifest.chromeFlags ?? []).join(" ")}`);
|
|
191
|
+
|
|
192
|
+
// MO-C-03 / MO-SH-04 / MO-SH-04a
|
|
193
|
+
const master = countFlashes(frameLines.sort((a, b) => a.frame - b.frame).map((line) => line.transitions ?? {}), fps, false);
|
|
194
|
+
const previewFps = manifest.preview?.fps ?? fps;
|
|
195
|
+
const preview = previewLines.length ? countFlashes(previewLines.sort((a, b) => a.previewFrame - b.previewFrame).map((line) => line.transitions ?? {}), previewFps, true) : null;
|
|
196
|
+
const general = Math.max(master.general, preview?.general ?? 0), red = Math.max(master.red, preview?.red ?? 0);
|
|
197
|
+
const worstSource = (preview && (preview.general > master.general || preview.red > master.red)) ? { name: "preview", r: preview, fps: previewFps } : { name: "master", r: master, fps };
|
|
198
|
+
const worstStart = worstSource.r.worstWindow.general.flashes >= worstSource.r.worstWindow.red.flashes ? worstSource.r.worstWindow.general : worstSource.r.worstWindow.red;
|
|
199
|
+
const flashDetail = `worst window ${general} general / ${red} red (limit ${limits.flashLimitGeneral} / ${limits.flashLimitRed}); ${worstSource.name} window at frame ${worstStart.start} (${(worstStart.start / worstSource.fps).toFixed(2)}s) transitions ${worstStart.transitions.join(" ") || "none"}`;
|
|
200
|
+
checks["MO-C-03"] = !previewLines.length && options.requirePreview !== false ? fail(`preview frames were not audited; ${flashDetail}`)
|
|
201
|
+
: general <= limits.flashLimitGeneral && red <= limits.flashLimitRed ? pass(flashDetail) : fail(flashDetail);
|
|
202
|
+
const stepFrame = frameLines.find((line) => line.fullStep);
|
|
203
|
+
const stepPreview = previewLines.find((line) => line.fullStep);
|
|
204
|
+
checks["MO-SH-04a"] = stepFrame || stepPreview ? fail(`full-frame luminance step at ${stepFrame ? `frame ${stepFrame.frame}` : `preview frame ${stepPreview.previewFrame}`}`) : pass("no full-frame luminance step in any frame pair");
|
|
205
|
+
|
|
206
|
+
// MO-SH-03 / MO-A-58
|
|
207
|
+
const events = eventsFromLog(frameLines);
|
|
208
|
+
let crowded = null;
|
|
209
|
+
for (const shot of shots) {
|
|
210
|
+
const times = events.filter((event) => event.sceneId === shot.sceneId && event.shotIndex === shot.shotIndex).map((event) => event.frame / fps).sort((a, b) => a - b);
|
|
211
|
+
for (let i = 0; i + limits.eventsPerSecondPerShot < times.length; i++) {
|
|
212
|
+
if (times[i + limits.eventsPerSecondPerShot] - times[i] < 1 - eps) { crowded = `${shot.id}: ${limits.eventsPerSecondPerShot + 1} events within ${(times[i + limits.eventsPerSecondPerShot] - times[i]).toFixed(2)}s from ${times[i].toFixed(2)}s`; break; }
|
|
213
|
+
}
|
|
214
|
+
if (crowded) break;
|
|
215
|
+
}
|
|
216
|
+
checks["MO-SH-03"] = crowded ? fail(crowded) : pass(`${events.length} events, at most ${limits.eventsPerSecondPerShot} in any 1 s window per shot`);
|
|
217
|
+
const overrideErrors = [];
|
|
218
|
+
let lastInvertChange = null;
|
|
219
|
+
const { beat } = gridBeats(manifest);
|
|
220
|
+
for (const line of frameLines) {
|
|
221
|
+
for (const [field, rule] of Object.entries(postOverrideTable)) {
|
|
222
|
+
const value = line.post?.[field];
|
|
223
|
+
if (value === undefined) continue;
|
|
224
|
+
if (rule.kind === "number" && (typeof value !== "number" || value < rule.min || (rule.exclusiveMin && value <= rule.min) || value > rule.max)) overrideErrors.push(`frame ${line.frame} ${field}=${value}`);
|
|
225
|
+
if (rule.kind === "boolean" && typeof value !== "boolean") overrideErrors.push(`frame ${line.frame} invert must be boolean`);
|
|
226
|
+
if (rule.kind === "pair" && !(Array.isArray(value) && value.length === 2 && value.every(Number.isFinite))) overrideErrors.push(`frame ${line.frame} shake must be a pair`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
for (const event of events.filter((e) => e.kind === "invert")) {
|
|
230
|
+
const shot = shotOfFrame(event.frame);
|
|
231
|
+
if (event.frame !== Math.round(shot.start * fps)) overrideErrors.push(`invert changes at frame ${event.frame}, not on a cut`);
|
|
232
|
+
if (lastInvertChange !== null && (event.frame - lastInvertChange) / fps < limits.minSceneBeats * beat - 1 / fps) overrideErrors.push(`invert held ${((event.frame - lastInvertChange) / fps).toFixed(2)}s < ${limits.minSceneBeats} beats`);
|
|
233
|
+
lastInvertChange = event.frame;
|
|
234
|
+
}
|
|
235
|
+
checks["MO-A-58"] = overrideErrors.length ? fail(overrideErrors[0]) : pass("every override inside its range; invert only on cuts");
|
|
236
|
+
|
|
237
|
+
// MO-SH-05..08, MO-SH-10, MO-A-05
|
|
238
|
+
const passErrors = [];
|
|
239
|
+
for (const range of manifest.passRanges ?? []) {
|
|
240
|
+
const p = range.params ?? {};
|
|
241
|
+
const shot = shots.find((s) => s.sceneId === range.sceneId && s.shotIndex === range.shotIndex);
|
|
242
|
+
if (range.pass === "glitch") {
|
|
243
|
+
if (!(p.hitRatePerSecRealized <= limits.glitchHitRateCap)) passErrors.push(["MO-SH-05", `glitch hit rate ${p.hitRatePerSecRealized}/s > ${limits.glitchHitRateCap}`]);
|
|
244
|
+
if (!(p.areaCapPct <= limits.glitchAreaCapPct) || !(p.maxAreaPctRealized <= limits.glitchAreaCapPct)) passErrors.push(["MO-SH-05", `glitch area ${p.maxAreaPctRealized ?? p.areaCapPct}% > ${limits.glitchAreaCapPct}%`]);
|
|
245
|
+
const hits = events.filter((e) => e.kind === "glitch" && e.sceneId === range.sceneId && e.shotIndex === range.shotIndex).map((e) => e.frame / fps);
|
|
246
|
+
for (let i = 0; i + 2 < hits.length; i++) if (hits[i + 2] - hits[i] < 1 - eps) passErrors.push(["MO-SH-05", `3 glitch hits within 1 s at ${hits[i].toFixed(2)}s`]);
|
|
247
|
+
}
|
|
248
|
+
if (range.pass === "tidal-gradient") {
|
|
249
|
+
if (!(p.surgeAttackSec >= limits.surgeEdgeFloorSec) || !(p.surgeDecaySec >= limits.surgeEdgeFloorSec)) passErrors.push(["MO-SH-06", `surge attack ${p.surgeAttackSec}s / decay ${p.surgeDecaySec}s under ${limits.surgeEdgeFloorSec}s`]);
|
|
250
|
+
if (!(p.surgeCapPerSec <= limits.surgeRateCap)) passErrors.push(["MO-SH-06", `surge cap ${p.surgeCapPerSec}/s > ${limits.surgeRateCap}`]);
|
|
251
|
+
const surges = events.filter((e) => e.kind === "surge" && e.sceneId === range.sceneId && e.shotIndex === range.shotIndex).map((e) => e.frame / fps);
|
|
252
|
+
for (let i = 0; i + 2 < surges.length; i++) if (surges[i + 2] - surges[i] < 1 - eps) passErrors.push(["MO-SH-06", `3 surges within 1 s at ${surges[i].toFixed(2)}s`]);
|
|
253
|
+
for (const e of events.filter((ev) => ev.kind === "surge" && ev.sceneId === range.sceneId)) if (!(e.attackSec >= limits.surgeEdgeFloorSec && e.decaySec >= limits.surgeEdgeFloorSec)) passErrors.push(["MO-SH-06", `surge at frame ${e.frame} has attack ${e.attackSec}s / decay ${e.decaySec}s`]);
|
|
254
|
+
}
|
|
255
|
+
if (range.pass === "crt") {
|
|
256
|
+
let peak = p.flickerAmpRealized ?? 0;
|
|
257
|
+
for (let frame = range.frameStart; frame <= range.frameEnd; frame++) peak = Math.max(peak, byFramePass.get(`${frame}:crt`)?.uniforms?.u_flickerAmp ?? 0);
|
|
258
|
+
if (peak > limits.crtFlickerCap + eps) passErrors.push(["MO-SH-07", `CRT flicker ${peak} > ${limits.crtFlickerCap}`]);
|
|
259
|
+
if (p.persistenceEnabled && !shot?.stateful) passErrors.push(["MO-A-05", `persistence on non-stateful scene ${range.sceneId}#${range.shotIndex}`]);
|
|
260
|
+
}
|
|
261
|
+
if (range.pass === "dither") {
|
|
262
|
+
for (let frame = range.frameStart; frame <= range.frameEnd; frame++) {
|
|
263
|
+
const logged = byFramePass.get(`${frame}:dither`)?.uniforms?.u_seed;
|
|
264
|
+
if (logged !== undefined && logged !== range.seed) { passErrors.push(["MO-SH-08", `dither reseeded inside ${range.sceneId}#${range.shotIndex} at frame ${frame}`]); break; }
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (range.pass === "swiss-grid") {
|
|
268
|
+
if (p.showGuides !== false) passErrors.push(["MO-SH-10", `showGuides ${p.showGuides} in ${range.sceneId}`]);
|
|
269
|
+
for (let frame = range.frameStart; frame <= range.frameEnd; frame++) if (byFramePass.get(`${frame}:swiss-grid`)?.uniforms?.u_showGuides === true) { passErrors.push(["MO-SH-10", `showGuides true at frame ${frame}`]); break; }
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
for (const id of ["MO-SH-05", "MO-SH-06", "MO-SH-07", "MO-SH-08", "MO-SH-10", "MO-A-05"]) {
|
|
273
|
+
const found = passErrors.find(([rule]) => rule === id);
|
|
274
|
+
checks[id] = found ? fail(found[1]) : pass();
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Type geometry rules (MO-C-04/05/25/26/27, MO-FT-04, MO-A-33, MO-A-35, MO-A-13 line breaks)
|
|
278
|
+
const boxes = frameLines.flatMap((line) => (line.textBoxes ?? []).map((box) => ({ ...box, frame: line.frame })));
|
|
279
|
+
const graphics = frameLines.flatMap((line) => (line.graphics ?? []).map((g) => ({ ...g, frame: line.frame })));
|
|
280
|
+
const titleViolations = boxes.filter((b) => b.bbox && (b.bbox[0] < limits.titleSafeX - eps || b.bbox[1] < limits.titleSafeY - eps || b.bbox[2] > FRAME.width - limits.titleSafeX + eps || b.bbox[3] > FRAME.height - limits.titleSafeY + eps));
|
|
281
|
+
const overrun = (b) => Math.max(limits.titleSafeX - b.bbox[0], limits.titleSafeY - b.bbox[1], b.bbox[2] - (FRAME.width - limits.titleSafeX), b.bbox[3] - (FRAME.height - limits.titleSafeY)).toFixed(1);
|
|
282
|
+
checks["MO-C-04"] = titleViolations.length ? fail(`violations: ${[...new Map(titleViolations.map((b) => [b.elementId, b])).values()].slice(0, 3).map((b) => `${b.text.replace(/\n/gu, " / ")}@${b.frame}:${overrun(b)} px`).join(", ")}`) : pass("violations: none");
|
|
283
|
+
const actionViolations = graphics.filter((g) => g.bbox && (g.bbox[0] < limits.actionSafeX - eps || g.bbox[1] < limits.actionSafeY - eps || g.bbox[2] > FRAME.width - limits.actionSafeX + eps || g.bbox[3] > FRAME.height - limits.actionSafeY + eps));
|
|
284
|
+
checks["MO-C-05"] = actionViolations.length ? fail(`${actionViolations[0].elementId} at frame ${actionViolations[0].frame} crosses the 95% action-safe border`) : pass(`${new Set(graphics.map((g) => g.elementId)).size} graphic elements inside action-safe`);
|
|
285
|
+
const trackingErrors = [], hangulErrors = [], outlineErrors = [], leadingErrors = [], measureErrors = [], measureNotes = [], breakErrors = [];
|
|
286
|
+
const pairFiles = ["LitOpenCodeSans-Regular.otf", "LitOpenCodeSans-Bold.otf", "Galmuri9.ttf", "Pretendard-Regular.otf"];
|
|
287
|
+
for (const box of boxes) {
|
|
288
|
+
for (const run of box.runs ?? []) {
|
|
289
|
+
if (run.script === "latin" && box.voice === "display" && run.trackingEm < limits.displayTrackingFloorEm - eps) trackingErrors.push(`"${box.text}" display tracking ${run.trackingEm}em @${box.frame}`);
|
|
290
|
+
if (run.script === "latin" && box.voice !== "display" && run.trackingEm < -eps) trackingErrors.push(`"${box.text}" ${box.voice} voice tracking ${run.trackingEm}em @${box.frame}`);
|
|
291
|
+
if (run.script === "hangul") {
|
|
292
|
+
if (Math.abs(run.trackingEm) > eps || run.widthPct !== 100) hangulErrors.push(["MO-FT-04", `Hangul run "${run.text}" tracking ${run.trackingEm}em width ${run.widthPct}% @${box.frame}`]);
|
|
293
|
+
if (run.scaleX !== 1 || ![400, 700].includes(run.weight) || !pairFiles.includes(path.basename(String(run.font)))) hangulErrors.push(["MO-A-33", `Hangul run "${run.text}" synthesized (scaleX ${run.scaleX}, weight ${run.weight}, font ${run.font}) @${box.frame}`]);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (box.outline || box.halo) outlineErrors.push(`"${box.text}" is ${box.outline ? "outlined" : "haloed"} @${box.frame}`);
|
|
297
|
+
if ((box.lineCount ?? 1) >= 2) {
|
|
298
|
+
const cjk = (box.runs ?? []).some((run) => run.script === "hangul");
|
|
299
|
+
const floor = Math.max(cjk ? limits.lineHeightCjk : limits.lineHeightLatin, box.lineCount >= 3 ? limits.lineHeightThreePlus : 0);
|
|
300
|
+
if (!(box.lineHeight >= floor - eps)) leadingErrors.push(`"${box.elementId}" ${box.lineCount} lines at line-height ${box.lineHeight} < ${floor} @${box.frame}`);
|
|
301
|
+
if (Array.isArray(box.lines) && cjk) {
|
|
302
|
+
const shot = shotOfFrame(box.frame);
|
|
303
|
+
const tokens = new Set(eojeols(shot.text.replace(/\n/gu, " ")));
|
|
304
|
+
const bad = box.lines.flatMap((line) => eojeols(line)).find((token) => !tokens.has(token));
|
|
305
|
+
if (bad) breakErrors.push(`"${bad}" in ${box.elementId} @${box.frame} is not a whole 어절 of the shot text`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (box.role === "paragraph") {
|
|
309
|
+
const cjk = (box.runs ?? []).some((run) => run.script === "hangul");
|
|
310
|
+
if (cjk && (box.measureCh < limits.cjkMeasureMinCh || box.measureCh > limits.cjkMeasureMaxCh)) measureNotes.push(`advisory: Korean paragraph measure ${box.measureCh}ch outside ${limits.cjkMeasureMinCh}-${limits.cjkMeasureMaxCh}ch`);
|
|
311
|
+
if (!cjk && (box.measureCh < limits.measureMinCh || box.measureCh > limits.measureMaxCh)) measureErrors.push(`paragraph "${box.elementId}" measure ${box.measureCh}ch outside ${limits.measureMinCh}-${limits.measureMaxCh}ch @${box.frame}`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
checks["MO-C-25"] = trackingErrors.length ? fail(trackingErrors[0]) : pass(`display >= ${limits.displayTrackingFloorEm}em, machine/body >= 0`);
|
|
315
|
+
for (const id of ["MO-FT-04", "MO-A-33"]) { const found = hangulErrors.find(([rule]) => rule === id); checks[id] = found ? fail(found[1]) : pass(); }
|
|
316
|
+
checks["MO-A-35"] = outlineErrors.length ? fail(outlineErrors[0]) : pass("no outlined or haloed type");
|
|
317
|
+
checks["MO-C-26"] = leadingErrors.length ? fail(leadingErrors[0]) : pass(`${new Set(boxes.filter((b) => (b.lineCount ?? 1) >= 2).map((b) => b.elementId)).size} multi-line blocks at or above their floor`);
|
|
318
|
+
checks["MO-C-27"] = measureErrors.length ? fail(measureErrors[0]) : pass(measureNotes[0] ?? "paragraph cards inside 60-75ch");
|
|
319
|
+
Object.assign(checks, timelineChecks(manifest));
|
|
320
|
+
if (breakErrors.length) checks["MO-A-13"] = fail(breakErrors[0]);
|
|
321
|
+
|
|
322
|
+
// MO-C-06 contrast on the recorded sample frames
|
|
323
|
+
let minContrast = null;
|
|
324
|
+
const contrastErrors = [];
|
|
325
|
+
const crtRange = (manifest.passRanges ?? []).find((range) => range.pass === "crt");
|
|
326
|
+
const crtCurvature = crtRange ? crtRange.params?.curvature ?? presets.terminalcore.params.crt.curvature : 0;
|
|
327
|
+
for (const sample of run.samples?.frames ?? []) {
|
|
328
|
+
const frameFile = path.join(run.root, ".run", "samples", `f${sample.frame}.png`);
|
|
329
|
+
const maskFile = path.join(run.root, ".run", "samples", `f${sample.frame}-mask.png`);
|
|
330
|
+
const line = frameByIndex.get(sample.frame);
|
|
331
|
+
if (!line || !existsSync(frameFile) || !existsSync(maskFile)) { contrastErrors.push(`sample frame ${sample.frame} is missing its PNG or mask`); continue; }
|
|
332
|
+
const image = decodePng(readFileSync(frameFile));
|
|
333
|
+
const maskImage = decodePng(readFileSync(maskFile));
|
|
334
|
+
const mask = warpMask(maskImage.pixels, maskImage.width, maskImage.height, crtCurvature);
|
|
335
|
+
for (const box of line.textBoxes ?? []) {
|
|
336
|
+
const measured = boxContrast(image.pixels, mask, image.width, image.height, box, manifest.scale ?? 1);
|
|
337
|
+
if (!measured) continue;
|
|
338
|
+
if (!minContrast || measured.ratio / measured.floor < minContrast.ratio / minContrast.floor) minContrast = { ...measured, frame: sample.frame, text: box.text };
|
|
339
|
+
if (measured.ratio < measured.floor) contrastErrors.push(`"${box.text.replace(/\n/gu, " / ")}" at frame ${sample.frame}: ${measured.ratio.toFixed(2)}:1 < ${measured.floor}:1`);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
checks["MO-C-06"] = !run.samples ? fail("no contrast sample frames were recorded")
|
|
343
|
+
: contrastErrors.length ? fail(contrastErrors[0])
|
|
344
|
+
: minContrast ? pass(`min ratio ${minContrast.ratio.toFixed(1)}:1 at frame ${minContrast.frame} (floor ${minContrast.floor.toFixed(1)}:1)`) : fail("no measurable text on the sample frames");
|
|
345
|
+
|
|
346
|
+
// MO-C-09 / MO-A-24 / MO-A-25 / MO-A-28
|
|
347
|
+
const det = run.determinism;
|
|
348
|
+
if (!det) checks["MO-C-09"] = fail("no determinism re-render was recorded");
|
|
349
|
+
else {
|
|
350
|
+
const mismatches = det.frames.filter((entry) => entry.master !== entry.rerender);
|
|
351
|
+
const logMismatch = det.frames.find((entry) => frameByIndex.get(entry.frame)?.rgbaSha256 !== entry.master);
|
|
352
|
+
const frames = det.frames.map((entry) => entry.frame).join(",");
|
|
353
|
+
if (logMismatch) checks["MO-C-09"] = fail(`recorded master hash for frame ${logMismatch.frame} does not match render.jsonl`);
|
|
354
|
+
else if (!mismatches.length) checks["MO-C-09"] = pass(`rgbaSha256 match Y (frames re-rendered: ${frames})`);
|
|
355
|
+
else if (det.swiftshader && det.swiftshader.every((entry) => entry.a === entry.b)) {
|
|
356
|
+
checks["MO-C-09"] = pass(`rgbaSha256 match N on hardware, Y under SwiftShader — WARN hardware-nondeterminism (frames re-rendered: ${frames})`);
|
|
357
|
+
warnings.push("hardware-nondeterminism");
|
|
358
|
+
} else checks["MO-C-09"] = fail(`rgbaSha256 match N at frame ${mismatches[0].frame}: ${mismatches[0].master} vs ${mismatches[0].rerender}${det.swiftshader ? " (SwiftShader pair also differs)" : ""}`);
|
|
359
|
+
}
|
|
360
|
+
checks["MO-A-25"] = det && det.seeked && checks["MO-C-09"].pass ? pass(`seeked re-render equals the sequential frame lines${det.statefulFrames?.length ? ` (stateful preroll frames ${det.statefulFrames.join(",")})` : ""}`) : fail(det ? "seeked frames did not match the sequential render" : "no seeked re-render recorded");
|
|
361
|
+
const badSample = frameLines.find((line) => !sampleTimesOk(line, manifest));
|
|
362
|
+
checks["MO-A-28"] = badSample ? fail(`frame ${badSample.frame} sample times do not follow t_n + (shutter/fps)((i+0.5)/N - 0.5)`) : pass(`${manifest.samples} pinned sub-samples, shutter ${manifest.shutter}`);
|
|
363
|
+
|
|
364
|
+
// MO-C-10..13, MO-A-03, MO-A-37..41, MO-C-14
|
|
365
|
+
const film = locateExport(run.root, "film.mp4");
|
|
366
|
+
const video = film ? probeVideo(film) : null;
|
|
367
|
+
const rate = video?.r_frame_rate ? Number(video.r_frame_rate.split("/")[0]) / Number(video.r_frame_rate.split("/")[1] ?? 1) : 0;
|
|
368
|
+
const tags = video && video.pix_fmt === "yuv420p" && video.color_space === "bt709" && video.color_range === "tv";
|
|
369
|
+
const techOk = video && !video.error && video.codec_name === "h264" && video.width >= FRAME.width && video.height >= FRAME.height && rate >= limits.minFps && Math.abs(rate - fps) < 0.01 && manifest.durationSec >= limits.minDurationSec && tags;
|
|
370
|
+
if (manifest.durationSec > limits.warnDurationSec) warnings.push(`duration ${manifest.durationSec}s is over ${limits.warnDurationSec}s`);
|
|
371
|
+
checks["MO-C-10/11/12"] = techOk ? pass(`(ffprobe yuv420p/bt709/tv Y) ${video.width}x${video.height} @ ${rate} fps, ${manifest.durationSec}s; transfer ${video.color_transfer ?? "unset"}, primaries ${video.color_primaries ?? "unset"}`)
|
|
372
|
+
: fail(`(ffprobe yuv420p/bt709/tv ${tags ? "Y" : "N"}) ${video ? JSON.stringify({ codec: video.codec_name, width: video.width, height: video.height, rate, pix: video.pix_fmt, space: video.color_space, range: video.color_range, trc: video.color_transfer, primaries: video.color_primaries, error: video.error }) : "no MP4"}; duration ${manifest.durationSec}s`);
|
|
373
|
+
checks["MO-A-03"] = techOk ? pass("pinned BT.709 encode") : fail("MP4 encode tags do not match the pinned BT.709 encode");
|
|
374
|
+
const previewFile = locateExport(run.root, "preview.webp") ?? locateExport(run.root, "preview.gif");
|
|
375
|
+
const poster = locateExport(run.root, "poster.png");
|
|
376
|
+
const still = locateExport(run.root, "reduced-motion.png");
|
|
377
|
+
const sizes = { mp4: film ? statSync(film).size : 0, preview: previewFile ? statSync(previewFile).size : 0, poster: poster ? statSync(poster).size : 0 };
|
|
378
|
+
if (film && sizes.mp4 > limits.mp4WarnBytesPer10s * (manifest.durationSec / 10)) warnings.push(`MP4 is ${sizes.mp4} B, over 100 MB per 10 s`);
|
|
379
|
+
const sizeOk = sizes.preview > 0 && sizes.preview <= limits.previewMaxBytes && sizes.poster > 0 && sizes.poster <= limits.posterMaxBytes;
|
|
380
|
+
checks["MO-C-13"] = sizeOk ? pass(`mp4 ${sizes.mp4}, ${previewFile ? path.basename(previewFile).split(".").pop() : "webp"} ${sizes.preview} (cap 3 MB), poster ${sizes.poster} (cap 1 MB)`)
|
|
381
|
+
: fail(`mp4 ${sizes.mp4}, preview ${sizes.preview} (cap 3 MB), poster ${sizes.poster} (cap 1 MB)`);
|
|
382
|
+
const missing = [["MP4", film], ["preview", previewFile], ["poster", poster], ["reduced-motion still", still]].filter(([, file]) => !file).map(([name]) => name);
|
|
383
|
+
checks["MO-A-37-41"] = missing.length ? fail(`missing ${missing.join(", ")}`) : pass("MP4, preview, poster, reduced-motion still and manifest present");
|
|
384
|
+
const lastText = [...timeline].filter(isShot).at(-1);
|
|
385
|
+
const referenceFrame = lastText ? Math.round(lastText.end * fps) - 1 : frameCount - 1;
|
|
386
|
+
const referenceInk = frameByIndex.get(referenceFrame)?.inkPixels ?? 0;
|
|
387
|
+
const stillInk = run.still?.inkPixels ?? 0;
|
|
388
|
+
checks["MO-C-14"] = still && referenceInk > 0 && stillInk >= limits.stillInkRatio * referenceInk ? pass(`present Y, ink-coverage check PASS (${stillInk} vs ${referenceInk} at frame ${referenceFrame})`)
|
|
389
|
+
: fail(`present ${still ? "Y" : "N"}, ink-coverage check FAIL (${stillInk} vs ${referenceInk} at frame ${referenceFrame}, need ${limits.stillInkRatio}x)`);
|
|
390
|
+
|
|
391
|
+
// MO-C-29 one signal + one accent cluster
|
|
392
|
+
const observations = [];
|
|
393
|
+
for (const line of frameLines) {
|
|
394
|
+
const shot = shotOfFrame(line.frame);
|
|
395
|
+
for (const box of line.textBoxes ?? []) if (typeof box.fill === "string" && bigEnough(box.bbox)) observations.push({ color: box.fill, frame: line.frame, entryId: shot.id });
|
|
396
|
+
for (const fill of line.fills ?? []) {
|
|
397
|
+
if (!bigEnough(fill.bbox)) continue;
|
|
398
|
+
for (const color of fill.stops ?? [fill.color]) observations.push({ color, frame: line.frame, entryId: shot.id });
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
const clusters = hueClusters(observations).sort((a, b) => b.frames - a.frames);
|
|
402
|
+
const accentHex = look.palette.accent?.toUpperCase();
|
|
403
|
+
const accent = clusters.find((c) => accentHex && c.colors.includes(accentHex)) ?? (clusters.length > 1 ? clusters.at(-1) : null);
|
|
404
|
+
checks["MO-C-29"] = clusters.length > 2 ? fail(`${clusters.length} saturated clusters: ${clusters.map((c) => `${c.hue}° ${c.colors.join("/")}`).join(", ")}`)
|
|
405
|
+
: accent && accent.entries.length > limits.accentMaxEntries ? fail(`accent ${accent.colors.join("/")} appears in ${accent.entries.length} timeline entries: ${accent.entries.join(", ")}`)
|
|
406
|
+
: accent && accent.frames > limits.accentMaxFrameFraction * frameCount ? fail(`accent ${accent.colors.join("/")} on ${accent.frames}/${frameCount} frames (> ${limits.accentMaxFrameFraction * 100}%)`)
|
|
407
|
+
: pass(`${clusters.length} saturated cluster(s)${accent ? `; accent in ${accent.entries.join(", ")} on ${accent.frames} frames` : ""}`);
|
|
408
|
+
|
|
409
|
+
// MO-D-02..04
|
|
410
|
+
const perf = run.perf;
|
|
411
|
+
if (!perf) checks["MO-D-02"] = fail("no perf run recorded");
|
|
412
|
+
else {
|
|
413
|
+
const times = [...perf.frameTimeMs].sort((a, b) => a - b);
|
|
414
|
+
const p95 = times[Math.max(0, Math.ceil(times.length * 0.95) - 1)];
|
|
415
|
+
const ceiling = isSoftwareRenderer(perf.renderer) ? limits.softwareGpuP95Ms : limits.realGpuP95Ms;
|
|
416
|
+
checks["MO-D-02"] = times.length >= limits.perfFrames && p95 <= ceiling ? pass(`p95 ${p95.toFixed(1)} ms over ${times.length} frames (ceiling ${ceiling} ms)`)
|
|
417
|
+
: fail(`p95 ${p95?.toFixed(1)} ms over ${times.length} frames (ceiling ${ceiling} ms); lower --samples or tidal octaves`);
|
|
418
|
+
}
|
|
419
|
+
let emptyError = null;
|
|
420
|
+
for (const shot of shots) {
|
|
421
|
+
const first = Math.round(shot.start * fps), last = Math.round(shot.end * fps) - 1;
|
|
422
|
+
const allowance = Math.round(limits.nearBlackFloorMultiple * limits.minSceneBeats * beat * fps);
|
|
423
|
+
let run0 = 0;
|
|
424
|
+
for (let frame = first; frame <= last; frame++) {
|
|
425
|
+
const line = frameByIndex.get(frame);
|
|
426
|
+
const empty = line && line.inkPixels === 0 && line.p995L < limits.nearBlackLuminance;
|
|
427
|
+
run0 = empty ? run0 + 1 : 0;
|
|
428
|
+
const edge = frame < limits.nearBlackFadeSec * fps || frame >= frameCount - limits.nearBlackFadeSec * fps ? limits.nearBlackFadeSec * fps : 0;
|
|
429
|
+
if (run0 > allowance + edge) { emptyError = `${run0} empty/near-black frames ending at ${frame} in ${shot.id} (allowance ${allowance})`; break; }
|
|
430
|
+
}
|
|
431
|
+
if (emptyError) break;
|
|
432
|
+
}
|
|
433
|
+
checks["MO-D-03"] = emptyError ? fail(emptyError) : pass("no empty or near-black run past 2x the scene's minimum hold");
|
|
434
|
+
const glyphs = run.preflight?.missingGlyphs;
|
|
435
|
+
checks["MO-D-04"] = !run.preflight ? fail("pre-flight glyph coverage was not recorded") : glyphs.length ? fail(`missing glyphs: ${glyphs.slice(0, 5).join(", ")}`) : pass("every codepoint resolves in its assigned font");
|
|
436
|
+
|
|
437
|
+
// MO-B-00 recheck, MO-C-15/16 round bookkeeping
|
|
438
|
+
if (run.brief) {
|
|
439
|
+
const expected = selectPreset(run.brief.sourceText ?? "", run.brief.explicitPreset);
|
|
440
|
+
checks["MO-B-00"] = expected.id === manifest.presetId ? pass(`${manifest.presetId} (${expected.reason})`) : fail(`preset ${manifest.presetId} but the keyword table picks ${expected.id}`);
|
|
441
|
+
} else checks["MO-B-00"] = fail("no brief copy recorded");
|
|
442
|
+
const round = manifest.craftRound;
|
|
443
|
+
checks["MO-C-15/16"] = Number.isInteger(round) && round >= 1 && round <= 3 ? pass(`round ${round} of 3`) : fail(`craft round ${round} outside 1..3`);
|
|
444
|
+
|
|
445
|
+
// Sound rows on the decoded muxed stream, for renders that planned sound through a treatment.
|
|
446
|
+
if (manifest.sound?.mode) {
|
|
447
|
+
for (const [id, row] of Object.entries(soundChecks(film, manifest.durationSec, manifest.sound.mode))) {
|
|
448
|
+
if (row.warn) warnings.push(`${id}: ${row.detail}`);
|
|
449
|
+
else checks[id] = row;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
const failed = Object.entries(checks).filter(([, check]) => !check.pass).map(([id]) => id);
|
|
453
|
+
return { run, manifest, checks, warnings, failed, passed: failed.length === 0, sizes, minContrast, frameCount, flash: { master, preview } };
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const fixedOrder = ["MO-C-25", "MO-C-26", "MO-C-27", "MO-C-29", "MO-D-02", "MO-D-03", "MO-D-04"];
|
|
457
|
+
const indexOrder = ["MO-A-03", "MO-A-04", "MO-A-05", "MO-A-13", "MO-A-15", "MO-A-16", "MO-A-25", "MO-A-28", "MO-A-33", "MO-A-35", "MO-A-37-41", "MO-A-41a", "MO-A-51", "MO-A-58",
|
|
458
|
+
"MO-SH-01", "MO-SH-03", "MO-SH-04a", "MO-SH-05", "MO-SH-06", "MO-SH-07", "MO-SH-08", "MO-SH-09", "MO-SH-10", "MO-B-00", "MO-FT-04", "MO-C-15/16"];
|
|
459
|
+
const labels = {
|
|
460
|
+
"MO-C-01": "GLSL presence: ", "MO-C-02": "WebGL2 tier: ", "MO-C-03": "flash audit: ", "MO-C-04": "title-safe: ",
|
|
461
|
+
"MO-C-05": "action-safe: ", "MO-C-06": "type contrast: ", "MO-C-07/08": "reading time: ", "MO-C-09": "determinism: ",
|
|
462
|
+
"MO-C-10/11/12": "duration/fps/res: ", "MO-C-13": "file sizes: ", "MO-C-14": "reduced-motion still: "
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
export function formatReport(result, context) {
|
|
466
|
+
const { manifest, checks } = result;
|
|
467
|
+
const out = (name) => path.join(context.exportDir, name);
|
|
468
|
+
const previewName = manifest.previewEncoder === "gif" ? "preview.gif" : "preview.webp";
|
|
469
|
+
const lines = [
|
|
470
|
+
"lit-typographic-motion — render report",
|
|
471
|
+
`outputs: ${out("film.mp4")} · ${out(previewName)} (encoder: ${manifest.previewEncoder}) · ${out("poster.png")} · ${out("reduced-motion.png")}`,
|
|
472
|
+
`preset: ${manifest.presetId} (chosen because: ${context.presetReason})`,
|
|
473
|
+
`duration / fps / resolution: ${manifest.durationSec} s @ ${manifest.fps} fps, ${manifest.resolution[0] * (manifest.scale ?? 1)}x${manifest.resolution[1] * (manifest.scale ?? 1)}`,
|
|
474
|
+
`GLSL passes (manifest): ${(manifest.passRanges ?? []).map((r) => `${r.pass}@${r.sceneId}#${r.shotIndex}:${r.frameStart}-${r.frameEnd}`).join(", ")}`,
|
|
475
|
+
`WebGL2: ${manifest.softwareRenderer ? "software" : "hardware"} — ${manifest.renderer} (flags: ${(manifest.chromeFlags ?? []).join(" ")})`,
|
|
476
|
+
"",
|
|
477
|
+
`QA gate: ${result.passed ? "PASS" : "FAIL"}`
|
|
478
|
+
];
|
|
479
|
+
const line = (id) => {
|
|
480
|
+
const check = checks[id];
|
|
481
|
+
const provisional = id === "MO-C-07/08" || provisionalRules.includes(id) ? " [provisional]" : "";
|
|
482
|
+
const status = check.pass ? "PASS" : "FAIL";
|
|
483
|
+
if (id === "MO-C-03") return ` MO-C-03 flash audit: ${check.detail.split(";")[0]} ${status}`;
|
|
484
|
+
if (id === "MO-C-06") return ` MO-C-06 type contrast: ${check.detail} ${status}${provisional}`;
|
|
485
|
+
if (id === "MO-C-07/08") return ` MO-C-07/08 reading time: ${check.detail} ${status}${provisional}`;
|
|
486
|
+
if (id === "MO-C-09") return ` MO-C-09 determinism: ${check.detail} ${status}`;
|
|
487
|
+
if (id === "MO-C-10/11/12") return ` MO-C-10/11/12 duration/fps/res: ${status} ${check.detail}`;
|
|
488
|
+
if (id === "MO-C-13") return ` MO-C-13 file sizes: ${check.detail} ${status}${provisional}`;
|
|
489
|
+
if (id === "MO-C-14") return ` MO-C-14 reduced-motion still: ${check.detail}${provisional}`;
|
|
490
|
+
return ` ${id} ${labels[id] ?? ""}${status}${check.detail ? ` (${check.detail})` : ""}${provisional}`;
|
|
491
|
+
};
|
|
492
|
+
for (const id of ["MO-C-01", "MO-C-02", "MO-C-03", "MO-C-04", "MO-C-05", "MO-C-06", "MO-C-07/08", "MO-C-09", "MO-C-10/11/12", "MO-C-13", "MO-C-14"]) lines.push(line(id));
|
|
493
|
+
for (const id of fixedOrder) lines.push(line(id));
|
|
494
|
+
for (const id of indexOrder) if (checks[id]) lines.push(line(id));
|
|
495
|
+
for (const id of Object.keys(checks).filter((key) => key.startsWith("SOUND"))) lines.push(line(id));
|
|
496
|
+
lines.push("", `craft rounds run: ${manifest.craftRound} / 3 max`, `frames actually viewed this run: ${context.viewed} (confirmed looked, not just rendered)`);
|
|
497
|
+
if (!checks["MO-C-03"].pass) lines.push(`MO-C-03 worst window: ${checks["MO-C-03"].detail}`);
|
|
498
|
+
if (result.warnings.length) lines.push(`warnings: ${result.warnings.join("; ")}`);
|
|
499
|
+
lines.push(`provisional thresholds (no user sign-off recorded): ${provisionalRules.join(", ")}`);
|
|
500
|
+
lines.push(`gate exit: ${context.exitCode}`);
|
|
501
|
+
lines.push(`export state: ${context.exportState}`);
|
|
502
|
+
lines.push(`brief: ${context.briefPath ?? "unknown"}`);
|
|
503
|
+
return lines.join("\n") + "\n";
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// Promote or withhold (MO-C-16): a surviving MO-C-03 FAIL puts every export only in withheld/;
|
|
507
|
+
// otherwise exports move to their deliverable names, including after other FAILs (MO-A-45).
|
|
508
|
+
export function applyExports(root, flashPass) {
|
|
509
|
+
const withheld = path.join(root, "withheld");
|
|
510
|
+
for (const name of exportNames) {
|
|
511
|
+
const sources = [path.join(root, ".run", name), path.join(root, name), path.join(withheld, name)].filter(existsSync);
|
|
512
|
+
const source = sources[0];
|
|
513
|
+
if (!source) continue;
|
|
514
|
+
const target = flashPass ? path.join(root, name) : path.join(withheld, name);
|
|
515
|
+
if (!flashPass) mkdirSync(withheld, { recursive: true });
|
|
516
|
+
if (source !== target) renameSync(source, target);
|
|
517
|
+
for (const extra of sources.slice(1)) if (extra !== target && existsSync(extra)) rmSync(extra);
|
|
518
|
+
}
|
|
519
|
+
if (flashPass && existsSync(withheld) && readdirSync(withheld).length === 0) rmSync(withheld, { recursive: true });
|
|
520
|
+
return flashPass ? "delivered" : "withheld";
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// Frames viewed this round: the look round recorded on this render when there is one; otherwise the
|
|
524
|
+
// --viewed count, which a re-run without --viewed keeps for the same craft round.
|
|
525
|
+
function viewedCount(root, round, viewed) {
|
|
526
|
+
const lookFile = path.join(root, "look.json");
|
|
527
|
+
if (existsSync(lookFile)) {
|
|
528
|
+
const look = JSON.parse(readFileSync(lookFile, "utf8"));
|
|
529
|
+
const recorded = look.rounds?.filter((entry) => entry.round === round).at(-1);
|
|
530
|
+
if (recorded) return Object.keys(recorded.frames ?? {}).length;
|
|
531
|
+
}
|
|
532
|
+
const viewedFile = path.join(root, ".run", "viewed.json");
|
|
533
|
+
if (viewed === undefined) {
|
|
534
|
+
try { const prior = JSON.parse(readFileSync(viewedFile, "utf8")); return prior.round === round ? prior.count : 0; } catch { return 0; }
|
|
535
|
+
}
|
|
536
|
+
if (existsSync(path.dirname(viewedFile))) writeFileSync(viewedFile, JSON.stringify({ round, count: viewed }) + "\n");
|
|
537
|
+
return viewed;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
export function runGate(dir, { viewed, requirePreview = true } = {}) {
|
|
541
|
+
const root = path.resolve(dir);
|
|
542
|
+
const manifestPath = JSON.parse(readFileSync(path.join(root, "manifest.json"), "utf8")).path;
|
|
543
|
+
if (manifestPath === "stage") return runStageGate(root, { viewed });
|
|
544
|
+
const result = checkRun(root, { requirePreview });
|
|
545
|
+
viewed = viewedCount(root, result.manifest?.craftRound ?? null, viewed);
|
|
546
|
+
const flashPass = result.checks["MO-C-03"].pass;
|
|
547
|
+
const exportState = applyExports(root, flashPass);
|
|
548
|
+
const exitCode = result.passed ? EXIT.OK : EXIT.GATE_FAIL_QA;
|
|
549
|
+
const presetReason = result.run.brief?.explicitPreset ? (result.run.brief.presetSource === "user" ? "user-specified" : "agent default") : selectPreset(result.run.brief?.sourceText ?? "", undefined).reason;
|
|
550
|
+
const report = formatReport(result, { exportDir: flashPass ? root : path.join(root, "withheld"), viewed, exitCode, exportState: exportState === "withheld" ? "withheld (MO-C-03 FAIL: diagnostics only, never deliverables)" : exportState, presetReason, briefPath: result.run.brief?.path });
|
|
551
|
+
writeFileSync(path.join(root, "gate-report.txt"), report);
|
|
552
|
+
return { ...result, exitCode, report, exportState };
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
function runStageGate(root, { viewed }) {
|
|
556
|
+
const result = checkStageRun(root);
|
|
557
|
+
viewed = viewedCount(root, result.manifest.craftRound, viewed);
|
|
558
|
+
const flashPass = result.checks["MO-C-03"].pass;
|
|
559
|
+
const exportState = applyExports(root, flashPass);
|
|
560
|
+
const exitCode = result.passed ? EXIT.OK : EXIT.GATE_FAIL_QA;
|
|
561
|
+
const report = formatStageReport(result, { exportDir: flashPass ? root : path.join(root, "withheld"), viewed, exitCode, exportState: exportState === "withheld" ? "withheld (MO-C-03 FAIL: diagnostics only, never deliverables)" : exportState });
|
|
562
|
+
writeFileSync(path.join(root, "gate-report.txt"), report);
|
|
563
|
+
return { ...result, exitCode, report, exportState };
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// A turn counts as done only after a gate PASS, a valid treatment and two recorded look rounds, the
|
|
567
|
+
// last one on the final render's own stills (engine/look.mjs).
|
|
568
|
+
export function completionState(dir) {
|
|
569
|
+
return doneState(dir);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
function main(argv) {
|
|
573
|
+
if (!argv.length || argv.includes("--help")) {
|
|
574
|
+
console.log("lit-typographic-motion gate\nUsage: node gate.mjs <output-dir> [--viewed N]\n node gate.mjs --done <output-dir> (0 DONE, 1 NOT DONE, 2 DONE_WITH_OPEN_ITEMS after round 3, 3 DONE_UNVIEWED)");
|
|
575
|
+
return 0;
|
|
576
|
+
}
|
|
577
|
+
if (argv[0] === "--done") {
|
|
578
|
+
const state = completionState(argv[1]);
|
|
579
|
+
console.log(formatDone(state));
|
|
580
|
+
return state.code;
|
|
581
|
+
}
|
|
582
|
+
const viewedIndex = argv.indexOf("--viewed");
|
|
583
|
+
const viewed = viewedIndex >= 0 ? Number(argv[viewedIndex + 1]) || 0 : undefined;
|
|
584
|
+
const result = runGate(argv[0], { viewed });
|
|
585
|
+
process.stdout.write(result.report);
|
|
586
|
+
return result.exitCode;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
if (process.argv[1] && existsSync(process.argv[1]) && realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url))) {
|
|
590
|
+
try { process.exitCode = main(process.argv.slice(2)); }
|
|
591
|
+
catch (error) { console.error(`gate error: ${error instanceof Error ? error.message : String(error)}`); process.exitCode = EXIT.GATE_FAIL_QA; }
|
|
592
|
+
}
|
|
593
|
+
|