agentic-dev 0.1.0 → 0.2.1
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/.agent/prd.json +29 -0
- package/.agent/progress.txt +1 -0
- package/.agent/prompt.md +21 -0
- package/.agent/ralph-loop-state.json +13 -0
- package/.agent/ralph-supervisor-state.json +12 -0
- package/.agent/ralph-supervisor.sh +238 -0
- package/.agent/ralph.sh +305 -0
- package/.agent/runs/README.md +7 -0
- package/.agent/sdd-build-ast-audit.json +13 -0
- package/.claude/CLAUDE.md +44 -0
- package/.claude/agentic-dev.json +3 -0
- package/.claude/agents/ai-dev.md +27 -0
- package/.claude/agents/backend-dev.md +26 -0
- package/.claude/agents/db-dev.md +26 -0
- package/.claude/agents/devops.md +27 -0
- package/.claude/agents/frontend-dev.md +25 -0
- package/.claude/agents/github-ops.md +25 -0
- package/.claude/agents/test-dev.md +26 -0
- package/.claude/agents/uiux-designer.md +25 -0
- package/.claude/settings.json +49 -0
- package/.claude/settings.local.json +8 -0
- package/.claude/skills/commit/SKILL.md +37 -0
- package/.claude/skills/dev-browser/SKILL.md +30 -0
- package/.claude/skills/otro/SKILL.md +43 -0
- package/.claude/skills/planning-with-files/SKILL.md +37 -0
- package/.claude/skills/prd/SKILL.md +27 -0
- package/.claude/skills/ralph-loop/SKILL.md +42 -0
- package/.claude/skills/sdd/SKILL.md +13 -0
- package/.claude/skills/sdd-dev/SKILL.md +71 -0
- package/.claude/skills/sdd-development/SKILL.md +13 -0
- package/.claude/workspace-config.json +3 -0
- package/.codex/agentic-dev.json +3 -0
- package/.codex/agents/README.md +22 -0
- package/.codex/agents/api.toml +11 -0
- package/.codex/agents/architecture.toml +11 -0
- package/.codex/agents/ci.toml +11 -0
- package/.codex/agents/gitops.toml +11 -0
- package/.codex/agents/orchestrator.toml +11 -0
- package/.codex/agents/quality.toml +11 -0
- package/.codex/agents/runtime.toml +11 -0
- package/.codex/agents/security.toml +11 -0
- package/.codex/agents/specs.toml +11 -0
- package/.codex/agents/ui.toml +11 -0
- package/.codex/config.toml +46 -0
- package/.codex/skills/SKILL.md +13 -0
- package/.codex/skills/agents/openai.yaml +4 -0
- package/.codex/skills/commit/SKILL.md +219 -0
- package/.codex/skills/commit/references/commit_examples.md +292 -0
- package/.codex/skills/dev-browser/SKILL.md +211 -0
- package/.codex/skills/dev-browser/bun.lock +443 -0
- package/.codex/skills/dev-browser/package-lock.json +2988 -0
- package/.codex/skills/dev-browser/package.json +31 -0
- package/.codex/skills/dev-browser/references/scraping.md +155 -0
- package/.codex/skills/dev-browser/scripts/start-relay.ts +32 -0
- package/.codex/skills/dev-browser/scripts/start-server.ts +117 -0
- package/.codex/skills/dev-browser/server.sh +24 -0
- package/.codex/skills/dev-browser/src/client.ts +474 -0
- package/.codex/skills/dev-browser/src/index.ts +287 -0
- package/.codex/skills/dev-browser/src/relay.ts +731 -0
- package/.codex/skills/dev-browser/src/snapshot/__tests__/snapshot.test.ts +223 -0
- package/.codex/skills/dev-browser/src/snapshot/browser-script.ts +877 -0
- package/.codex/skills/dev-browser/src/snapshot/index.ts +14 -0
- package/.codex/skills/dev-browser/src/snapshot/inject.ts +13 -0
- package/.codex/skills/dev-browser/src/types.ts +34 -0
- package/.codex/skills/dev-browser/tsconfig.json +36 -0
- package/.codex/skills/dev-browser/vitest.config.ts +12 -0
- package/.codex/skills/otro/SKILL.md +74 -0
- package/.codex/skills/otro/agents/openai.yaml +4 -0
- package/.codex/skills/otro/references/agent-prompts.md +61 -0
- package/.codex/skills/otro/references/contracts.md +146 -0
- package/.codex/skills/otro/references/orchestration-loop.md +51 -0
- package/.codex/skills/otro/references/runtime.md +79 -0
- package/.codex/skills/otro/runs/README.md +11 -0
- package/.codex/skills/otro/schemas/step_plan.schema.json +289 -0
- package/.codex/skills/otro/schemas/task_result.schema.json +142 -0
- package/.codex/skills/otro/schemas/wave_plan.schema.json +4 -0
- package/.codex/skills/otro/scripts/README.md +38 -0
- package/.codex/skills/otro/scripts/bump_validation_header.py +179 -0
- package/.codex/skills/otro/scripts/check_validation_header.py +84 -0
- package/.codex/skills/otro/scripts/common.py +303 -0
- package/.codex/skills/otro/scripts/init_run.sh +68 -0
- package/.codex/skills/otro/scripts/plan_loop.py +8 -0
- package/.codex/skills/otro/scripts/plan_step.py +367 -0
- package/.codex/skills/otro/scripts/plan_wave.py +8 -0
- package/.codex/skills/otro/scripts/reconcile_loop.py +8 -0
- package/.codex/skills/otro/scripts/reconcile_step.py +37 -0
- package/.codex/skills/otro/scripts/reconcile_wave.py +8 -0
- package/.codex/skills/otro/scripts/run_loop.py +300 -0
- package/.codex/skills/otro/scripts/run_loop_step.py +8 -0
- package/.codex/skills/otro/scripts/run_step.py +246 -0
- package/.codex/skills/otro/scripts/run_wave.py +8 -0
- package/.codex/skills/otro/validation/validation.md +15 -0
- package/.codex/skills/planning-with-files/SKILL.md +42 -0
- package/.codex/skills/planning-with-files/agents/openai.yaml +4 -0
- package/.codex/skills/planning-with-files/assets/plan-template.md +37 -0
- package/.codex/skills/planning-with-files/references/plan-rules.md +35 -0
- package/.codex/skills/planning-with-files/scripts/new_plan.sh +65 -0
- package/.codex/skills/prd/SKILL.md +235 -0
- package/.codex/skills/ralph-loop/SKILL.md +46 -0
- package/.codex/skills/ralph-loop/agents/openai.yaml +4 -0
- package/.codex/skills/ralph-loop/references/failure-triage.md +32 -0
- package/.codex/skills/ralph-loop/scripts/loop_until_success.sh +97 -0
- package/.codex/skills/sdd/SKILL.md +184 -0
- package/.codex/skills/sdd/agents/openai.yaml +4 -0
- package/.codex/skills/sdd/references/section-map.md +67 -0
- package/.dockerignore +8 -0
- package/.env.example +50 -0
- package/.gitignore +16 -0
- package/AGENTS.md +78 -0
- package/README.md +83 -47
- package/SDD_SKILL.md +589 -0
- package/bin/agentic-dev.mjs +97 -0
- package/client/admin/.dockerignore +3 -0
- package/client/admin/.env.example +1 -0
- package/client/admin/Dockerfile +16 -0
- package/client/admin/Dockerfile.dev +18 -0
- package/client/admin/README.md +20 -0
- package/client/admin/index.html +12 -0
- package/client/admin/package.json +41 -0
- package/client/admin/postcss.config.js +6 -0
- package/client/admin/scripts/ui-parity-admin-adapter.mjs +65 -0
- package/client/admin/src/api/alerts.ts +33 -0
- package/client/admin/src/api/client.ts +71 -0
- package/client/admin/src/api/orders.ts +33 -0
- package/client/admin/src/api/support.ts +11 -0
- package/client/admin/src/app/App.tsx +23 -0
- package/client/admin/src/auth/AuthProvider.tsx +122 -0
- package/client/admin/src/auth/ProtectedRoute.tsx +22 -0
- package/client/admin/src/auth/auth-client.ts +38 -0
- package/client/admin/src/auth/types.ts +18 -0
- package/client/admin/src/components/AdminNotificationsDrawer.tsx +162 -0
- package/client/admin/src/components/AdminShell.tsx +76 -0
- package/client/admin/src/components/ui/button.tsx +34 -0
- package/client/admin/src/components/ui/input.tsx +21 -0
- package/client/admin/src/lib/cn.ts +6 -0
- package/client/admin/src/lib/specRouteCatalog.json +30 -0
- package/client/admin/src/lib/specScreens.json +22 -0
- package/client/admin/src/main.tsx +17 -0
- package/client/admin/src/pages/AdminDashboardPage.tsx +171 -0
- package/client/admin/src/pages/AdminLoginPage.tsx +75 -0
- package/client/admin/src/pages/AdminQueuePage.tsx +107 -0
- package/client/admin/src/pages/AdminSupportPage.tsx +61 -0
- package/client/admin/src/styles/globals.css +17 -0
- package/client/admin/src/theme-vars.ts +18 -0
- package/client/admin/src/theme.ts +25 -0
- package/client/admin/src/vite-env.d.ts +1 -0
- package/client/admin/tailwind.config.js +8 -0
- package/client/admin/tsconfig.json +25 -0
- package/client/admin/vite.config.ts +12 -0
- package/client/landing/.dockerignore +3 -0
- package/client/landing/.env.example +1 -0
- package/client/landing/Dockerfile +16 -0
- package/client/landing/Dockerfile.dev +18 -0
- package/client/landing/README.md +18 -0
- package/client/landing/index.html +12 -0
- package/client/landing/package.json +41 -0
- package/client/landing/postcss.config.js +6 -0
- package/client/landing/scripts/ui-parity-landing-adapter.mjs +65 -0
- package/client/landing/src/App.tsx +21 -0
- package/client/landing/src/api/catalog.ts +30 -0
- package/client/landing/src/api/client.ts +30 -0
- package/client/landing/src/auth/AuthProvider.tsx +122 -0
- package/client/landing/src/auth/ProtectedRoute.tsx +22 -0
- package/client/landing/src/auth/auth-client.ts +38 -0
- package/client/landing/src/auth/types.ts +18 -0
- package/client/landing/src/components/LandingShell.tsx +34 -0
- package/client/landing/src/lib/specRouteCatalog.json +23 -0
- package/client/landing/src/lib/specScreens.json +17 -0
- package/client/landing/src/main.tsx +17 -0
- package/client/landing/src/pages/LandingHomePage.tsx +215 -0
- package/client/landing/src/pages/LandingLoginPage.tsx +90 -0
- package/client/landing/src/pages/LandingWorkspacePage.tsx +126 -0
- package/client/landing/src/styles/globals.css +17 -0
- package/client/landing/src/theme-vars.ts +16 -0
- package/client/landing/src/theme.ts +21 -0
- package/client/landing/src/vite-env.d.ts +1 -0
- package/client/landing/tailwind.config.js +8 -0
- package/client/landing/tsconfig.json +25 -0
- package/client/landing/vite.config.ts +12 -0
- package/client/mobile/.dockerignore +2 -0
- package/client/mobile/.env.example +1 -0
- package/client/mobile/Dockerfile +16 -0
- package/client/mobile/Dockerfile.dev +18 -0
- package/client/mobile/README.md +19 -0
- package/client/mobile/index.html +12 -0
- package/client/mobile/package.json +42 -0
- package/client/mobile/postcss.config.js +6 -0
- package/client/mobile/scripts/ui-parity-mobile-adapter.mjs +67 -0
- package/client/mobile/src/App.tsx +1 -0
- package/client/mobile/src/api/client.ts +62 -0
- package/client/mobile/src/api/fulfillment.ts +55 -0
- package/client/mobile/src/api/shipping.ts +56 -0
- package/client/mobile/src/app/App.tsx +23 -0
- package/client/mobile/src/auth/AuthProvider.tsx +122 -0
- package/client/mobile/src/auth/ProtectedRoute.tsx +27 -0
- package/client/mobile/src/auth/auth-client.ts +38 -0
- package/client/mobile/src/auth/types.ts +18 -0
- package/client/mobile/src/components/InShell.tsx +74 -0
- package/client/mobile/src/components/ui/button.tsx +35 -0
- package/client/mobile/src/components/ui/card.tsx +15 -0
- package/client/mobile/src/components/ui/input.tsx +21 -0
- package/client/mobile/src/lib/cn.ts +6 -0
- package/client/mobile/src/lib/specRouteCatalog.json +26 -0
- package/client/mobile/src/lib/specScreens.json +22 -0
- package/client/mobile/src/lib/useSpeechRecognitionInput.ts +271 -0
- package/client/mobile/src/main.tsx +17 -0
- package/client/mobile/src/pages/DashboardPage.tsx +172 -0
- package/client/mobile/src/pages/FulfillmentPage.tsx +138 -0
- package/client/mobile/src/pages/LoginPage.tsx +74 -0
- package/client/mobile/src/pages/ShippingPage.tsx +338 -0
- package/client/mobile/src/styles/globals.css +23 -0
- package/client/mobile/src/theme-vars.ts +16 -0
- package/client/mobile/src/theme.ts +21 -0
- package/client/mobile/src/vite-env.d.ts +1 -0
- package/client/mobile/tailwind.config.js +8 -0
- package/client/mobile/tsconfig.json +25 -0
- package/client/mobile/vite.config.ts +12 -0
- package/client/platform/.dockerignore +3 -0
- package/client/platform/.env.example +1 -0
- package/client/platform/Dockerfile +16 -0
- package/client/platform/Dockerfile.dev +18 -0
- package/client/platform/README.md +47 -0
- package/client/platform/index.html +12 -0
- package/client/platform/package.json +42 -0
- package/client/platform/postcss.config.js +6 -0
- package/client/platform/scripts/ui-parity-platform-adapter.mjs +66 -0
- package/client/platform/src/api/client.ts +30 -0
- package/client/platform/src/api/orders.ts +42 -0
- package/client/platform/src/app/App.tsx +21 -0
- package/client/platform/src/auth/AuthProvider.tsx +122 -0
- package/client/platform/src/auth/ProtectedRoute.tsx +22 -0
- package/client/platform/src/auth/auth-client.ts +38 -0
- package/client/platform/src/auth/types.ts +18 -0
- package/client/platform/src/components/AppShell.tsx +59 -0
- package/client/platform/src/components/ui/button.tsx +35 -0
- package/client/platform/src/components/ui/card.tsx +7 -0
- package/client/platform/src/components/ui/input.tsx +21 -0
- package/client/platform/src/lib/cn.ts +6 -0
- package/client/platform/src/lib/specRouteCatalog.json +23 -0
- package/client/platform/src/lib/specScreens.json +17 -0
- package/client/platform/src/main.tsx +17 -0
- package/client/platform/src/pages/DashboardPage.tsx +158 -0
- package/client/platform/src/pages/LoginPage.tsx +72 -0
- package/client/platform/src/pages/OrdersPage.tsx +123 -0
- package/client/platform/src/styles/globals.css +17 -0
- package/client/platform/src/theme-vars.ts +18 -0
- package/client/platform/src/theme.ts +25 -0
- package/client/platform/src/vite-env.d.ts +1 -0
- package/client/platform/tailwind.config.js +8 -0
- package/client/platform/tsconfig.json +25 -0
- package/client/platform/vite.config.ts +12 -0
- package/compose.yml +206 -0
- package/infra/compose/.env.dev.example +28 -0
- package/infra/compose/.env.prod.example +29 -0
- package/infra/compose/README.md +35 -0
- package/infra/compose/dev.yml +125 -0
- package/infra/compose/prod.yml +126 -0
- package/infra/terraform/README.md +34 -0
- package/infra/terraform/aws/data/.terraform.lock.hcl +25 -0
- package/infra/terraform/aws/data/README.md +18 -0
- package/infra/terraform/aws/data/main.tf +147 -0
- package/infra/terraform/aws/data/outputs.tf +14 -0
- package/infra/terraform/aws/data/variables.tf +57 -0
- package/infra/terraform/aws/data/versions.tf +10 -0
- package/infra/terraform/aws/domain/.terraform.lock.hcl +25 -0
- package/infra/terraform/aws/domain/README.md +20 -0
- package/infra/terraform/aws/domain/env/dev.tfvars.example +6 -0
- package/infra/terraform/aws/domain/env/prod.tfvars.example +7 -0
- package/infra/terraform/aws/domain/main.tf +149 -0
- package/infra/terraform/aws/domain/outputs.tf +29 -0
- package/infra/terraform/aws/domain/variables.tf +58 -0
- package/infra/terraform/aws/domain/versions.tf +10 -0
- package/infra/terraform/openstack/README.md +38 -0
- package/infra/terraform/openstack/dev/.terraform.lock.hcl +24 -0
- package/infra/terraform/openstack/dev/README.md +18 -0
- package/infra/terraform/openstack/dev/main.tf +49 -0
- package/infra/terraform/openstack/dev/providers.tf +15 -0
- package/infra/terraform/openstack/dev/terraform.tfvars.example +54 -0
- package/infra/terraform/openstack/dev/variables.tf +210 -0
- package/infra/terraform/openstack/dev/versions.tf +10 -0
- package/infra/terraform/openstack/modules/environment_host/main.tf +143 -0
- package/infra/terraform/openstack/modules/environment_host/outputs.tf +25 -0
- package/infra/terraform/openstack/modules/environment_host/templates/docker-host-user-data.sh.tftpl +40 -0
- package/infra/terraform/openstack/modules/environment_host/variables.tf +145 -0
- package/infra/terraform/openstack/modules/environment_host/versions.tf +7 -0
- package/infra/terraform/openstack/prod/.terraform.lock.hcl +24 -0
- package/infra/terraform/openstack/prod/README.md +18 -0
- package/infra/terraform/openstack/prod/main.tf +49 -0
- package/infra/terraform/openstack/prod/providers.tf +15 -0
- package/infra/terraform/openstack/prod/terraform.tfvars.example +55 -0
- package/infra/terraform/openstack/prod/variables.tf +210 -0
- package/infra/terraform/openstack/prod/versions.tf +10 -0
- package/infra/terraform/openstack/server/.terraform.lock.hcl +45 -0
- package/infra/terraform/openstack/server/README.md +47 -0
- package/infra/terraform/openstack/server/main.tf +161 -0
- package/infra/terraform/openstack/server/outputs.tf +30 -0
- package/infra/terraform/openstack/server/providers.tf +30 -0
- package/infra/terraform/openstack/server/templates/server-user-data.sh.tftpl +50 -0
- package/infra/terraform/openstack/server/variables.tf +233 -0
- package/infra/terraform/openstack/server/zz_aspace.auto.tfvars.example.json +29 -0
- package/lib/scaffold.mjs +379 -0
- package/package.json +33 -12
- package/pnpm-workspace.yaml +2 -0
- package/scripts/dev/audit_sdd_build_ast.py +277 -0
- package/sdd/01_planning/01_feature/INDEX.md +16 -0
- package/sdd/01_planning/01_feature/README.md +76 -0
- package/sdd/01_planning/01_feature/alerts_feature_spec.md +55 -0
- package/sdd/01_planning/01_feature/auth_feature_spec.md +57 -0
- package/sdd/01_planning/01_feature/catalog_feature_spec.md +61 -0
- package/sdd/01_planning/01_feature/fulfillment_feature_spec.md +58 -0
- package/sdd/01_planning/01_feature/health_feature_spec.md +52 -0
- package/sdd/01_planning/01_feature/inventory_feature_spec.md +60 -0
- package/sdd/01_planning/01_feature/order_feature_spec.md +63 -0
- package/sdd/01_planning/01_feature/shipping_feature_spec.md +55 -0
- package/sdd/01_planning/01_feature/support_feature_spec.md +53 -0
- package/sdd/01_planning/01_feature/user_feature_spec.md +54 -0
- package/sdd/01_planning/02_screen/INDEX.md +13 -0
- package/sdd/01_planning/02_screen/README.md +41 -0
- package/sdd/01_planning/02_screen/admin_screen_spec.pdf +0 -0
- package/sdd/01_planning/02_screen/assets/README.md +16 -0
- package/sdd/01_planning/02_screen/assets/example/README.md +13 -0
- package/sdd/01_planning/02_screen/landing_screen_spec.pdf +0 -0
- package/sdd/01_planning/02_screen/mobile_screen_spec.pdf +0 -0
- package/sdd/01_planning/02_screen/platform_screen_spec.pdf +0 -0
- package/sdd/01_planning/03_architecture/INDEX.md +9 -0
- package/sdd/01_planning/03_architecture/README.md +25 -0
- package/sdd/01_planning/03_architecture/architecture_document_structure.md +77 -0
- package/sdd/01_planning/03_architecture/backend/README.md +10 -0
- package/sdd/01_planning/03_architecture/frontend/README.md +12 -0
- package/sdd/01_planning/03_architecture/infra/README.md +10 -0
- package/sdd/01_planning/03_architecture/tech-research/README.md +4 -0
- package/sdd/01_planning/03_architecture/templates_system_architecture.md +84 -0
- package/sdd/01_planning/04_data/INDEX.md +4 -0
- package/sdd/01_planning/04_data/README.md +10 -0
- package/sdd/01_planning/04_data/templates_data_modeling.md +119 -0
- package/sdd/01_planning/05_api/README.md +12 -0
- package/sdd/01_planning/05_api/templates_api_contract.md +90 -0
- package/sdd/01_planning/06_iac/README.md +11 -0
- package/sdd/01_planning/06_iac/templates_runtime_and_cicd_baseline.md +46 -0
- package/sdd/01_planning/07_integration/README.md +11 -0
- package/sdd/01_planning/07_integration/templates_frontend_api_integration.md +46 -0
- package/sdd/01_planning/08_nonfunctional/README.md +7 -0
- package/sdd/01_planning/09_security/README.md +7 -0
- package/sdd/01_planning/10_test/README.md +12 -0
- package/sdd/01_planning/10_test/templates_test_strategy.md +60 -0
- package/sdd/01_planning/INDEX.md +19 -0
- package/sdd/01_planning/README.md +17 -0
- package/sdd/02_plan/01_feature/README.md +34 -0
- package/sdd/02_plan/01_feature/_feature_todo_template.md +29 -0
- package/sdd/02_plan/02_screen/INDEX.md +19 -0
- package/sdd/02_plan/02_screen/README.md +39 -0
- package/sdd/02_plan/02_screen/_screen_todo_template.md +60 -0
- package/sdd/02_plan/03_architecture/README.md +23 -0
- package/sdd/02_plan/03_architecture/architecture_document_governance.md +40 -0
- package/sdd/02_plan/03_architecture/build_ast_runtime_tree_governance.md +53 -0
- package/sdd/02_plan/03_architecture/repository_governance.md +39 -0
- package/sdd/02_plan/03_architecture/runtime_and_structure_governance.md +38 -0
- package/sdd/02_plan/03_architecture/templates-hexagonal-template-architecture.md +9 -0
- package/sdd/02_plan/03_architecture/toolchain_governance.md +98 -0
- package/sdd/02_plan/04_data/README.md +5 -0
- package/sdd/02_plan/05_api/README.md +5 -0
- package/sdd/02_plan/06_iac/README.md +11 -0
- package/sdd/02_plan/06_iac/dev_runtime_delivery.md +36 -0
- package/sdd/02_plan/06_iac/template_runtime_delivery.md +50 -0
- package/sdd/02_plan/07_integration/README.md +5 -0
- package/sdd/02_plan/07_integration/frontend_live_integration.md +31 -0
- package/sdd/02_plan/08_nonfunctional/README.md +5 -0
- package/sdd/02_plan/08_nonfunctional/repository_hygiene.md +26 -0
- package/sdd/02_plan/09_security/README.md +5 -0
- package/sdd/02_plan/10_test/README.md +11 -0
- package/sdd/02_plan/10_test/regression_verification.md +39 -0
- package/sdd/02_plan/10_test/templates/README.md +8 -0
- package/sdd/02_plan/10_test/templates/ui_parity_platform_contract.template.yaml +23 -0
- package/sdd/02_plan/10_test/verification_strategy.md +43 -0
- package/sdd/02_plan/99_generated/from_planning/ui_parity/.gitkeep +1 -0
- package/sdd/02_plan/README.md +40 -0
- package/sdd/03_build/01_feature/README.md +20 -0
- package/sdd/03_build/01_feature/domain/README.md +3 -0
- package/sdd/03_build/01_feature/domain/account_and_access.md +20 -0
- package/sdd/03_build/01_feature/domain/catalog_and_inventory.md +20 -0
- package/sdd/03_build/01_feature/domain/ordering_and_fulfillment.md +21 -0
- package/sdd/03_build/01_feature/domain/support_and_observability.md +21 -0
- package/sdd/03_build/01_feature/domain_surfaces.md +28 -0
- package/sdd/03_build/01_feature/service/README.md +3 -0
- package/sdd/03_build/01_feature/service/admin_surface.md +15 -0
- package/sdd/03_build/01_feature/service/landing_surface.md +13 -0
- package/sdd/03_build/01_feature/service/mobile_surface.md +14 -0
- package/sdd/03_build/01_feature/service/platform_surface.md +14 -0
- package/sdd/03_build/02_screen/README.md +25 -0
- package/sdd/03_build/02_screen/_screen_build_template.md +26 -0
- package/sdd/03_build/02_screen/admin/README.md +5 -0
- package/sdd/03_build/02_screen/landing/README.md +5 -0
- package/sdd/03_build/02_screen/mobile/README.md +5 -0
- package/sdd/03_build/02_screen/platform/README.md +5 -0
- package/sdd/03_build/03_architecture/README.md +10 -0
- package/sdd/03_build/03_architecture/architecture_document_governance.md +30 -0
- package/sdd/03_build/03_architecture/build_ast_runtime_tree_governance.md +24 -0
- package/sdd/03_build/03_architecture/repository_governance.md +18 -0
- package/sdd/03_build/03_architecture/toolchain_governance.md +36 -0
- package/sdd/03_build/06_iac/README.md +3 -0
- package/sdd/03_build/06_iac/dev_runtime_delivery.md +10 -0
- package/sdd/03_build/06_iac/template_runtime_delivery.md +49 -0
- package/sdd/03_build/07_integration/README.md +3 -0
- package/sdd/03_build/07_integration/frontend_live_integration.md +11 -0
- package/sdd/03_build/08_nonfunctional/README.md +3 -0
- package/sdd/03_build/08_nonfunctional/repository_hygiene.md +10 -0
- package/sdd/03_build/10_test/README.md +9 -0
- package/sdd/03_build/10_test/regression_verification.md +16 -0
- package/sdd/03_build/10_test/verification_harness.md +11 -0
- package/sdd/03_build/README.md +35 -0
- package/sdd/04_verify/01_feature/README.md +5 -0
- package/sdd/04_verify/01_feature/domain_verification.md +14 -0
- package/sdd/04_verify/01_feature/service_verification.md +22 -0
- package/sdd/04_verify/02_screen/README.md +6 -0
- package/sdd/04_verify/02_screen/_screen_verify_template.md +20 -0
- package/sdd/04_verify/02_screen/admin/README.md +4 -0
- package/sdd/04_verify/02_screen/landing/README.md +4 -0
- package/sdd/04_verify/02_screen/mobile/README.md +4 -0
- package/sdd/04_verify/02_screen/platform/README.md +4 -0
- package/sdd/04_verify/03_architecture/README.md +10 -0
- package/sdd/04_verify/03_architecture/architecture_document_governance.md +15 -0
- package/sdd/04_verify/03_architecture/build_ast_runtime_tree_governance.md +28 -0
- package/sdd/04_verify/03_architecture/repository_governance.md +16 -0
- package/sdd/04_verify/03_architecture/toolchain_governance.md +58 -0
- package/sdd/04_verify/06_iac/README.md +3 -0
- package/sdd/04_verify/06_iac/dev_runtime_delivery.md +10 -0
- package/sdd/04_verify/06_iac/template_runtime_delivery.md +42 -0
- package/sdd/04_verify/07_integration/README.md +3 -0
- package/sdd/04_verify/07_integration/frontend_live_integration.md +16 -0
- package/sdd/04_verify/08_nonfunctional/README.md +3 -0
- package/sdd/04_verify/08_nonfunctional/repository_hygiene.md +14 -0
- package/sdd/04_verify/10_test/README.md +9 -0
- package/sdd/04_verify/10_test/regression_verification.md +16 -0
- package/sdd/04_verify/10_test/ui_parity/README.md +4 -0
- package/sdd/04_verify/10_test/ui_parity/loop_runs/.gitkeep +0 -0
- package/sdd/04_verify/10_test/ui_parity/reference/.gitkeep +0 -0
- package/sdd/04_verify/10_test/ui_parity/staged_runs/.gitkeep +0 -0
- package/sdd/04_verify/10_test/verification_harness.md +17 -0
- package/sdd/04_verify/README.md +22 -0
- package/sdd/05_operate/01_runbooks/.gitkeep +1 -0
- package/sdd/05_operate/01_runbooks/README.md +4 -0
- package/sdd/05_operate/02_delivery_status/README.md +4 -0
- package/sdd/05_operate/02_delivery_status/service_status.md +16 -0
- package/sdd/05_operate/README.md +12 -0
- package/sdd/99_toolchain/01_automation/.gitkeep +1 -0
- package/sdd/99_toolchain/01_automation/README.md +76 -0
- package/sdd/99_toolchain/01_automation/agentic-dev/analyze_proof_results.py +132 -0
- package/sdd/99_toolchain/01_automation/agentic-dev/analyze_route_gap.py +85 -0
- package/sdd/99_toolchain/01_automation/agentic-dev/assets/repo-contract.template.json +75 -0
- package/sdd/99_toolchain/01_automation/agentic-dev/bootstrap_frontend_parity.sh +84 -0
- package/sdd/99_toolchain/01_automation/agentic-dev/init_frontend_parity.sh +33 -0
- package/sdd/99_toolchain/01_automation/agentic-dev/init_repo_contract.sh +51 -0
- package/sdd/99_toolchain/01_automation/agentic-dev/repo-contract.json +76 -0
- package/sdd/99_toolchain/01_automation/agentic-dev/resolve_frontend_target.py +52 -0
- package/sdd/99_toolchain/01_automation/agentic-dev/resolve_repo_contract.py +56 -0
- package/sdd/99_toolchain/01_automation/agentic-dev/run_frontend_target.sh +100 -0
- package/sdd/99_toolchain/01_automation/agentic-dev/run_repo_phase.sh +140 -0
- package/sdd/99_toolchain/01_automation/agentic-dev/validate_json_schema.py +39 -0
- package/sdd/99_toolchain/01_automation/agentic-parity-harness-design.md +291 -0
- package/sdd/99_toolchain/01_automation/assets/admin_screen_capture/dashboard.png +0 -0
- package/sdd/99_toolchain/01_automation/assets/admin_screen_capture/login.png +0 -0
- package/sdd/99_toolchain/01_automation/assets/admin_screen_capture/queue.png +0 -0
- package/sdd/99_toolchain/01_automation/assets/admin_screen_capture/support.png +0 -0
- package/sdd/99_toolchain/01_automation/assets/landing_screen_capture/home.png +0 -0
- package/sdd/99_toolchain/01_automation/assets/landing_screen_capture/login.png +0 -0
- package/sdd/99_toolchain/01_automation/assets/landing_screen_capture/workspace.png +0 -0
- package/sdd/99_toolchain/01_automation/assets/mobile_screen_capture/dashboard.png +0 -0
- package/sdd/99_toolchain/01_automation/assets/mobile_screen_capture/fulfillment.png +0 -0
- package/sdd/99_toolchain/01_automation/assets/mobile_screen_capture/login.png +0 -0
- package/sdd/99_toolchain/01_automation/assets/platform_screen_capture/dashboard.png +0 -0
- package/sdd/99_toolchain/01_automation/assets/platform_screen_capture/login.png +0 -0
- package/sdd/99_toolchain/01_automation/assets/platform_screen_capture/orders.png +0 -0
- package/sdd/99_toolchain/01_automation/build_asset_recipes.py +10 -0
- package/sdd/99_toolchain/01_automation/build_screen_spec_pdf.py +427 -0
- package/sdd/99_toolchain/01_automation/capture_screen_assets.mjs +148 -0
- package/sdd/99_toolchain/01_automation/harness-layout.md +34 -0
- package/sdd/99_toolchain/01_automation/parity-execution-tooling-design.md +319 -0
- package/sdd/99_toolchain/01_automation/playwright_exactness_manifest.py +21 -0
- package/sdd/99_toolchain/01_automation/run_playwright_exactness.py +87 -0
- package/sdd/99_toolchain/01_automation/screen_spec_manifest.py +321 -0
- package/sdd/99_toolchain/01_automation/spec_asset_builder.py +274 -0
- package/sdd/99_toolchain/01_automation/ui-contract-projection.md +79 -0
- package/sdd/99_toolchain/01_automation/ui-parity/README.md +60 -0
- package/sdd/99_toolchain/01_automation/ui-parity/cli/extract-reference-pages.mjs +2 -0
- package/sdd/99_toolchain/01_automation/ui-parity/cli/materialize-reference-assets.mjs +58 -0
- package/sdd/99_toolchain/01_automation/ui-parity/cli/normalize-reference-assets.mjs +2 -0
- package/sdd/99_toolchain/01_automation/ui-parity/cli/route-gap-report.mjs +187 -0
- package/sdd/99_toolchain/01_automation/ui-parity/cli/run-proof.mjs +50 -0
- package/sdd/99_toolchain/01_automation/ui-parity/cli/scaffold-contract.mjs +62 -0
- package/sdd/99_toolchain/01_automation/ui-parity/cli/upload-parity1.mjs +2 -0
- package/sdd/99_toolchain/01_automation/ui-parity/contracts/collector-metadata.schema.json +33 -0
- package/sdd/99_toolchain/01_automation/ui-parity/contracts/proof-result.schema.json +76 -0
- package/sdd/99_toolchain/01_automation/ui-parity/contracts/route-gap-report.schema.json +95 -0
- package/sdd/99_toolchain/01_automation/ui-parity/core/capture-runner.mjs +55 -0
- package/sdd/99_toolchain/01_automation/ui-parity/core/load-adapter.mjs +25 -0
- package/sdd/99_toolchain/01_automation/ui-parity/core/load-contract.mjs +81 -0
- package/sdd/99_toolchain/01_automation/ui-parity/core/paths.mjs +23 -0
- package/sdd/99_toolchain/01_automation/ui-parity/core/proof-runner.mjs +255 -0
- package/sdd/99_toolchain/01_automation/ui-parity/interfaces/ui-parity-artifact-layout.md +23 -0
- package/sdd/99_toolchain/01_automation/ui-parity/interfaces/ui-parity-proof-interface.md +60 -0
- package/sdd/99_toolchain/01_automation/ui-parity/interfaces/ui-parity-route-gap-interface.md +82 -0
- package/sdd/99_toolchain/01_automation/ui-parity/runtime/playwright-runtime.mjs +16 -0
- package/sdd/99_toolchain/01_automation/ui-parity/runtime/static-runtime.mjs +6 -0
- package/sdd/99_toolchain/02_policies/.gitkeep +1 -0
- package/sdd/99_toolchain/02_policies/build-ast-governance-policy.md +22 -0
- package/sdd/99_toolchain/02_policies/compose-runtime-baseline-policy.md +24 -0
- package/sdd/99_toolchain/02_policies/convention-storage-policy.md +26 -0
- package/sdd/99_toolchain/02_policies/main-push-before-dev-deploy-policy.md +27 -0
- package/sdd/99_toolchain/02_policies/otro-orchestration-policy.md +30 -0
- package/sdd/99_toolchain/02_policies/regression-verification-policy.md +22 -0
- package/sdd/99_toolchain/03_templates/.gitkeep +1 -0
- package/sdd/99_toolchain/03_templates/asset_recipe_manifest.example.py +38 -0
- package/sdd/99_toolchain/03_templates/generated_assets/README.md +11 -0
- package/sdd/99_toolchain/03_templates/generated_assets/example-brand-lockup.svg +3 -0
- package/sdd/99_toolchain/03_templates/generated_assets/example-brand-mark.svg +3 -0
- package/sdd/99_toolchain/03_templates/generated_assets/example-brand-wordmark.svg +3 -0
- package/sdd/99_toolchain/03_templates/playwright_exactness_manifest.example.py +21 -0
- package/sdd/99_toolchain/README.md +23 -0
- package/sdd/README.md +21 -0
- package/server/.dockerignore +4 -0
- package/server/.env.example +19 -0
- package/server/Dockerfile +22 -0
- package/server/Dockerfile.dev +19 -0
- package/server/README.md +33 -0
- package/server/__init__.py +0 -0
- package/server/api/__init__.py +1 -0
- package/server/api/http/__init__.py +4 -0
- package/server/api/http/app.py +53 -0
- package/server/api/http/router.py +24 -0
- package/server/config.py +52 -0
- package/server/contexts/__init__.py +12 -0
- package/server/contexts/alerts/__init__.py +1 -0
- package/server/contexts/alerts/application/__init__.py +13 -0
- package/server/contexts/alerts/application/services.py +41 -0
- package/server/contexts/alerts/contracts/__init__.py +3 -0
- package/server/contexts/alerts/contracts/http/__init__.py +3 -0
- package/server/contexts/alerts/contracts/http/router.py +37 -0
- package/server/contexts/alerts/domain/__init__.py +15 -0
- package/server/contexts/alerts/domain/models.py +29 -0
- package/server/contexts/alerts/infrastructure/__init__.py +11 -0
- package/server/contexts/alerts/infrastructure/repository.py +41 -0
- package/server/contexts/auth/__init__.py +1 -0
- package/server/contexts/auth/application/__init__.py +3 -0
- package/server/contexts/auth/application/ports.py +10 -0
- package/server/contexts/auth/application/services.py +64 -0
- package/server/contexts/auth/contracts/__init__.py +4 -0
- package/server/contexts/auth/contracts/http/__init__.py +4 -0
- package/server/contexts/auth/contracts/http/dependencies.py +37 -0
- package/server/contexts/auth/contracts/http/router.py +19 -0
- package/server/contexts/auth/domain/__init__.py +3 -0
- package/server/contexts/auth/domain/models.py +24 -0
- package/server/contexts/auth/infrastructure/__init__.py +4 -0
- package/server/contexts/auth/infrastructure/adapters/memory.py +19 -0
- package/server/contexts/auth/infrastructure/adapters/mongodb.py +24 -0
- package/server/contexts/auth/infrastructure/adapters/sqlalchemy.py +74 -0
- package/server/contexts/auth/infrastructure/repository.py +28 -0
- package/server/contexts/catalog/__init__.py +1 -0
- package/server/contexts/catalog/application/__init__.py +28 -0
- package/server/contexts/catalog/application/ports.py +15 -0
- package/server/contexts/catalog/application/services.py +154 -0
- package/server/contexts/catalog/contracts/__init__.py +3 -0
- package/server/contexts/catalog/contracts/http/__init__.py +3 -0
- package/server/contexts/catalog/contracts/http/router.py +60 -0
- package/server/contexts/catalog/domain/__init__.py +45 -0
- package/server/contexts/catalog/domain/models.py +113 -0
- package/server/contexts/catalog/infrastructure/__init__.py +4 -0
- package/server/contexts/catalog/infrastructure/adapters/memory.py +62 -0
- package/server/contexts/catalog/infrastructure/repository.py +8 -0
- package/server/contexts/fulfillment/__init__.py +1 -0
- package/server/contexts/fulfillment/application/__init__.py +13 -0
- package/server/contexts/fulfillment/application/ports.py +20 -0
- package/server/contexts/fulfillment/application/services.py +85 -0
- package/server/contexts/fulfillment/contracts/__init__.py +3 -0
- package/server/contexts/fulfillment/contracts/http/__init__.py +3 -0
- package/server/contexts/fulfillment/contracts/http/router.py +40 -0
- package/server/contexts/fulfillment/domain/__init__.py +25 -0
- package/server/contexts/fulfillment/domain/models.py +73 -0
- package/server/contexts/fulfillment/infrastructure/__init__.py +13 -0
- package/server/contexts/fulfillment/infrastructure/adapters/memory.py +43 -0
- package/server/contexts/fulfillment/infrastructure/repository.py +97 -0
- package/server/contexts/health/__init__.py +1 -0
- package/server/contexts/health/application/__init__.py +3 -0
- package/server/contexts/health/application/services.py +2 -0
- package/server/contexts/health/contracts/__init__.py +3 -0
- package/server/contexts/health/contracts/http/__init__.py +3 -0
- package/server/contexts/health/contracts/http/router.py +10 -0
- package/server/contexts/inventory/__init__.py +1 -0
- package/server/contexts/inventory/application/__init__.py +28 -0
- package/server/contexts/inventory/application/ports.py +11 -0
- package/server/contexts/inventory/application/services.py +214 -0
- package/server/contexts/inventory/contracts/__init__.py +3 -0
- package/server/contexts/inventory/contracts/http/__init__.py +3 -0
- package/server/contexts/inventory/contracts/http/router.py +82 -0
- package/server/contexts/inventory/domain/__init__.py +33 -0
- package/server/contexts/inventory/domain/models.py +93 -0
- package/server/contexts/inventory/infrastructure/__init__.py +4 -0
- package/server/contexts/inventory/infrastructure/adapters/memory.py +24 -0
- package/server/contexts/inventory/infrastructure/repository.py +8 -0
- package/server/contexts/orders/__init__.py +1 -0
- package/server/contexts/orders/application/__init__.py +19 -0
- package/server/contexts/orders/application/services.py +127 -0
- package/server/contexts/orders/contracts/__init__.py +3 -0
- package/server/contexts/orders/contracts/http/__init__.py +3 -0
- package/server/contexts/orders/contracts/http/router.py +82 -0
- package/server/contexts/orders/domain/__init__.py +29 -0
- package/server/contexts/orders/domain/models.py +95 -0
- package/server/contexts/orders/infrastructure/__init__.py +7 -0
- package/server/contexts/orders/infrastructure/repository.py +104 -0
- package/server/contexts/shipping/__init__.py +1 -0
- package/server/contexts/shipping/application/__init__.py +13 -0
- package/server/contexts/shipping/application/services.py +92 -0
- package/server/contexts/shipping/contracts/__init__.py +3 -0
- package/server/contexts/shipping/contracts/http/__init__.py +3 -0
- package/server/contexts/shipping/contracts/http/router.py +40 -0
- package/server/contexts/shipping/domain/__init__.py +19 -0
- package/server/contexts/shipping/domain/models.py +48 -0
- package/server/contexts/shipping/infrastructure/__init__.py +9 -0
- package/server/contexts/shipping/infrastructure/repository.py +50 -0
- package/server/contexts/support/__init__.py +1 -0
- package/server/contexts/support/application/__init__.py +13 -0
- package/server/contexts/support/application/services.py +29 -0
- package/server/contexts/support/contracts/__init__.py +3 -0
- package/server/contexts/support/contracts/http/__init__.py +3 -0
- package/server/contexts/support/contracts/http/router.py +40 -0
- package/server/contexts/support/domain/__init__.py +13 -0
- package/server/contexts/support/domain/models.py +27 -0
- package/server/contexts/support/infrastructure/__init__.py +11 -0
- package/server/contexts/support/infrastructure/repository.py +70 -0
- package/server/contexts/user/__init__.py +1 -0
- package/server/contexts/user/application/__init__.py +3 -0
- package/server/contexts/user/application/ports.py +11 -0
- package/server/contexts/user/application/services.py +44 -0
- package/server/contexts/user/contracts/__init__.py +3 -0
- package/server/contexts/user/contracts/http/__init__.py +3 -0
- package/server/contexts/user/contracts/http/router.py +26 -0
- package/server/contexts/user/domain/__init__.py +3 -0
- package/server/contexts/user/domain/models.py +22 -0
- package/server/contexts/user/infrastructure/__init__.py +3 -0
- package/server/contexts/user/infrastructure/adapters/memory.py +27 -0
- package/server/contexts/user/infrastructure/adapters/mongodb.py +41 -0
- package/server/contexts/user/infrastructure/adapters/sqlalchemy.py +94 -0
- package/server/contexts/user/infrastructure/factory.py +28 -0
- package/server/data/README.md +24 -0
- package/server/data/bootstrap/alerts.json +38 -0
- package/server/data/bootstrap/auth_accounts.json +18 -0
- package/server/data/bootstrap/catalog_products.json +179 -0
- package/server/data/bootstrap/fulfillment_events.json +5 -0
- package/server/data/bootstrap/fulfillment_notes.json +5 -0
- package/server/data/bootstrap/fulfillment_tasks.json +50 -0
- package/server/data/bootstrap/inventory_levels.json +80 -0
- package/server/data/bootstrap/orders.json +62 -0
- package/server/data/bootstrap/shipping_shipments.json +50 -0
- package/server/data/bootstrap/support_faqs.json +26 -0
- package/server/data/bootstrap/users.json +20 -0
- package/server/data/bootstrap_loader.py +15 -0
- package/server/docker-entrypoint.sh +56 -0
- package/server/main.py +3 -0
- package/server/pyproject.toml +36 -0
- package/server/shared/__init__.py +1 -0
- package/server/shared/application/__init__.py +3 -0
- package/server/shared/application/health.py +2 -0
- package/server/shared/infrastructure/__init__.py +10 -0
- package/server/shared/infrastructure/runtime.py +6 -0
- package/server/shared/infrastructure/security.py +33 -0
- package/server/tests/e2e/test_domain_feature_flows.py +483 -0
- package/server/tests/test_health.py +49 -0
- package/server/uv.lock +1169 -0
- package/bin/agentic-dev.js +0 -9
- package/src/cli.js +0 -37
- package/src/init-command.js +0 -230
- package/src/lib/command.js +0 -14
- package/src/lib/fs.js +0 -92
- package/src/lib/git.js +0 -57
- package/src/lib/github.js +0 -77
- package/src/lib/prompt.js +0 -52
- package/src/lib/system.js +0 -15
- package/src/lib/template.js +0 -143
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
ROOT = Path(__file__).resolve().parents[3]
|
|
7
|
+
SCREEN_DIR = ROOT / "sdd/01_planning/02_screen"
|
|
8
|
+
ASSET_DIR = ROOT / "sdd/99_toolchain/01_automation/assets"
|
|
9
|
+
DESKTOP_CAPTURE_POLICY = {
|
|
10
|
+
"reference_visible_area": "1710x951",
|
|
11
|
+
"default_viewport": "1690x940",
|
|
12
|
+
"capture_mode": "viewport-first",
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def screen(
|
|
17
|
+
*,
|
|
18
|
+
code: str,
|
|
19
|
+
name: str,
|
|
20
|
+
route: str,
|
|
21
|
+
access: str,
|
|
22
|
+
features: list[str],
|
|
23
|
+
asset: str,
|
|
24
|
+
requires_auth: bool,
|
|
25
|
+
callouts: list[tuple[tuple[float, float], str, str]],
|
|
26
|
+
crop_box: tuple[int, int, int, int] | None = None,
|
|
27
|
+
segments: list[dict] | None = None,
|
|
28
|
+
) -> dict:
|
|
29
|
+
item = {
|
|
30
|
+
"code": code,
|
|
31
|
+
"name": name,
|
|
32
|
+
"route": route,
|
|
33
|
+
"access": access,
|
|
34
|
+
"features": features,
|
|
35
|
+
"asset": asset,
|
|
36
|
+
"requires_auth": requires_auth,
|
|
37
|
+
"callouts": callouts,
|
|
38
|
+
}
|
|
39
|
+
if crop_box:
|
|
40
|
+
item["crop_box"] = crop_box
|
|
41
|
+
if segments:
|
|
42
|
+
item["segments"] = segments
|
|
43
|
+
return item
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
SCREEN_MANIFESTS = {
|
|
47
|
+
"platform": {
|
|
48
|
+
"title": "Templates Platform Screen Spec",
|
|
49
|
+
"service_label": "platform",
|
|
50
|
+
"output": str(SCREEN_DIR / "platform_screen_spec.pdf"),
|
|
51
|
+
"asset_dir": str(ASSET_DIR / "platform_screen_capture"),
|
|
52
|
+
"base_url": "http://127.0.0.1:3001",
|
|
53
|
+
"api_base": "http://127.0.0.1:8000/api/v1",
|
|
54
|
+
"storage_key": "platform.auth.token",
|
|
55
|
+
"capture_policy": DESKTOP_CAPTURE_POLICY,
|
|
56
|
+
"trim_background": False,
|
|
57
|
+
"cover_note": "첫 페이지는 platform 화면코드 인덱스다. 이후 페이지는 좌측 화면 캡처와 우측 번호형 기능 테이블을 함께 제공한다.",
|
|
58
|
+
"source_refs": [
|
|
59
|
+
{"label": "implementation", "path": "client/platform/src/app/App.tsx"},
|
|
60
|
+
{"label": "implementation", "path": "client/platform/src/pages"},
|
|
61
|
+
{"label": "capture assets", "path": "sdd/99_toolchain/01_automation/assets/platform_screen_capture"},
|
|
62
|
+
],
|
|
63
|
+
"screens": [
|
|
64
|
+
screen(
|
|
65
|
+
code="PLT-S001",
|
|
66
|
+
name="Platform Login",
|
|
67
|
+
route="/login",
|
|
68
|
+
access="public",
|
|
69
|
+
features=["AUT-F001", "AUT-F002"],
|
|
70
|
+
asset="login.png",
|
|
71
|
+
requires_auth=False,
|
|
72
|
+
callouts=[
|
|
73
|
+
((0.25, 0.31), "서비스 소개 카피", "실제 API 인증 흐름과 로그인 이후 앱 셸 진입 방식을 좌측 설명 영역에서 안내한다."),
|
|
74
|
+
((0.78, 0.21), "Sign in 헤더", "플랫폼 로그인 목적과 예시 계정 프리셋 상태를 보여준다."),
|
|
75
|
+
((0.78, 0.38), "자격 증명 입력", "이메일과 비밀번호를 입력하고 오류 상태를 같은 카드 안에서 확인한다."),
|
|
76
|
+
((0.78, 0.48), "Continue CTA", "인증 성공 시 저장된 토큰으로 `auth/me`를 다시 호출한 뒤 보호 라우트로 이동한다."),
|
|
77
|
+
],
|
|
78
|
+
),
|
|
79
|
+
screen(
|
|
80
|
+
code="PLT-S002",
|
|
81
|
+
name="Platform Dashboard",
|
|
82
|
+
route="/",
|
|
83
|
+
access="protected",
|
|
84
|
+
features=["AUT-F002", "ORD-F001"],
|
|
85
|
+
asset="dashboard.png",
|
|
86
|
+
requires_auth=True,
|
|
87
|
+
callouts=[
|
|
88
|
+
((0.17, 0.09), "상단 앱 셸", "브랜드, Overview/Orders 전환, 검색, 알림, 사용자 액션을 한 줄 헤더로 제공한다."),
|
|
89
|
+
((0.26, 0.25), "운영 통계 카드", "주문 overview 핵심 지표와 로딩 또는 오류 상태를 상단 카드 묶음으로 보여준다."),
|
|
90
|
+
((0.34, 0.60), "Recent activity 표", "최근 주문 이벤트를 주문 ID, 일자, customer, 상태 기준으로 읽는 메인 데이터 테이블이다."),
|
|
91
|
+
((0.83, 0.55), "Selected detail 패널", "선택 주문의 product, customer, status, amount를 보조 패널에서 확인한다."),
|
|
92
|
+
],
|
|
93
|
+
),
|
|
94
|
+
screen(
|
|
95
|
+
code="PLT-S003",
|
|
96
|
+
name="Platform Orders",
|
|
97
|
+
route="/orders",
|
|
98
|
+
access="protected",
|
|
99
|
+
features=["AUT-F002", "ORD-F002"],
|
|
100
|
+
asset="orders.png",
|
|
101
|
+
requires_auth=True,
|
|
102
|
+
callouts=[
|
|
103
|
+
((0.17, 0.09), "상단 앱 셸", "Overview와 Orders 간 전환, 검색, 알림, 로그아웃 등 공통 워크스페이스 셸을 유지한다."),
|
|
104
|
+
((0.36, 0.25), "검색/필터 바", "Search orders 입력값으로 product, customer, 상태, 주문 ID를 클라이언트 필터링한다."),
|
|
105
|
+
((0.78, 0.25), "액션 버튼", "Reset으로 필터를 초기화하고 New order CTA로 신규 흐름 진입점을 노출한다."),
|
|
106
|
+
((0.44, 0.61), "주문 목록 테이블", "주문 ID, product, customer, 상태를 기준으로 현재 결과 집합을 표 형태로 보여준다."),
|
|
107
|
+
],
|
|
108
|
+
),
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
"admin": {
|
|
112
|
+
"title": "Templates Admin Screen Spec",
|
|
113
|
+
"service_label": "admin",
|
|
114
|
+
"output": str(SCREEN_DIR / "admin_screen_spec.pdf"),
|
|
115
|
+
"asset_dir": str(ASSET_DIR / "admin_screen_capture"),
|
|
116
|
+
"base_url": "http://127.0.0.1:4000",
|
|
117
|
+
"api_base": "http://127.0.0.1:8000/api/v1",
|
|
118
|
+
"storage_key": "admin.auth.token",
|
|
119
|
+
"capture_policy": DESKTOP_CAPTURE_POLICY,
|
|
120
|
+
"trim_background": False,
|
|
121
|
+
"cover_note": "첫 페이지는 admin 화면코드 인덱스다. 이후 페이지는 좌측 화면 캡처와 우측 번호형 기능 테이블을 함께 제공한다.",
|
|
122
|
+
"source_refs": [
|
|
123
|
+
{"label": "implementation", "path": "client/admin/src/app/App.tsx"},
|
|
124
|
+
{"label": "implementation", "path": "client/admin/src/pages"},
|
|
125
|
+
{"label": "capture assets", "path": "sdd/99_toolchain/01_automation/assets/admin_screen_capture"},
|
|
126
|
+
],
|
|
127
|
+
"screens": [
|
|
128
|
+
screen(
|
|
129
|
+
code="ADM-S001",
|
|
130
|
+
name="Admin Login",
|
|
131
|
+
route="/login",
|
|
132
|
+
access="public",
|
|
133
|
+
features=["AUT-F001", "AUT-F002"],
|
|
134
|
+
asset="login.png",
|
|
135
|
+
requires_auth=False,
|
|
136
|
+
callouts=[
|
|
137
|
+
((0.24, 0.34), "운영 콘솔 소개 영역", "좌측 다크 패널에서 Admin console 컨텍스트와 실로그인 인증 흐름을 설명한다."),
|
|
138
|
+
((0.79, 0.18), "Restricted access 헤더", "관리자 접근 제어 맥락과 Sign in 제목을 우측 카드 상단에 배치한다."),
|
|
139
|
+
((0.79, 0.36), "자격 증명 입력", "이메일, 비밀번호, 오류 메시지를 한 폼 안에서 처리한다."),
|
|
140
|
+
((0.79, 0.48), "Open admin console CTA", "성공 시 관리자 토큰 저장 후 운영 셸로 진입한다."),
|
|
141
|
+
],
|
|
142
|
+
),
|
|
143
|
+
screen(
|
|
144
|
+
code="ADM-S002",
|
|
145
|
+
name="Admin Dashboard",
|
|
146
|
+
route="/",
|
|
147
|
+
access="protected",
|
|
148
|
+
features=["AUT-F002", "ORD-F003"],
|
|
149
|
+
asset="dashboard.png",
|
|
150
|
+
requires_auth=True,
|
|
151
|
+
callouts=[
|
|
152
|
+
((0.08, 0.35), "좌측 운영 레일", "대시보드, 거래 관리, 고객지원으로 이동하는 관리자 전용 내비게이션 레일이다."),
|
|
153
|
+
((0.40, 0.18), "운영 요약 카드", "운영 핵심 수치를 카드 단위로 먼저 요약해 전체 상태를 빠르게 파악하게 한다."),
|
|
154
|
+
((0.37, 0.58), "거래 상태별 현황", "단계별 거래 건수를 리스트형 카드로 보여주는 메인 분석 영역이다."),
|
|
155
|
+
((0.79, 0.58), "운영 알림", "위험/일반 알림을 tone별 카드로 묶어 후속 조치를 안내한다."),
|
|
156
|
+
],
|
|
157
|
+
),
|
|
158
|
+
screen(
|
|
159
|
+
code="ADM-S003",
|
|
160
|
+
name="Admin Queue",
|
|
161
|
+
route="/queue",
|
|
162
|
+
access="protected",
|
|
163
|
+
features=["AUT-F002", "ORD-F003"],
|
|
164
|
+
asset="queue.png",
|
|
165
|
+
requires_auth=True,
|
|
166
|
+
callouts=[
|
|
167
|
+
((0.08, 0.35), "좌측 운영 레일", "현재 거래 관리 섹션 위치를 강조하면서 다른 운영 화면으로 이동할 수 있다."),
|
|
168
|
+
((0.33, 0.24), "거래관리 헤더", "현재 큐 화면 제목과 전체 거래 건수를 요약해 상단에 고정한다."),
|
|
169
|
+
((0.44, 0.60), "거래 큐 테이블", "주문 ID, 상품, 고객 정보를 기준으로 관리자 작업 대상을 표로 노출한다."),
|
|
170
|
+
((0.82, 0.60), "상태/SLA 열", "운영 우선순위 판단에 필요한 상태와 SLA 정보를 같은 행에서 확인하게 한다."),
|
|
171
|
+
],
|
|
172
|
+
),
|
|
173
|
+
screen(
|
|
174
|
+
code="ADM-S004",
|
|
175
|
+
name="Admin Support",
|
|
176
|
+
route="/support",
|
|
177
|
+
access="protected",
|
|
178
|
+
features=["AUT-F002", "SUP-F001"],
|
|
179
|
+
asset="support.png",
|
|
180
|
+
requires_auth=True,
|
|
181
|
+
callouts=[
|
|
182
|
+
((0.08, 0.35), "좌측 운영 레일", "고객지원 섹션과 다른 관리자 기능을 오가는 공통 운영 내비게이션이다."),
|
|
183
|
+
((0.37, 0.21), "FAQ 관리 헤더", "현재 화면이 FAQ 운영 surface임을 제목으로 표시한다."),
|
|
184
|
+
((0.46, 0.55), "FAQ 목록", "질문과 노출 상태를 row 단위 카드로 보여주는 읽기 모델 영역이다."),
|
|
185
|
+
((0.86, 0.09), "상단 글로벌 액션", "알림, 현재 사용자, 로그아웃 등 관리자 공통 액션을 상단 바에 유지한다."),
|
|
186
|
+
],
|
|
187
|
+
),
|
|
188
|
+
],
|
|
189
|
+
},
|
|
190
|
+
"mobile": {
|
|
191
|
+
"title": "Templates Mobile Screen Spec",
|
|
192
|
+
"service_label": "mobile",
|
|
193
|
+
"output": str(SCREEN_DIR / "mobile_screen_spec.pdf"),
|
|
194
|
+
"asset_dir": str(ASSET_DIR / "mobile_screen_capture"),
|
|
195
|
+
"base_url": "http://127.0.0.1:3002",
|
|
196
|
+
"api_base": "http://127.0.0.1:8000/api/v1",
|
|
197
|
+
"storage_key": "mobile.auth.token",
|
|
198
|
+
"capture_policy": DESKTOP_CAPTURE_POLICY,
|
|
199
|
+
"trim_background": False,
|
|
200
|
+
"cover_note": "첫 페이지는 mobile 화면코드 인덱스다. 이후 페이지는 좌측 화면 캡처와 우측 번호형 기능 테이블을 함께 제공한다.",
|
|
201
|
+
"source_refs": [
|
|
202
|
+
{"label": "implementation", "path": "client/mobile/src/app/App.tsx"},
|
|
203
|
+
{"label": "implementation", "path": "client/mobile/src/pages"},
|
|
204
|
+
{"label": "capture assets", "path": "sdd/99_toolchain/01_automation/assets/mobile_screen_capture"},
|
|
205
|
+
],
|
|
206
|
+
"screens": [
|
|
207
|
+
screen(
|
|
208
|
+
code="MOB-S001",
|
|
209
|
+
name="Mobile Login",
|
|
210
|
+
route="/login",
|
|
211
|
+
access="public",
|
|
212
|
+
features=["AUT-F001", "AUT-F002"],
|
|
213
|
+
asset="login.png",
|
|
214
|
+
requires_auth=False,
|
|
215
|
+
callouts=[
|
|
216
|
+
((0.25, 0.32), "IN 소개 영역", "상담 흐름과 운영 상태를 동시에 여는 템플릿 맥락을 좌측 hero에서 설명한다."),
|
|
217
|
+
((0.79, 0.19), "Sign in 헤더", "IN workspace 진입 목적과 operator 기본 계정을 우측 로그인 카드 상단에 표시한다."),
|
|
218
|
+
((0.79, 0.39), "자격 증명 입력", "operator@example.com 기준 로그인 입력과 오류 상태를 한 폼에서 처리한다."),
|
|
219
|
+
((0.79, 0.49), "Continue CTA", "로그인 성공 시 저장된 세션으로 IN 보호 라우트에 진입한다."),
|
|
220
|
+
],
|
|
221
|
+
),
|
|
222
|
+
screen(
|
|
223
|
+
code="MOB-S002",
|
|
224
|
+
name="Mobile Dashboard",
|
|
225
|
+
route="/",
|
|
226
|
+
access="protected",
|
|
227
|
+
features=["AUT-F002", "FUL-F001"],
|
|
228
|
+
asset="dashboard.png",
|
|
229
|
+
requires_auth=True,
|
|
230
|
+
callouts=[
|
|
231
|
+
((0.15, 0.11), "상단 IN 셸", "Overview/Fulfillment 전환, relay status pill, 사용자 정보, 로그아웃을 묶은 공통 헤더다."),
|
|
232
|
+
((0.34, 0.25), "Units in motion hero", "현재 이행 중인 unit 수와 모바일 워크스페이스 목적을 한눈에 보여주는 대형 hero section이다."),
|
|
233
|
+
((0.27, 0.48), "운영 통계 카드", "Open tasks, blocked, outbound ready 지표를 카드 묶음으로 제공한다."),
|
|
234
|
+
((0.33, 0.79), "Relay timeline", "패킹, 예외 검토, 출고 준비 이벤트를 시간축 카드로 정리한다."),
|
|
235
|
+
((0.83, 0.60), "Stage load", "단계별 작업량과 병목 구간을 우측 보조 패널에서 보여준다."),
|
|
236
|
+
],
|
|
237
|
+
),
|
|
238
|
+
screen(
|
|
239
|
+
code="MOB-S003",
|
|
240
|
+
name="Mobile Fulfillment",
|
|
241
|
+
route="/fulfillment",
|
|
242
|
+
access="protected",
|
|
243
|
+
features=["AUT-F002", "FUL-F002"],
|
|
244
|
+
asset="fulfillment.png",
|
|
245
|
+
requires_auth=True,
|
|
246
|
+
callouts=[
|
|
247
|
+
((0.15, 0.11), "상단 IN 셸", "Fulfillment 화면에서도 Overview/Fulfillment 전환과 사용자 액션이 동일한 헤더 구조를 유지한다."),
|
|
248
|
+
((0.36, 0.23), "Fulfillment board 헤더", "현재 이행 보드 화면의 제목과 task surface 설명을 상단에서 제공한다."),
|
|
249
|
+
((0.39, 0.58), "Fulfillment board 테이블", "Order, task, assignee, status, SLA를 행 단위로 정리한 메인 운영 테이블이다."),
|
|
250
|
+
((0.83, 0.59), "Fulfillment notes", "이행 메모와 운영 체크포인트를 별도 패널로 분리해 보조 컨텍스트를 제공한다."),
|
|
251
|
+
],
|
|
252
|
+
),
|
|
253
|
+
],
|
|
254
|
+
},
|
|
255
|
+
"landing": {
|
|
256
|
+
"title": "Templates Landing Screen Spec",
|
|
257
|
+
"service_label": "landing",
|
|
258
|
+
"output": str(SCREEN_DIR / "landing_screen_spec.pdf"),
|
|
259
|
+
"asset_dir": str(ASSET_DIR / "landing_screen_capture"),
|
|
260
|
+
"base_url": "http://127.0.0.1:3000",
|
|
261
|
+
"api_base": "http://127.0.0.1:8000/api/v1",
|
|
262
|
+
"storage_key": "landing.auth.token",
|
|
263
|
+
"capture_policy": DESKTOP_CAPTURE_POLICY,
|
|
264
|
+
"trim_background": False,
|
|
265
|
+
"cover_note": "첫 페이지는 landing 화면코드 인덱스다. 이후 페이지는 좌측 화면 캡처와 우측 번호형 기능 테이블을 함께 제공한다.",
|
|
266
|
+
"source_refs": [
|
|
267
|
+
{"label": "implementation", "path": "client/landing/src/App.tsx"},
|
|
268
|
+
{"label": "implementation", "path": "client/landing/src/pages"},
|
|
269
|
+
{"label": "capture assets", "path": "sdd/99_toolchain/01_automation/assets/landing_screen_capture"},
|
|
270
|
+
],
|
|
271
|
+
"screens": [
|
|
272
|
+
screen(
|
|
273
|
+
code="LND-S001",
|
|
274
|
+
name="Landing Home",
|
|
275
|
+
route="/",
|
|
276
|
+
access="public",
|
|
277
|
+
features=["CAT-F001", "AUT-F002"],
|
|
278
|
+
asset="home.png",
|
|
279
|
+
requires_auth=False,
|
|
280
|
+
callouts=[
|
|
281
|
+
((0.16, 0.05), "글로벌 헤더", "브랜드, 섹션 앵커, 로그인 또는 workspace handoff 링크를 상단 헤더에 배치한다."),
|
|
282
|
+
((0.27, 0.20), "히어로 메시지와 CTA", "서비스 가치 제안과 Sign in/See examples CTA를 첫 섹션에서 강조한다."),
|
|
283
|
+
((0.77, 0.25), "카탈로그 스냅샷", "실시간 product 수, active count, highlighted catalog 상태를 요약 카드로 보여준다."),
|
|
284
|
+
((0.30, 0.56), "Features grid", "라이브 catalog product를 카드 그리드로 노출하는 예시 섹션이다."),
|
|
285
|
+
((0.50, 0.75), "Live proof 섹션", "`/api/v1/catalog/products` 기반 sync 결과를 집계 카드와 함께 증빙한다."),
|
|
286
|
+
((0.50, 0.92), "마무리 CTA", "템플릿 시작 CTA로 로그인 또는 workspace 진입을 유도하는 하단 배너다."),
|
|
287
|
+
],
|
|
288
|
+
),
|
|
289
|
+
screen(
|
|
290
|
+
code="LND-S002",
|
|
291
|
+
name="Landing Login",
|
|
292
|
+
route="/login",
|
|
293
|
+
access="public",
|
|
294
|
+
features=["AUT-F001", "AUT-F002"],
|
|
295
|
+
asset="login.png",
|
|
296
|
+
requires_auth=False,
|
|
297
|
+
callouts=[
|
|
298
|
+
((0.24, 0.34), "랜딩 소개 패널", "로그인 후 `/workspace`로 이어지는 흐름과 landing 복귀 링크를 좌측 소개 영역에서 설명한다."),
|
|
299
|
+
((0.78, 0.20), "Sign in 헤더", "멤버 접근 맥락과 workspace 진입 목적을 우측 로그인 폼 상단에 둔다."),
|
|
300
|
+
((0.78, 0.39), "자격 증명 입력", "이메일, 비밀번호, 오류 메시지를 같은 카드 안에서 처리한다."),
|
|
301
|
+
((0.78, 0.50), "Continue CTA", "성공 시 토큰 저장 후 보호된 `/workspace`로 이동한다."),
|
|
302
|
+
],
|
|
303
|
+
),
|
|
304
|
+
screen(
|
|
305
|
+
code="LND-S003",
|
|
306
|
+
name="Landing Workspace",
|
|
307
|
+
route="/workspace",
|
|
308
|
+
access="protected",
|
|
309
|
+
features=["AUT-F002", "CAT-F001"],
|
|
310
|
+
asset="workspace.png",
|
|
311
|
+
requires_auth=True,
|
|
312
|
+
callouts=[
|
|
313
|
+
((0.16, 0.07), "멤버 셸 헤더", "Workspace 링크, 현재 사용자, 로그아웃을 포함한 보호 라우트 전용 헤더다."),
|
|
314
|
+
((0.30, 0.32), "Protected workspace 카드", "인증된 사용자 전용 shell 설명과 catalog sync 상태를 메인 카드에 배치한다."),
|
|
315
|
+
((0.77, 0.32), "Current member 카드", "현재 로그인 사용자의 email, role, status를 우측 패널에서 보여준다."),
|
|
316
|
+
((0.30, 0.78), "Workspace product grid", "보호 라우트 안에서 조회한 product 목록을 카드 그리드로 렌더링한다."),
|
|
317
|
+
],
|
|
318
|
+
),
|
|
319
|
+
],
|
|
320
|
+
},
|
|
321
|
+
}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import atexit
|
|
5
|
+
import base64
|
|
6
|
+
import importlib.util
|
|
7
|
+
import re
|
|
8
|
+
import shutil
|
|
9
|
+
import subprocess
|
|
10
|
+
import tempfile
|
|
11
|
+
from io import BytesIO
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Sequence
|
|
14
|
+
|
|
15
|
+
from PIL import Image, ImageChops
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
TEMP_DIR = Path(tempfile.mkdtemp(prefix="spec-asset-builder-"))
|
|
19
|
+
PAGE_CACHE: dict[tuple[str, int, int], Image.Image] = {}
|
|
20
|
+
SVG_IMAGE_HREF_PATTERN = re.compile(r'href="data:image/png;base64,([^"]+)"')
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def cleanup_tempdir() -> None:
|
|
24
|
+
shutil.rmtree(TEMP_DIR, ignore_errors=True)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
atexit.register(cleanup_tempdir)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def parse_args(
|
|
31
|
+
argv: Sequence[str] | None = None,
|
|
32
|
+
*,
|
|
33
|
+
default_manifest: Path | None = None,
|
|
34
|
+
default_recipes_var: str = "ASSET_RECIPES",
|
|
35
|
+
) -> argparse.Namespace:
|
|
36
|
+
parser = argparse.ArgumentParser(description="Build static design assets from a recipe manifest.")
|
|
37
|
+
parser.add_argument("--manifest", default=str(default_manifest) if default_manifest else None, help="Path to the Python recipe manifest.")
|
|
38
|
+
parser.add_argument("--recipes-var", default=default_recipes_var, help="Manifest variable name that contains the recipe list.")
|
|
39
|
+
parser.add_argument("--asset", action="append", dest="assets", help="Asset id to build. Repeatable. Default builds all assets.")
|
|
40
|
+
parser.add_argument("--list", action="store_true", help="List available asset ids and exit.")
|
|
41
|
+
parser.add_argument("--verify-exact", action="store_true", help="Verify that each generated output is pixel-identical to the resolved source crop.")
|
|
42
|
+
args = parser.parse_args(argv)
|
|
43
|
+
|
|
44
|
+
if not args.manifest:
|
|
45
|
+
parser.error("--manifest is required")
|
|
46
|
+
|
|
47
|
+
return args
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def load_manifest(manifest_path: Path, recipes_var: str) -> list[dict]:
|
|
51
|
+
spec = importlib.util.spec_from_file_location("asset_recipe_manifest", manifest_path)
|
|
52
|
+
if spec is None or spec.loader is None:
|
|
53
|
+
raise SystemExit(f"Unable to load manifest: {manifest_path}")
|
|
54
|
+
|
|
55
|
+
module = importlib.util.module_from_spec(spec)
|
|
56
|
+
spec.loader.exec_module(module)
|
|
57
|
+
recipes = getattr(module, recipes_var, None)
|
|
58
|
+
if recipes is None:
|
|
59
|
+
raise SystemExit(f"Manifest {manifest_path} does not define {recipes_var}")
|
|
60
|
+
if not isinstance(recipes, list):
|
|
61
|
+
raise SystemExit(f"{recipes_var} in {manifest_path} must be a list")
|
|
62
|
+
return recipes
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def load_pdf_page(path: Path, page: int, dpi: int) -> Image.Image:
|
|
66
|
+
key = (str(path), page, dpi)
|
|
67
|
+
cached = PAGE_CACHE.get(key)
|
|
68
|
+
if cached is not None:
|
|
69
|
+
return cached.copy()
|
|
70
|
+
|
|
71
|
+
output_prefix = TEMP_DIR / f"{path.stem}-p{page}-{dpi}"
|
|
72
|
+
subprocess.run(
|
|
73
|
+
[
|
|
74
|
+
"pdftoppm",
|
|
75
|
+
"-png",
|
|
76
|
+
"-singlefile",
|
|
77
|
+
"-f",
|
|
78
|
+
str(page),
|
|
79
|
+
"-l",
|
|
80
|
+
str(page),
|
|
81
|
+
"-r",
|
|
82
|
+
str(dpi),
|
|
83
|
+
str(path),
|
|
84
|
+
str(output_prefix),
|
|
85
|
+
],
|
|
86
|
+
capture_output=True,
|
|
87
|
+
text=True,
|
|
88
|
+
check=True,
|
|
89
|
+
)
|
|
90
|
+
image = Image.open(output_prefix.with_suffix(".png")).convert("RGBA")
|
|
91
|
+
PAGE_CACHE[key] = image.copy()
|
|
92
|
+
return image
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def load_image(path: Path) -> Image.Image:
|
|
96
|
+
return Image.open(path).convert("RGBA")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def resolve_source(source: dict) -> Image.Image:
|
|
100
|
+
kind = source["kind"]
|
|
101
|
+
if kind == "pdf_page":
|
|
102
|
+
return load_pdf_page(Path(source["path"]), int(source["page"]), int(source["dpi"]))
|
|
103
|
+
if kind == "image":
|
|
104
|
+
return load_image(Path(source["path"]))
|
|
105
|
+
raise ValueError(f"Unsupported source kind: {kind}")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def make_white_transparent(image: Image.Image, threshold: int) -> Image.Image:
|
|
109
|
+
converted = image.convert("RGBA")
|
|
110
|
+
pixels = []
|
|
111
|
+
for red, green, blue, alpha in converted.getdata():
|
|
112
|
+
if alpha and red >= threshold and green >= threshold and blue >= threshold:
|
|
113
|
+
pixels.append((255, 255, 255, 0))
|
|
114
|
+
else:
|
|
115
|
+
pixels.append((red, green, blue, alpha))
|
|
116
|
+
converted.putdata(pixels)
|
|
117
|
+
return converted
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def transform_image(image: Image.Image, recipe: dict) -> Image.Image:
|
|
121
|
+
working = image.copy()
|
|
122
|
+
|
|
123
|
+
crop_box = recipe.get("crop_box")
|
|
124
|
+
if crop_box is not None:
|
|
125
|
+
working = working.crop(tuple(crop_box))
|
|
126
|
+
|
|
127
|
+
threshold = recipe.get("transparent_white_threshold")
|
|
128
|
+
if threshold is not None:
|
|
129
|
+
working = make_white_transparent(working, int(threshold))
|
|
130
|
+
|
|
131
|
+
if recipe.get("trim"):
|
|
132
|
+
bbox = working.getbbox()
|
|
133
|
+
if not bbox:
|
|
134
|
+
raise ValueError(f"Recipe {recipe['id']} trimmed to empty image")
|
|
135
|
+
working = working.crop(bbox)
|
|
136
|
+
|
|
137
|
+
return working
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def encode_svg(image: Image.Image) -> str:
|
|
141
|
+
buffer = BytesIO()
|
|
142
|
+
image.save(buffer, format="PNG")
|
|
143
|
+
payload = base64.b64encode(buffer.getvalue()).decode("ascii")
|
|
144
|
+
return (
|
|
145
|
+
f'<svg xmlns="http://www.w3.org/2000/svg" width="{image.width}" height="{image.height}" '
|
|
146
|
+
f'viewBox="0 0 {image.width} {image.height}" fill="none" role="img" aria-hidden="true">\n'
|
|
147
|
+
f' <image width="{image.width}" height="{image.height}" href="data:image/png;base64,{payload}" />\n'
|
|
148
|
+
"</svg>\n"
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def write_output(image: Image.Image, output_path: Path, output_format: str) -> None:
|
|
153
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
154
|
+
if output_format == "svg":
|
|
155
|
+
output_path.write_text(encode_svg(image), encoding="utf-8")
|
|
156
|
+
return
|
|
157
|
+
if output_format == "png":
|
|
158
|
+
image.save(output_path, format="PNG")
|
|
159
|
+
return
|
|
160
|
+
raise ValueError(f"Unsupported output format: {output_format}")
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def read_output_image(output_path: Path, output_format: str) -> Image.Image:
|
|
164
|
+
if output_format == "png":
|
|
165
|
+
return Image.open(output_path).convert("RGBA")
|
|
166
|
+
if output_format == "svg":
|
|
167
|
+
raw = output_path.read_text(encoding="utf-8")
|
|
168
|
+
matched = SVG_IMAGE_HREF_PATTERN.search(raw)
|
|
169
|
+
if not matched:
|
|
170
|
+
raise ValueError(f"Unable to decode embedded PNG from {output_path}")
|
|
171
|
+
buffer = BytesIO(base64.b64decode(matched.group(1)))
|
|
172
|
+
return Image.open(buffer).convert("RGBA")
|
|
173
|
+
raise ValueError(f"Unsupported output format: {output_format}")
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def verify_exact_output(output_path: Path, output_format: str, expected_image: Image.Image) -> None:
|
|
177
|
+
actual = read_output_image(output_path, output_format)
|
|
178
|
+
expected = expected_image.convert("RGBA")
|
|
179
|
+
|
|
180
|
+
if actual.size != expected.size:
|
|
181
|
+
raise SystemExit(f"Exact verification failed for {output_path}: size mismatch {actual.size} != {expected.size}")
|
|
182
|
+
|
|
183
|
+
if ImageChops.difference(actual, expected).getbbox() is not None:
|
|
184
|
+
raise SystemExit(f"Exact verification failed for {output_path}: pixel mismatch")
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def infer_output_format(recipe: dict, output_path: Path) -> str:
|
|
188
|
+
if recipe.get("output_format"):
|
|
189
|
+
return str(recipe["output_format"])
|
|
190
|
+
if output_path.suffix.lower() == ".png":
|
|
191
|
+
return "png"
|
|
192
|
+
return "svg"
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def emit_recipe(
|
|
196
|
+
recipe: dict,
|
|
197
|
+
*,
|
|
198
|
+
source_image: Image.Image | None = None,
|
|
199
|
+
selected_assets: set[str] | None = None,
|
|
200
|
+
verify_exact: bool = False,
|
|
201
|
+
) -> list[Path]:
|
|
202
|
+
source = source_image if source_image is not None else resolve_source(recipe["source"])
|
|
203
|
+
rendered = transform_image(source, recipe)
|
|
204
|
+
written: list[Path] = []
|
|
205
|
+
|
|
206
|
+
output = recipe.get("output")
|
|
207
|
+
if output is not None and (selected_assets is None or recipe["id"] in selected_assets):
|
|
208
|
+
output_path = Path(output)
|
|
209
|
+
output_format = infer_output_format(recipe, output_path)
|
|
210
|
+
write_output(rendered, output_path, output_format)
|
|
211
|
+
if verify_exact:
|
|
212
|
+
verify_exact_output(output_path, output_format, rendered)
|
|
213
|
+
written.append(output_path)
|
|
214
|
+
|
|
215
|
+
for child in recipe.get("children", []):
|
|
216
|
+
written.extend(
|
|
217
|
+
emit_recipe(
|
|
218
|
+
child,
|
|
219
|
+
source_image=rendered,
|
|
220
|
+
selected_assets=selected_assets,
|
|
221
|
+
verify_exact=verify_exact,
|
|
222
|
+
)
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
return written
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def collect_asset_ids(recipes: list[dict]) -> list[str]:
|
|
229
|
+
asset_ids: list[str] = []
|
|
230
|
+
for recipe in recipes:
|
|
231
|
+
if recipe.get("output") is not None:
|
|
232
|
+
asset_ids.append(recipe["id"])
|
|
233
|
+
asset_ids.extend(collect_asset_ids(recipe.get("children", [])))
|
|
234
|
+
return asset_ids
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def main(
|
|
238
|
+
argv: Sequence[str] | None = None,
|
|
239
|
+
*,
|
|
240
|
+
default_manifest: Path | None = None,
|
|
241
|
+
default_recipes_var: str = "ASSET_RECIPES",
|
|
242
|
+
) -> None:
|
|
243
|
+
args = parse_args(argv, default_manifest=default_manifest, default_recipes_var=default_recipes_var)
|
|
244
|
+
manifest_path = Path(args.manifest).resolve()
|
|
245
|
+
recipes = load_manifest(manifest_path, args.recipes_var)
|
|
246
|
+
asset_ids = collect_asset_ids(recipes)
|
|
247
|
+
|
|
248
|
+
if args.list:
|
|
249
|
+
for asset_id in asset_ids:
|
|
250
|
+
print(asset_id)
|
|
251
|
+
return
|
|
252
|
+
|
|
253
|
+
selected_assets = set(args.assets) if args.assets else None
|
|
254
|
+
if selected_assets is not None:
|
|
255
|
+
unknown = sorted(selected_assets.difference(asset_ids))
|
|
256
|
+
if unknown:
|
|
257
|
+
raise SystemExit(f"Unknown asset ids: {', '.join(unknown)}")
|
|
258
|
+
|
|
259
|
+
written: list[Path] = []
|
|
260
|
+
for recipe in recipes:
|
|
261
|
+
written.extend(
|
|
262
|
+
emit_recipe(
|
|
263
|
+
recipe,
|
|
264
|
+
selected_assets=selected_assets,
|
|
265
|
+
verify_exact=args.verify_exact,
|
|
266
|
+
)
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
for path in written:
|
|
270
|
+
print(path)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
if __name__ == "__main__":
|
|
274
|
+
main()
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# UI Contract Projection
|
|
2
|
+
|
|
3
|
+
`UI Contract Projection`은 화면설계서, 계약 문서, reference asset을 제품 UI 구조 위로 투영하고 검증하는 템플릿/스킬 계층의 공통 명칭이다.
|
|
4
|
+
|
|
5
|
+
## Canonical Naming
|
|
6
|
+
|
|
7
|
+
- capability: `UI Contract Projection`
|
|
8
|
+
- modes:
|
|
9
|
+
- `strict projection`
|
|
10
|
+
- `soft projection`
|
|
11
|
+
- common actions:
|
|
12
|
+
- `project`
|
|
13
|
+
- `verify`
|
|
14
|
+
- `report`
|
|
15
|
+
|
|
16
|
+
## Ownership Boundary
|
|
17
|
+
|
|
18
|
+
- 이 문서는 Claude/Codex 스킬과 CLI 래퍼가 공유할 일반 템플릿 계약만 정의한다.
|
|
19
|
+
- 제품별 screen registry, contract rows, evidence 경로는 각 제품 저장소의 `sdd`에서 확장한다.
|
|
20
|
+
- 제품 런타임 route, auth, layout은 이 템플릿을 이유로 분기하지 않는다.
|
|
21
|
+
|
|
22
|
+
## Skill Prompt Skeleton
|
|
23
|
+
|
|
24
|
+
```md
|
|
25
|
+
Goal: run UI Contract Projection for {product} {screen_or_flow}
|
|
26
|
+
Mode: {strict projection | soft projection}
|
|
27
|
+
Inputs:
|
|
28
|
+
- contract: {path}
|
|
29
|
+
- source artifact: {pdf|figma export|reference image|yaml}
|
|
30
|
+
- product target: {repo path or route}
|
|
31
|
+
|
|
32
|
+
Rules:
|
|
33
|
+
- Preserve product runtime behavior.
|
|
34
|
+
- Use React + shadcn/ui primitives and CSS token surface first.
|
|
35
|
+
- Keep projection assets in sdd/toolchain only.
|
|
36
|
+
- Do not add spec/proof/parity-only branches to runtime.
|
|
37
|
+
|
|
38
|
+
Outputs:
|
|
39
|
+
- updated product UI
|
|
40
|
+
- updated sdd contract/evidence
|
|
41
|
+
- projection report
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## CLI Contract Template
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
ui-contract-projection project \
|
|
48
|
+
--mode strict \
|
|
49
|
+
--contract <path> \
|
|
50
|
+
--source <path-or-id> \
|
|
51
|
+
--target <repo-or-route>
|
|
52
|
+
|
|
53
|
+
ui-contract-projection verify \
|
|
54
|
+
--mode strict \
|
|
55
|
+
--contract <path> \
|
|
56
|
+
--target <url-or-route> \
|
|
57
|
+
--evidence-root <path>
|
|
58
|
+
|
|
59
|
+
ui-contract-projection report \
|
|
60
|
+
--contract <path> \
|
|
61
|
+
--evidence-root <path> \
|
|
62
|
+
--format markdown
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Required Gates
|
|
66
|
+
|
|
67
|
+
1. `Structure`
|
|
68
|
+
2. `Behavior`
|
|
69
|
+
3. `Data`
|
|
70
|
+
4. `UI Contract Projection`
|
|
71
|
+
|
|
72
|
+
`strict projection`은 앞선 세 gate를 통과한 뒤에만 사용한다.
|
|
73
|
+
|
|
74
|
+
## Frontend Defaults
|
|
75
|
+
|
|
76
|
+
- `React + shadcn/ui`
|
|
77
|
+
- CSS token surface
|
|
78
|
+
- style expansion via `scss`, `styled-components`, or equivalent modern layer
|
|
79
|
+
- shared primitives before screen-specific markup
|