@geraldmaron/construct 1.1.1 → 1.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/README.md +30 -19
- package/bin/construct +487 -87
- package/bin/construct-postinstall.mjs +30 -2
- package/examples/distribution/README.md +42 -0
- package/examples/distribution/manifest.json +53 -0
- package/examples/distribution/sources/adr.md +84 -0
- package/examples/distribution/sources/deck-one-pager.md +65 -0
- package/examples/distribution/sources/prd-platform.md +161 -0
- package/examples/distribution/sources/research-brief.md +80 -0
- package/examples/distribution/sources/rfc-platform.md +82 -0
- package/examples/distribution/sources/runbook.md +103 -0
- package/examples/distribution/sources/strategy.md +88 -0
- package/lib/adapters-sync.mjs +67 -0
- package/lib/artifact-gate-notice.mjs +38 -0
- package/lib/artifact-manifest.mjs +258 -0
- package/lib/artifact-release-gate.mjs +171 -0
- package/lib/artifact-reviewers.mjs +68 -0
- package/lib/artifact-type-from-path.mjs +79 -0
- package/lib/artifact-workflow.mjs +252 -0
- package/lib/audit-skills.mjs +4 -0
- package/lib/audit-specialists.mjs +285 -0
- package/lib/audit-trail.mjs +1 -1
- package/lib/auto-docs.mjs +90 -19
- package/lib/beads/drift.mjs +27 -6
- package/lib/beads-client.mjs +49 -121
- package/lib/beads-optimistic.mjs +5 -12
- package/lib/brand-fonts.mjs +93 -0
- package/lib/brand-prose.mjs +214 -0
- package/lib/brand-tokens.mjs +92 -0
- package/lib/bridges/copilot-proxy.mjs +13 -26
- package/lib/capability-ledger.mjs +156 -0
- package/lib/certification/artifact-fixtures.mjs +132 -0
- package/lib/certification/artifact-gates.mjs +63 -0
- package/lib/certification/artifact-provenance.mjs +97 -0
- package/lib/certification/canonical-scenarios.mjs +78 -0
- package/lib/certification/cli.mjs +191 -0
- package/lib/certification/dashboard-api.mjs +71 -0
- package/lib/certification/demo-parity.mjs +116 -0
- package/lib/certification/document-io-fixtures.mjs +246 -0
- package/lib/certification/document-workflow.mjs +97 -0
- package/lib/certification/eval-bridge.mjs +77 -0
- package/lib/certification/model-routing.mjs +149 -0
- package/lib/certification/prompt-budget.mjs +119 -0
- package/lib/certification/rc-gate.mjs +206 -0
- package/lib/certification/real-llm-scenarios.mjs +303 -0
- package/lib/certification/role-cards.mjs +113 -0
- package/lib/certification/role-overlays.mjs +117 -0
- package/lib/certification/run.mjs +122 -0
- package/lib/certification/runner.mjs +323 -0
- package/lib/certification/scenarios.mjs +51 -0
- package/lib/certification/skill-inventory.mjs +289 -0
- package/lib/certification/skill-scenarios.mjs +147 -0
- package/lib/certification/specialist-contracts.mjs +85 -0
- package/lib/certification/specialist-scenarios.mjs +175 -0
- package/lib/certification/stale-impact.mjs +146 -0
- package/lib/certification/status.mjs +252 -0
- package/lib/certification/store.mjs +77 -0
- package/lib/chat/cli.mjs +333 -0
- package/lib/chat/command-suggest.mjs +161 -0
- package/lib/chat/commands.mjs +215 -0
- package/lib/chat/config.mjs +142 -0
- package/lib/chat/context-compactor.mjs +250 -0
- package/lib/chat/context-continuation.mjs +253 -0
- package/lib/chat/continuation-source.mjs +58 -0
- package/lib/chat/demo-guide.mjs +61 -0
- package/lib/chat/design-tokens.mjs +91 -0
- package/lib/chat/desktop-binary.mjs +79 -0
- package/lib/chat/desktop-build.mjs +130 -0
- package/lib/chat/desktop-launcher.mjs +133 -0
- package/lib/chat/evidence.mjs +145 -0
- package/lib/chat/export.mjs +74 -0
- package/lib/chat/harness/driver.mjs +91 -0
- package/lib/chat/list-picker.mjs +112 -0
- package/lib/chat/model-picker.mjs +356 -0
- package/lib/chat/openrouter-fallback.mjs +151 -0
- package/lib/chat/permission-prompt.mjs +33 -0
- package/lib/chat/picker-catalog.mjs +45 -0
- package/lib/chat/policy-telemetry.mjs +34 -0
- package/lib/chat/present.mjs +246 -0
- package/lib/chat/session-context.mjs +39 -0
- package/lib/chat/session-persist.mjs +73 -0
- package/lib/chat/session-restore.mjs +71 -0
- package/lib/chat/session-settings.mjs +53 -0
- package/lib/chat/system-prompt.mjs +52 -0
- package/lib/chat/transparency.mjs +93 -0
- package/lib/chat/tui/color-scheme.mjs +42 -0
- package/lib/chat/tui/markdown.mjs +123 -0
- package/lib/chat/tui/presentation.mjs +100 -0
- package/lib/chat/tui/render.mjs +500 -0
- package/lib/chat/tui/turn-block.mjs +284 -0
- package/lib/chat/tui/turn-present.mjs +18 -0
- package/lib/chat/tui/turn-state.mjs +88 -0
- package/lib/chat/tui/usage.mjs +122 -0
- package/lib/chat/web-commands.mjs +146 -0
- package/lib/chat/web-launcher.mjs +63 -0
- package/lib/chat/web-picker-keys.mjs +46 -0
- package/lib/chat/web-session.mjs +159 -0
- package/lib/cli-commands.mjs +146 -15
- package/lib/cli-service-inventory.mjs +42 -0
- package/lib/comment-lint.mjs +6 -4
- package/lib/config/intake-policy.mjs +209 -0
- package/lib/config/project-config.mjs +11 -2
- package/lib/config/schema.mjs +86 -6
- package/lib/config/source-targets.mjs +311 -0
- package/lib/contract-schemas/decision.json +50 -0
- package/lib/contract-schemas/implementation.json +51 -0
- package/lib/contract-schemas/review-report.json +32 -0
- package/lib/contract-schemas/test-report.json +43 -0
- package/lib/contracts/construct-handoff.mjs +60 -0
- package/lib/contracts/validate.mjs +32 -3
- package/lib/contracts/violation-log.mjs +58 -5
- package/lib/dashboard-demo.mjs +71 -0
- package/lib/dashboard-static.mjs +19 -5
- package/lib/decisions/registry.mjs +5 -3
- package/lib/deck-export-pptx.mjs +1152 -0
- package/lib/demo-recording.mjs +142 -0
- package/lib/demo-script.mjs +114 -0
- package/lib/demo-surface.mjs +249 -0
- package/lib/demo.mjs +598 -140
- package/lib/diagram-export.mjs +192 -0
- package/lib/diagram.mjs +13 -11
- package/lib/docs-verify.mjs +25 -7
- package/lib/doctor/index.mjs +3 -1
- package/lib/doctor/project-adapters.mjs +44 -0
- package/lib/doctor/source-checkout.mjs +16 -0
- package/lib/doctor/watchers/cx-budget.mjs +98 -0
- package/lib/doctor/watchers/graph-staleness.mjs +52 -0
- package/lib/doctor/watchers/process-pressure.mjs +4 -3
- package/lib/document-export.mjs +384 -38
- package/lib/document-extract.mjs +44 -4
- package/lib/document-ingest.mjs +29 -20
- package/lib/embed/auto-sources.mjs +44 -0
- package/lib/embed/cli.mjs +4 -3
- package/lib/embed/daemon.mjs +18 -46
- package/lib/embed/demand-fetch.mjs +50 -19
- package/lib/embed/inbox.mjs +6 -10
- package/lib/embed/worker.mjs +4 -3
- package/lib/embedded-contract/index.mjs +11 -0
- package/lib/embedded-contract/model-resolve.mjs +10 -9
- package/lib/env-config.mjs +26 -0
- package/lib/evals/dataset.mjs +137 -0
- package/lib/evals/gates.mjs +175 -0
- package/lib/export-branding.mjs +32 -0
- package/lib/graph/build-co-change.mjs +54 -0
- package/lib/graph/build-from-registry.mjs +136 -0
- package/lib/graph/build-import-graph.mjs +153 -0
- package/lib/graph/cli.mjs +110 -0
- package/lib/graph/impact-cli.mjs +89 -0
- package/lib/graph/impact.mjs +108 -0
- package/lib/graph/staleness.mjs +44 -0
- package/lib/graph/store.mjs +172 -0
- package/lib/health-check.mjs +90 -76
- package/lib/hooks/artifact-release-gate.mjs +43 -0
- package/lib/hooks/brand-prose-lint.mjs +38 -0
- package/lib/hooks/graph-impact-advisory.mjs +62 -0
- package/lib/hooks/session-optimize.mjs +84 -207
- package/lib/hooks/session-start.mjs +18 -16
- package/lib/host-disposition.mjs +7 -0
- package/lib/improvement/cli.mjs +189 -0
- package/lib/improvement/controller.mjs +137 -0
- package/lib/improvement/proposal.mjs +120 -0
- package/lib/improvement/specialist-loop.mjs +192 -0
- package/lib/improvement/store.mjs +89 -0
- package/lib/improvement/surface.mjs +219 -0
- package/lib/ingest-tooling.mjs +97 -0
- package/lib/init/doc-lanes.mjs +165 -0
- package/lib/init-docs.mjs +31 -178
- package/lib/init-unified.mjs +50 -119
- package/lib/init-update-guide.mjs +102 -0
- package/lib/init-update.mjs +32 -1
- package/lib/init.mjs +8 -0
- package/lib/install/desktop-binary-download.mjs +85 -0
- package/lib/install/legacy-global-cleanup.mjs +189 -0
- package/lib/intake/constants.mjs +29 -0
- package/lib/intake/daemon.mjs +2 -0
- package/lib/intake/git-queue.mjs +9 -8
- package/lib/intake/intake-config.mjs +52 -105
- package/lib/intake/legacy-paths.mjs +5 -0
- package/lib/intake/queue.mjs +2 -1
- package/lib/intake/session-prelude.mjs +90 -1
- package/lib/libreoffice-export.mjs +97 -0
- package/lib/logging/rotate.mjs +9 -0
- package/lib/maintenance/docker-reclaim.mjs +206 -0
- package/lib/mcp/external-schema-cost.mjs +74 -0
- package/lib/mcp/server.mjs +103 -10
- package/lib/mcp/stdio-mcp-probe.mjs +188 -0
- package/lib/mcp/tool-budget.mjs +55 -4
- package/lib/mcp/tools/document.mjs +18 -4
- package/lib/mcp/tools/embedded-contract.mjs +14 -0
- package/lib/mcp/tools/skills.mjs +32 -3
- package/lib/mcp/tools/workflow.mjs +13 -1
- package/lib/model-free-selector.mjs +18 -0
- package/lib/model-registry.mjs +13 -229
- package/lib/model-router.mjs +373 -92
- package/lib/models/behavior-matrix.mjs +289 -0
- package/lib/models/catalog.mjs +209 -0
- package/lib/models/execution-capability-profile.mjs +196 -0
- package/lib/models/execution-policy.mjs +307 -0
- package/lib/models/provider-poll.mjs +383 -0
- package/lib/npm-spawn-env.mjs +17 -0
- package/lib/ollama/installed-models.mjs +129 -0
- package/lib/oracle/actions.mjs +187 -0
- package/lib/oracle/artifact-gate.mjs +99 -0
- package/lib/oracle/cli.mjs +204 -0
- package/lib/oracle/daemon-entry.mjs +14 -0
- package/lib/oracle/dispatch.mjs +81 -0
- package/lib/oracle/execute.mjs +143 -0
- package/lib/oracle/gaps.mjs +76 -0
- package/lib/oracle/index.mjs +84 -0
- package/lib/oracle/issues.mjs +164 -0
- package/lib/oracle/org-graph.mjs +170 -0
- package/lib/oracle/policy.mjs +80 -0
- package/lib/oracle/read-model.mjs +398 -0
- package/lib/oracle/reconcile.mjs +191 -0
- package/lib/oracle/routing.mjs +89 -0
- package/lib/oracle/synthesize.mjs +384 -0
- package/lib/oracle/verdicts.mjs +51 -0
- package/lib/orchestration/worker.mjs +88 -27
- package/lib/orchestration-policy.mjs +50 -0
- package/lib/parity.mjs +96 -2
- package/lib/playwright-demo.mjs +254 -0
- package/lib/project-init-shared.mjs +4 -1
- package/lib/prompt-composer.js +7 -3
- package/lib/prompt-validation-contract.mjs +24 -0
- package/lib/provider-capabilities.js +57 -11
- package/lib/providers/contract/adapters/confluence/index.mjs +181 -0
- package/lib/providers/contract/adapters/git/index.mjs +115 -0
- package/lib/providers/contract/adapters/github/index.mjs +166 -0
- package/lib/providers/contract/adapters/jira/index.mjs +187 -0
- package/lib/providers/contract/adapters/slack/index.mjs +175 -0
- package/lib/providers/contract/contract-tests.mjs +57 -0
- package/lib/providers/contract/errors.mjs +48 -0
- package/lib/providers/contract/interface.mjs +50 -0
- package/lib/providers/contract/registry.mjs +102 -0
- package/lib/providers/copilot-auth.mjs +297 -0
- package/lib/providers/credential-bootstrap.mjs +178 -0
- package/lib/providers/credential-catalog.mjs +46 -0
- package/lib/providers/credential-sources.mjs +63 -0
- package/lib/providers/creds.mjs +5 -2
- package/lib/providers/op-run.mjs +59 -0
- package/lib/providers/secret-resolver.mjs +159 -0
- package/lib/publish-template.mjs +163 -0
- package/lib/publish-tooling.mjs +119 -0
- package/lib/publish.mjs +305 -0
- package/lib/registry/cli.mjs +82 -0
- package/lib/registry/consolidation.mjs +147 -0
- package/lib/registry/generate-docs.mjs +75 -0
- package/lib/registry/skill-verification.mjs +57 -0
- package/lib/registry/surface-map.mjs +76 -0
- package/lib/registry/validate.mjs +135 -0
- package/lib/resources/budget.mjs +82 -0
- package/lib/resources/process-budget.mjs +45 -0
- package/lib/rules-delivery.mjs +9 -2
- package/lib/rules-read.mjs +26 -0
- package/lib/runtime-env.mjs +23 -0
- package/lib/runtime-pressure.mjs +51 -7
- package/lib/schema-infer.mjs +13 -2
- package/lib/server/chat-loop.mjs +622 -0
- package/lib/server/demo-preview.mjs +63 -0
- package/lib/server/index.mjs +259 -9
- package/lib/server/rate-limit.mjs +5 -3
- package/lib/server/webhook.mjs +3 -2
- package/lib/service-manager.mjs +60 -8
- package/lib/setup.mjs +70 -7
- package/lib/specialists/prompt-schema.mjs +19 -10
- package/lib/specialists/roster.mjs +43 -0
- package/lib/specialists/scaffold.mjs +56 -0
- package/lib/storage/backend.mjs +6 -6
- package/lib/storage/hybrid-query.mjs +7 -4
- package/lib/storage/state-source.mjs +5 -5
- package/lib/storage/sync.mjs +2 -3
- package/lib/telemetry/rule-calls.mjs +52 -0
- package/lib/template-registry.mjs +2 -2
- package/lib/templates/visual-requirements.mjs +27 -51
- package/lib/test-corpus-inventory.mjs +313 -0
- package/lib/uninstall/uninstall.mjs +19 -7
- package/lib/update.mjs +12 -0
- package/lib/upgrade.mjs +14 -0
- package/lib/wireframe.mjs +20 -14
- package/lib/worker/run.mjs +17 -4
- package/lib/worker/trace.mjs +11 -3
- package/package.json +28 -14
- package/personas/construct.md +2 -2
- package/platforms/claude/settings.template.json +36 -0
- package/rules/common/patterns.md +1 -1
- package/rules/common/release-gates.md +4 -3
- package/scripts/sync-specialists.mjs +41 -28
- package/skills/devops/data-engineering.md +1 -1
- package/skills/docs/adr-workflow.md +1 -0
- package/skills/docs/backlog-proposal-workflow.md +1 -0
- package/skills/docs/codebase-research-workflow.md +40 -0
- package/skills/docs/customer-profile-workflow.md +1 -0
- package/skills/docs/document-ingest-workflow.md +1 -0
- package/skills/docs/evidence-ingest-workflow.md +1 -0
- package/skills/docs/init-docs.md +2 -2
- package/skills/docs/init-project.md +7 -2
- package/skills/docs/prd-workflow.md +24 -1
- package/skills/docs/prfaq-workflow.md +1 -0
- package/skills/docs/product-intelligence-workflow.md +1 -0
- package/skills/docs/product-signal-workflow.md +1 -0
- package/skills/docs/research-workflow.md +54 -37
- package/skills/docs/runbook-workflow.md +1 -0
- package/skills/docs/strategy-workflow.md +1 -0
- package/skills/docs/user-research-workflow.md +40 -0
- package/skills/operating/orchestration-reference.md +1 -1
- package/skills/roles/architect.md +5 -0
- package/skills/roles/operator.docs.md +4 -0
- package/skills/routing.md +4 -2
- package/specialists/artifact-manifest.json +489 -0
- package/specialists/artifact-manifest.schema.json +83 -0
- package/specialists/audit-enrichments.json +454 -0
- package/specialists/contracts.json +37 -25
- package/specialists/prompts/_shared/validation-contract.md +26 -0
- package/specialists/prompts/cx-accessibility.md +21 -2
- package/specialists/prompts/cx-ai-engineer.md +24 -1
- package/specialists/prompts/cx-architect.md +4 -1
- package/specialists/prompts/cx-business-strategist.md +23 -2
- package/specialists/prompts/cx-data-analyst.md +22 -1
- package/specialists/prompts/cx-data-engineer.md +23 -2
- package/specialists/prompts/cx-debugger.md +21 -2
- package/specialists/prompts/cx-designer.md +26 -3
- package/specialists/prompts/cx-devil-advocate.md +19 -2
- package/specialists/prompts/cx-docs-keeper.md +28 -1
- package/specialists/prompts/cx-engineer.md +22 -1
- package/specialists/prompts/cx-evaluator.md +18 -1
- package/specialists/prompts/cx-explorer.md +21 -2
- package/specialists/prompts/cx-legal-compliance.md +21 -2
- package/specialists/prompts/cx-operations.md +21 -2
- package/specialists/prompts/cx-oracle.md +94 -0
- package/specialists/prompts/cx-orchestrator.md +20 -1
- package/specialists/prompts/cx-platform-engineer.md +25 -2
- package/specialists/prompts/cx-product-manager.md +32 -1
- package/specialists/prompts/cx-qa.md +24 -2
- package/specialists/prompts/cx-rd-lead.md +23 -2
- package/specialists/prompts/cx-release-manager.md +23 -2
- package/specialists/prompts/cx-researcher.md +22 -2
- package/specialists/prompts/cx-reviewer.md +18 -1
- package/specialists/prompts/cx-security.md +22 -2
- package/specialists/prompts/cx-sre.md +30 -3
- package/specialists/prompts/cx-test-automation.md +7 -1
- package/specialists/prompts/cx-trace-reviewer.md +22 -3
- package/specialists/prompts/cx-ux-researcher.md +21 -2
- package/specialists/registry.json +52 -173
- package/specialists/tone-profiles.json +42 -0
- package/templates/demos/playwright/demo-recording.config.mjs +47 -0
- package/templates/demos/recordings/agentic-platforms-prd.json +19 -0
- package/templates/demos/scripts/agentic-platforms-prd.json +44 -0
- package/templates/demos/specs/_helpers/scroll-artifact.ts +89 -0
- package/templates/demos/tapes/agentic-platforms-prd.tape +49 -0
- package/templates/demos/tapes/resource-guard-rails.tape +49 -0
- package/templates/demos/vhs/construct-cockpit.json +24 -0
- package/templates/distribution/construct-brand.typ +446 -0
- package/templates/distribution/construct-decision.typ +38 -0
- package/templates/distribution/construct-deck.html +95 -0
- package/templates/distribution/construct-pdf.typ +38 -0
- package/templates/distribution/construct-prd.typ +38 -0
- package/templates/distribution/construct-reference.docx +0 -0
- package/templates/distribution/construct-research.typ +38 -0
- package/templates/distribution/construct-web.html +92 -0
- package/templates/distribution/fonts/Geist-Bold.ttf +0 -0
- package/templates/distribution/fonts/Geist-Medium.ttf +0 -0
- package/templates/distribution/fonts/Geist-Regular.ttf +0 -0
- package/templates/distribution/fonts/Geist-SemiBold.ttf +0 -0
- package/templates/distribution/fonts/GeistMono-Medium.ttf +0 -0
- package/templates/distribution/fonts/GeistMono-Regular.ttf +0 -0
- package/templates/distribution/fonts/GeistMono-SemiBold.ttf +0 -0
- package/templates/distribution/fonts/IBMPlexMono-Regular.otf +0 -0
- package/templates/distribution/fonts/JetBrainsMono-Medium.ttf +0 -0
- package/templates/distribution/fonts/JetBrainsMono-Regular.ttf +0 -0
- package/templates/distribution/fonts/JetBrainsMono-SemiBold.ttf +0 -0
- package/templates/distribution/fonts/PlusJakartaSans-Bold.ttf +0 -0
- package/templates/distribution/fonts/PlusJakartaSans-Medium.ttf +0 -0
- package/templates/distribution/fonts/PlusJakartaSans-Regular.ttf +0 -0
- package/templates/distribution/fonts/PlusJakartaSans-SemiBold.ttf +0 -0
- package/templates/distribution/fonts/README.md +36 -0
- package/templates/distribution/fonts/SpaceGrotesk-Variable.ttf +0 -0
- package/templates/distribution/fonts/handwritten/Caveat.ttf +0 -0
- package/templates/distribution/fonts/legacy/IBMPlexMono-Regular.otf +0 -0
- package/templates/distribution/fonts/legacy/Inter-Medium.otf +0 -0
- package/templates/distribution/fonts/legacy/Inter-Regular.otf +0 -0
- package/templates/distribution/fonts/legacy/Inter-SemiBold.otf +0 -0
- package/templates/distribution/fonts/legacy/InterDisplay-SemiBold.otf +0 -0
- package/templates/distribution/fonts/legacy/SourceSerif4-Regular.otf +0 -0
- package/templates/distribution/fonts/legacy/SourceSerif4-Semibold.otf +0 -0
- package/templates/distribution/icons/activity.svg +15 -0
- package/templates/distribution/icons/alert-triangle.svg +17 -0
- package/templates/distribution/icons/book-open.svg +16 -0
- package/templates/distribution/icons/bot.svg +20 -0
- package/templates/distribution/icons/brain.svg +22 -0
- package/templates/distribution/icons/circle-check.svg +16 -0
- package/templates/distribution/icons/clipboard-check.svg +17 -0
- package/templates/distribution/icons/cpu.svg +28 -0
- package/templates/distribution/icons/database.svg +17 -0
- package/templates/distribution/icons/eye.svg +16 -0
- package/templates/distribution/icons/file-text.svg +19 -0
- package/templates/distribution/icons/gauge.svg +16 -0
- package/templates/distribution/icons/git-branch.svg +17 -0
- package/templates/distribution/icons/key.svg +17 -0
- package/templates/distribution/icons/layers.svg +17 -0
- package/templates/distribution/icons/list-checks.svg +19 -0
- package/templates/distribution/icons/lock.svg +16 -0
- package/templates/distribution/icons/message-square.svg +15 -0
- package/templates/distribution/icons/network.svg +19 -0
- package/templates/distribution/icons/route.svg +17 -0
- package/templates/distribution/icons/scale.svg +19 -0
- package/templates/distribution/icons/search.svg +16 -0
- package/templates/distribution/icons/send.svg +16 -0
- package/templates/distribution/icons/server.svg +18 -0
- package/templates/distribution/icons/shield-check.svg +16 -0
- package/templates/distribution/icons/users.svg +18 -0
- package/templates/distribution/icons/webhook.svg +17 -0
- package/templates/distribution/icons/workflow.svg +17 -0
- package/templates/distribution/icons/wrench.svg +15 -0
- package/templates/distribution/run.mjs +18 -1
- package/templates/docs/adr.md +7 -0
- package/templates/docs/construct_guide.md +8 -8
- package/templates/docs/customer-profile.md +4 -0
- package/templates/docs/one-pager.md +4 -0
- package/templates/docs/postmortem.md +31 -0
- package/templates/docs/prd-platform.md +19 -0
- package/templates/docs/prd.md +15 -0
- package/templates/docs/prfaq.md +7 -0
- package/templates/docs/qa-strategy.md +103 -0
- package/templates/docs/research-brief.md +8 -0
- package/templates/docs/rfc-platform.md +12 -0
- package/templates/docs/test-plan.md +7 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "Decision",
|
|
4
|
+
"description": "Output from cx-architect: a documented architectural or design decision",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["type", "goal", "approach", "tasks", "risks", "acceptanceCriteria"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"type": { "type": "string", "enum": ["decision"] },
|
|
9
|
+
"goal": { "type": "string", "description": "Objective restated in one sentence" },
|
|
10
|
+
"approach": { "type": "string", "description": "Chosen strategy and rationale" },
|
|
11
|
+
"alternativesConsidered": {
|
|
12
|
+
"type": "array",
|
|
13
|
+
"items": { "type": "string" }
|
|
14
|
+
},
|
|
15
|
+
"tasks": {
|
|
16
|
+
"type": "array",
|
|
17
|
+
"items": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"required": ["description", "owner", "complexity"],
|
|
20
|
+
"properties": {
|
|
21
|
+
"description": { "type": "string" },
|
|
22
|
+
"owner": { "type": "string" },
|
|
23
|
+
"complexity": { "type": "string", "enum": ["S", "M", "L"] }
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"risks": {
|
|
28
|
+
"type": "array",
|
|
29
|
+
"items": {
|
|
30
|
+
"type": "object",
|
|
31
|
+
"required": ["description", "mitigation"],
|
|
32
|
+
"properties": {
|
|
33
|
+
"description": { "type": "string" },
|
|
34
|
+
"mitigation": { "type": "string" }
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"acceptanceCriteria": {
|
|
39
|
+
"type": "array",
|
|
40
|
+
"items": { "type": "string" },
|
|
41
|
+
"minItems": 1
|
|
42
|
+
},
|
|
43
|
+
"doNotTouch": {
|
|
44
|
+
"type": "array",
|
|
45
|
+
"items": { "type": "string" },
|
|
46
|
+
"description": "Files or systems that must stay unchanged"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"additionalProperties": false
|
|
50
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "Implementation",
|
|
4
|
+
"description": "Output from cx-engineer: summary of changes made",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["type", "status", "filesChanged", "verificationChecklist"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"type": { "type": "string", "enum": ["implementation"] },
|
|
9
|
+
"status": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"enum": ["DONE", "BLOCKED", "NEEDS_MAIN_INPUT"]
|
|
12
|
+
},
|
|
13
|
+
"filesChanged": {
|
|
14
|
+
"type": "array",
|
|
15
|
+
"items": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"required": ["path", "action"],
|
|
18
|
+
"properties": {
|
|
19
|
+
"path": { "type": "string" },
|
|
20
|
+
"action": { "type": "string", "enum": ["created", "modified", "deleted"] },
|
|
21
|
+
"summary": { "type": "string" }
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"verificationChecklist": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"properties": {
|
|
28
|
+
"compilesWithoutErrors": { "type": "boolean" },
|
|
29
|
+
"existingTestsPass": { "type": "boolean" },
|
|
30
|
+
"newBehaviorHasCoverage": { "type": "boolean" },
|
|
31
|
+
"noHardcodedSecrets": { "type": "boolean" },
|
|
32
|
+
"noFileOver800Lines": { "type": "boolean" }
|
|
33
|
+
},
|
|
34
|
+
"required": ["compilesWithoutErrors", "existingTestsPass", "newBehaviorHasCoverage", "noHardcodedSecrets", "noFileOver800Lines"]
|
|
35
|
+
},
|
|
36
|
+
"blockerDescription": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"description": "Required when status is BLOCKED or NEEDS_MAIN_INPUT"
|
|
39
|
+
},
|
|
40
|
+
"needsMainInput": {
|
|
41
|
+
"type": "object",
|
|
42
|
+
"description": "Required when status is NEEDS_MAIN_INPUT",
|
|
43
|
+
"properties": {
|
|
44
|
+
"taskKey": { "type": "string" },
|
|
45
|
+
"question": { "type": "string" },
|
|
46
|
+
"safeDefault": { "type": "string" }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"additionalProperties": false
|
|
51
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "ReviewReport",
|
|
4
|
+
"description": "Output from cx-reviewer or cx-security: findings from a code or security review",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["type", "target", "verdict", "findings"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"type": { "type": "string", "enum": ["review-report"] },
|
|
9
|
+
"target": { "type": "string", "description": "Files or scope reviewed" },
|
|
10
|
+
"verdict": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"enum": ["APPROVED", "APPROVED_WITH_WARNINGS", "BLOCKED"],
|
|
13
|
+
"description": "APPROVED = no CRITICAL/HIGH. APPROVED_WITH_WARNINGS = only HIGH (merge with caution). BLOCKED = CRITICAL found."
|
|
14
|
+
},
|
|
15
|
+
"findings": {
|
|
16
|
+
"type": "array",
|
|
17
|
+
"items": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"required": ["severity", "category", "location", "description", "fix"],
|
|
20
|
+
"properties": {
|
|
21
|
+
"severity": { "type": "string", "enum": ["CRITICAL", "HIGH", "MEDIUM", "LOW"] },
|
|
22
|
+
"category": { "type": "string", "description": "e.g. correctness, security, regression, coverage, maintainability" },
|
|
23
|
+
"location": { "type": "string", "description": "file:line" },
|
|
24
|
+
"description": { "type": "string" },
|
|
25
|
+
"fix": { "type": "string", "description": "Concrete remediation" }
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"summary": { "type": "string", "description": "One-paragraph summary of overall quality" }
|
|
30
|
+
},
|
|
31
|
+
"additionalProperties": false
|
|
32
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "TestReport",
|
|
4
|
+
"description": "Output from cx-qa: results of test planning and execution",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["type", "verdict", "passed", "failed", "coverage"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"type": { "type": "string", "enum": ["test-report"] },
|
|
9
|
+
"verdict": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"enum": ["PASS", "FAIL"],
|
|
12
|
+
"description": "PASS = all tests green and coverage meets threshold. FAIL = failing tests or coverage below threshold."
|
|
13
|
+
},
|
|
14
|
+
"passed": { "type": "integer", "minimum": 0 },
|
|
15
|
+
"failed": { "type": "integer", "minimum": 0 },
|
|
16
|
+
"skipped": { "type": "integer", "minimum": 0 },
|
|
17
|
+
"coverage": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"properties": {
|
|
20
|
+
"overall": { "type": "number", "description": "0.0–100.0 percentage" },
|
|
21
|
+
"meetsThreshold": { "type": "boolean" },
|
|
22
|
+
"threshold": { "type": "number" }
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"failingTests": {
|
|
26
|
+
"type": "array",
|
|
27
|
+
"items": {
|
|
28
|
+
"type": "object",
|
|
29
|
+
"required": ["name", "error"],
|
|
30
|
+
"properties": {
|
|
31
|
+
"name": { "type": "string" },
|
|
32
|
+
"error": { "type": "string" }
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"gaps": {
|
|
37
|
+
"type": "array",
|
|
38
|
+
"items": { "type": "string" },
|
|
39
|
+
"description": "Untested behaviors that should have tests"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"additionalProperties": false
|
|
43
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/contracts/construct-handoff.mjs — normalize construct→orchestrator handoffs.
|
|
3
|
+
*
|
|
4
|
+
* MCP callers often pass only `goal`; enrich from orchestration_policy before
|
|
5
|
+
* validateHandoff so incomplete packets do not flood contract-violations.jsonl.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { buildConstructToOrchestratorPacket, routeRequest } from '../orchestration-policy.mjs';
|
|
9
|
+
|
|
10
|
+
export const CONSTRUCT_ORCHESTRATOR_REQUIRED = Object.freeze([
|
|
11
|
+
'goal',
|
|
12
|
+
'intent',
|
|
13
|
+
'workCategory',
|
|
14
|
+
'riskFlags',
|
|
15
|
+
'acceptanceCriteria',
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
export function needsConstructHandoffEnrichment(artifact) {
|
|
19
|
+
if (!artifact || typeof artifact !== 'object') return false;
|
|
20
|
+
return CONSTRUCT_ORCHESTRATOR_REQUIRED.some((field) => !Object.prototype.hasOwnProperty.call(artifact, field));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param {object} artifact — partial or full handoff packet
|
|
25
|
+
* @param {object} [opts]
|
|
26
|
+
* @param {string} [opts.request]
|
|
27
|
+
* @param {number} [opts.fileCount]
|
|
28
|
+
* @param {number} [opts.moduleCount]
|
|
29
|
+
* @param {boolean} [opts.introducesContract]
|
|
30
|
+
*/
|
|
31
|
+
export function enrichConstructOrchestratorHandoff(artifact, opts = {}) {
|
|
32
|
+
if (!needsConstructHandoffEnrichment(artifact)) return artifact;
|
|
33
|
+
const request = String(opts.request || artifact.goal || artifact.request || '').trim();
|
|
34
|
+
const routeOpts = {
|
|
35
|
+
request,
|
|
36
|
+
fileCount: opts.fileCount ?? artifact.fileCount ?? 0,
|
|
37
|
+
moduleCount: opts.moduleCount ?? artifact.moduleCount ?? 0,
|
|
38
|
+
introducesContract: opts.introducesContract ?? artifact.introducesContract ?? false,
|
|
39
|
+
};
|
|
40
|
+
const built = buildConstructToOrchestratorPacket({
|
|
41
|
+
...routeOpts,
|
|
42
|
+
goal: artifact.goal,
|
|
43
|
+
acceptanceCriteria: artifact.acceptanceCriteria,
|
|
44
|
+
});
|
|
45
|
+
if (built) return { ...built, ...artifact };
|
|
46
|
+
|
|
47
|
+
const route = routeRequest(routeOpts);
|
|
48
|
+
const goal = String(artifact.goal || request || 'User-requested work').trim();
|
|
49
|
+
const acceptanceCriteria = Array.isArray(artifact.acceptanceCriteria) && artifact.acceptanceCriteria.length
|
|
50
|
+
? artifact.acceptanceCriteria
|
|
51
|
+
: ['Dispatch plan emitted with specialists in sequence', 'Acceptance criteria verified before close'];
|
|
52
|
+
return {
|
|
53
|
+
goal,
|
|
54
|
+
intent: route.intent,
|
|
55
|
+
workCategory: route.workCategory,
|
|
56
|
+
riskFlags: route.riskFlags || {},
|
|
57
|
+
acceptanceCriteria,
|
|
58
|
+
...artifact,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Three validation tiers:
|
|
5
5
|
* 1. Schema shape: contracts.json conforms to contracts.schema.json (minimal
|
|
6
6
|
* validator — top-level required fields, contract entry required fields).
|
|
7
|
-
* 2. Cross-file refs: every output.schema points to a real file in lib/schemas/,
|
|
7
|
+
* 2. Cross-file refs: every output.schema points to a real file in lib/contract-schemas/,
|
|
8
8
|
* every producer/consumer name resolves to an agent or persona in
|
|
9
9
|
* specialists/registry.json, every well-known event/intake string is reachable.
|
|
10
10
|
* 3. Runtime handoff: a single artifact validated against the schema referenced
|
|
@@ -19,6 +19,10 @@
|
|
|
19
19
|
import { existsSync, readFileSync } from 'node:fs';
|
|
20
20
|
import { join, dirname, resolve } from 'node:path';
|
|
21
21
|
import { fileURLToPath } from 'node:url';
|
|
22
|
+
import {
|
|
23
|
+
missingRequiredReviewers,
|
|
24
|
+
} from '../artifact-reviewers.mjs';
|
|
25
|
+
import { inferArtifactTypeFromPath } from '../artifact-type-from-path.mjs';
|
|
22
26
|
import { POSTCONDITIONS, validateBinaryPostconditions } from '../specialists/postconditions.mjs';
|
|
23
27
|
import { logViolation } from './violation-log.mjs';
|
|
24
28
|
|
|
@@ -244,7 +248,12 @@ export function validateHandoff(opts = {}) {
|
|
|
244
248
|
const absPath = isAbsolutePath(declaredArtifactPath)
|
|
245
249
|
? declaredArtifactPath
|
|
246
250
|
: join(repoRoot, declaredArtifactPath);
|
|
247
|
-
const pcErrors = validateArtifactPostconditions({
|
|
251
|
+
const pcErrors = validateArtifactPostconditions({
|
|
252
|
+
contract,
|
|
253
|
+
artifactPath: absPath,
|
|
254
|
+
cwd: repoRoot,
|
|
255
|
+
rootDir: repoRoot,
|
|
256
|
+
});
|
|
248
257
|
for (const e of pcErrors) errors.push(e);
|
|
249
258
|
}
|
|
250
259
|
|
|
@@ -397,7 +406,7 @@ function claimsHaveCitations(body) {
|
|
|
397
406
|
* disk. Postconditions can be strings (descriptive, ignored here) or objects
|
|
398
407
|
* with { id, check, field?, section? }. Returns an array of error strings.
|
|
399
408
|
*/
|
|
400
|
-
export function validateArtifactPostconditions({ contract, artifactPath }) {
|
|
409
|
+
export function validateArtifactPostconditions({ contract, artifactPath, cwd = process.cwd(), rootDir } = {}) {
|
|
401
410
|
if (!contract?.postconditions?.length) return [];
|
|
402
411
|
if (!artifactPath || !existsSync(artifactPath)) {
|
|
403
412
|
const structured = contract.postconditions.filter((p) => typeof p === 'object');
|
|
@@ -473,6 +482,26 @@ export function validateArtifactPostconditions({ contract, artifactPath }) {
|
|
|
473
482
|
}
|
|
474
483
|
break;
|
|
475
484
|
}
|
|
485
|
+
case 'artifact-reviewers-seen': {
|
|
486
|
+
let missing;
|
|
487
|
+
if (pc.fromManifest) {
|
|
488
|
+
missing = missingRequiredReviewers({ filePath: artifactPath, cwd, rootDir });
|
|
489
|
+
} else {
|
|
490
|
+
missing = missingRequiredReviewers({
|
|
491
|
+
docType: pc.docType || inferArtifactTypeFromPath(artifactPath, { rootDir: cwd }),
|
|
492
|
+
filePath: artifactPath,
|
|
493
|
+
cwd,
|
|
494
|
+
rootDir,
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
if (Array.isArray(pc.reviewers) && pc.reviewers.length > 0) {
|
|
498
|
+
missing = missing.filter((r) => pc.reviewers.includes(r));
|
|
499
|
+
}
|
|
500
|
+
if (missing.length > 0) {
|
|
501
|
+
errors.push(`[${tag}] required reviewers not in agent log: ${missing.join(', ')}`);
|
|
502
|
+
}
|
|
503
|
+
break;
|
|
504
|
+
}
|
|
476
505
|
default:
|
|
477
506
|
errors.push(`[${tag}] unknown postcondition check: ${pc.check}`);
|
|
478
507
|
}
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* verifyChain; all writers go through logViolation.
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import { existsSync, mkdirSync, readFileSync } from 'node:fs';
|
|
26
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
27
27
|
import { join, dirname } from 'node:path';
|
|
28
28
|
import { homedir } from 'node:os';
|
|
29
29
|
import { createHash } from 'node:crypto';
|
|
@@ -55,6 +55,51 @@ function logFile(repoRoot) {
|
|
|
55
55
|
// log writes from the developer's project log.
|
|
56
56
|
export function violationLogPath(repoRoot) { return logFile(repoRoot); }
|
|
57
57
|
|
|
58
|
+
function supersedeMarkerFile(repoRoot) {
|
|
59
|
+
if (repoRoot) return join(repoRoot, '.cx', 'contract-violations-superseded.json');
|
|
60
|
+
return resolveProjectScopedPath('contract-violations-superseded.json', { ensureDir: false });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Read ISO timestamp before which violations are ignored for oracle/doctor counts.
|
|
65
|
+
*/
|
|
66
|
+
export function readViolationSupersedeCutoff(repoRoot) {
|
|
67
|
+
const file = supersedeMarkerFile(repoRoot);
|
|
68
|
+
if (!existsSync(file)) return null;
|
|
69
|
+
try {
|
|
70
|
+
const data = JSON.parse(readFileSync(file, 'utf8'));
|
|
71
|
+
const ts = data?.supersededBefore;
|
|
72
|
+
const parsed = Date.parse(String(ts ?? ''));
|
|
73
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
74
|
+
} catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Mark pre-fix contract violations as superseded for hygiene surfaces.
|
|
81
|
+
* Forensic log is preserved; oracle read model ignores older entries.
|
|
82
|
+
*/
|
|
83
|
+
export function markContractViolationsSuperseded({ repoRoot, reason = 'superseded by contract hygiene repair' } = {}) {
|
|
84
|
+
const file = supersedeMarkerFile(repoRoot);
|
|
85
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
86
|
+
const payload = {
|
|
87
|
+
supersededBefore: new Date().toISOString(),
|
|
88
|
+
reason,
|
|
89
|
+
};
|
|
90
|
+
writeFileSync(file, `${JSON.stringify(payload, null, 2)}\n`);
|
|
91
|
+
return payload;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function violationMatchesTail(tail, contractId, direction, missing, packet) {
|
|
95
|
+
if (!tail) return false;
|
|
96
|
+
if (tail.contractId !== contractId || tail.direction !== direction) return false;
|
|
97
|
+
const keys = packet && typeof packet === 'object' ? Object.keys(packet).sort() : null;
|
|
98
|
+
const tailKeys = Array.isArray(tail.packet_keys) ? [...tail.packet_keys].sort() : null;
|
|
99
|
+
return JSON.stringify(tail.missing) === JSON.stringify(missing)
|
|
100
|
+
&& JSON.stringify(tailKeys) === JSON.stringify(keys);
|
|
101
|
+
}
|
|
102
|
+
|
|
58
103
|
function sha256(input) { return createHash('sha256').update(input).digest('hex'); }
|
|
59
104
|
|
|
60
105
|
function readLastAgent() {
|
|
@@ -91,6 +136,8 @@ function nextSequence(repoRoot) {
|
|
|
91
136
|
export function logViolation(contractId, direction, missing, packet, extra = {}) {
|
|
92
137
|
try {
|
|
93
138
|
const { repoRoot, ...persistedExtra } = extra || {};
|
|
139
|
+
const tail = readTailRecord(repoRoot);
|
|
140
|
+
if (violationMatchesTail(tail, contractId, direction, missing, packet)) return;
|
|
94
141
|
const file = logFile(repoRoot);
|
|
95
142
|
mkdirSync(dirname(file), { recursive: true });
|
|
96
143
|
const record = {
|
|
@@ -113,17 +160,23 @@ export function logViolation(contractId, direction, missing, packet, extra = {})
|
|
|
113
160
|
* Rotated segments are intentionally not included — this is the doctor
|
|
114
161
|
* surface, scoped to the active window.
|
|
115
162
|
*/
|
|
116
|
-
export function recentViolations({ windowMs = 24 * 60 * 60 * 1000 } = {}) {
|
|
117
|
-
const file = logFile();
|
|
163
|
+
export function recentViolations({ windowMs = 24 * 60 * 60 * 1000, repoRoot } = {}) {
|
|
164
|
+
const file = logFile(repoRoot);
|
|
118
165
|
if (!existsSync(file)) return [];
|
|
166
|
+
const supersedeCutoff = readViolationSupersedeCutoff(repoRoot);
|
|
119
167
|
try {
|
|
120
|
-
const
|
|
168
|
+
const windowCutoff = Date.now() - windowMs;
|
|
121
169
|
return readFileSync(file, 'utf8')
|
|
122
170
|
.trim()
|
|
123
171
|
.split('\n')
|
|
124
172
|
.filter(Boolean)
|
|
125
173
|
.map((line) => { try { return JSON.parse(line); } catch { return null; } })
|
|
126
|
-
.filter((r) =>
|
|
174
|
+
.filter((r) => {
|
|
175
|
+
if (!r) return false;
|
|
176
|
+
const ts = new Date(r.ts).getTime();
|
|
177
|
+
if (supersedeCutoff != null && ts <= supersedeCutoff) return false;
|
|
178
|
+
return ts >= windowCutoff;
|
|
179
|
+
});
|
|
127
180
|
} catch { return []; }
|
|
128
181
|
}
|
|
129
182
|
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/dashboard-demo.mjs — Construct cockpit Playwright demo adapter.
|
|
3
|
+
*
|
|
4
|
+
* Resolves dashboard recording manifests (or legacy script bridge) and delegates
|
|
5
|
+
* to lib/playwright-demo.mjs with dashboard workspace defaults.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { loadDemoRecording } from './demo-recording.mjs';
|
|
10
|
+
import {
|
|
11
|
+
detectPlaywrightDemo,
|
|
12
|
+
recordPlaywrightDemo,
|
|
13
|
+
collectVideoFiles,
|
|
14
|
+
newestVideo,
|
|
15
|
+
finalizeDemoVideo,
|
|
16
|
+
locateFfmpeg,
|
|
17
|
+
transcodeWebmToMp4,
|
|
18
|
+
resolvePlaywrightPackage,
|
|
19
|
+
} from './playwright-demo.mjs';
|
|
20
|
+
|
|
21
|
+
export {
|
|
22
|
+
collectVideoFiles,
|
|
23
|
+
newestVideo,
|
|
24
|
+
finalizeDemoVideo,
|
|
25
|
+
locateFfmpeg,
|
|
26
|
+
transcodeWebmToMp4,
|
|
27
|
+
resolvePlaywrightPackage,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export function dashboardWorkspaceRoot(repoRoot) {
|
|
31
|
+
return path.join(repoRoot, 'apps', 'dashboard');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function detectDashboardDemo({ cwd = process.cwd(), repoRoot } = {}) {
|
|
35
|
+
const root = repoRoot || cwd;
|
|
36
|
+
return detectPlaywrightDemo({ workspace: dashboardWorkspaceRoot(root), repoRoot: root, cwd });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function recordDashboardDemo(name, {
|
|
40
|
+
cwd = process.cwd(),
|
|
41
|
+
repoRoot,
|
|
42
|
+
outputDir,
|
|
43
|
+
outputPath = null,
|
|
44
|
+
format = 'mp4',
|
|
45
|
+
artifactDir = null,
|
|
46
|
+
artifactFile = null,
|
|
47
|
+
env = process.env,
|
|
48
|
+
} = {}) {
|
|
49
|
+
const root = repoRoot || cwd;
|
|
50
|
+
const recording = loadDemoRecording(name, { cwd, repoRoot: root });
|
|
51
|
+
|
|
52
|
+
if (!recording) {
|
|
53
|
+
const detection = detectDashboardDemo({ cwd, repoRoot: root });
|
|
54
|
+
if (!detection.present) {
|
|
55
|
+
return { ok: false, name, missing: detection.missing, message: detection.message };
|
|
56
|
+
}
|
|
57
|
+
return { ok: false, name, message: `No recording manifest or dashboard demo script for ${name}` };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const outDir = outputDir || path.join(cwd, '.cx', 'demos', 'dashboard');
|
|
61
|
+
return recordPlaywrightDemo(recording, {
|
|
62
|
+
cwd,
|
|
63
|
+
repoRoot: root,
|
|
64
|
+
outputDir: outDir,
|
|
65
|
+
outputPath,
|
|
66
|
+
format,
|
|
67
|
+
artifactDir,
|
|
68
|
+
artifactFile,
|
|
69
|
+
env,
|
|
70
|
+
});
|
|
71
|
+
}
|
package/lib/dashboard-static.mjs
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import fs from 'node:fs';
|
|
12
12
|
import path from 'node:path';
|
|
13
13
|
import { spawnSync } from 'node:child_process';
|
|
14
|
+
import { sanitizeNpmSpawnEnv } from './npm-spawn-env.mjs';
|
|
14
15
|
|
|
15
16
|
function walkFiles(rootDir, currentDir = rootDir) {
|
|
16
17
|
if (!fs.existsSync(currentDir)) return [];
|
|
@@ -125,11 +126,24 @@ export function buildDashboardStatic({
|
|
|
125
126
|
spawnSyncFn = spawnSync,
|
|
126
127
|
} = {}) {
|
|
127
128
|
const { dashboardDir } = getDashboardPaths({ rootDir });
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
129
|
+
const nextCache = path.join(dashboardDir, '.next');
|
|
130
|
+
fs.rmSync(nextCache, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
131
|
+
|
|
132
|
+
const env = sanitizeNpmSpawnEnv(process.env);
|
|
133
|
+
let result;
|
|
134
|
+
for (let attempt = 1; attempt <= 2; attempt += 1) {
|
|
135
|
+
result = spawnSyncFn('npm', ['--prefix', dashboardDir, 'run', 'build'], {
|
|
136
|
+
cwd: rootDir,
|
|
137
|
+
encoding: 'utf8',
|
|
138
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
139
|
+
env,
|
|
140
|
+
});
|
|
141
|
+
if (result.status === 0) break;
|
|
142
|
+
const output = `${result.stdout || ''}\n${result.stderr || ''}`;
|
|
143
|
+
const transientRename = /ENOENT.*rename.*\.next\/(export|server\/pages)\/500\.html/.test(output);
|
|
144
|
+
if (!transientRename) break;
|
|
145
|
+
fs.rmSync(nextCache, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
146
|
+
}
|
|
133
147
|
if (result.status !== 0) {
|
|
134
148
|
throw new Error((result.stderr || result.stdout || 'dashboard build failed').trim());
|
|
135
149
|
}
|
|
@@ -23,6 +23,8 @@ import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
|
23
23
|
import { join, dirname, resolve, relative } from 'node:path';
|
|
24
24
|
import { fileURLToPath } from 'node:url';
|
|
25
25
|
|
|
26
|
+
import { readRuleFile } from '../rules-read.mjs';
|
|
27
|
+
|
|
26
28
|
export const DECISION_STATUSES = ['proposed', 'accepted', 'superseded', 'deprecated'];
|
|
27
29
|
|
|
28
30
|
const REPO_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
@@ -35,7 +37,7 @@ const REPO_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
|
35
37
|
const ENFORCES_LINE_RE = /^(?:\/\/|#|\*)?\s*@enforces\s+(\S+)\s*$/;
|
|
36
38
|
|
|
37
39
|
// The body carries Status and Supersedes as markdown definition lines, not
|
|
38
|
-
// frontmatter (see docs/adr/_template.md); parse them positionally.
|
|
40
|
+
// frontmatter (see docs/adr/templates/_template.md); parse them positionally.
|
|
39
41
|
|
|
40
42
|
// ADR field styles vary across the lane: inline bold (`- **Status**: x`,
|
|
41
43
|
// `**Status:** X`) and heading-then-value (`## Status` followed by the value on
|
|
@@ -184,9 +186,9 @@ function ruleDecisions({ repoRoot, bindings }) {
|
|
|
184
186
|
const dir = join(repoRoot, 'rules');
|
|
185
187
|
const out = [];
|
|
186
188
|
for (const file of walk(dir, (f) => f.endsWith('.md'))) {
|
|
187
|
-
const text = readFileSync(file, 'utf8');
|
|
188
|
-
const fm = parseFrontmatter(text);
|
|
189
189
|
const rel = relative(repoRoot, file);
|
|
190
|
+
const text = readRuleFile(rel, { rootDir: repoRoot, source: 'validation', callerContext: 'decisions-registry' });
|
|
191
|
+
const fm = parseFrontmatter(text);
|
|
190
192
|
const id = `rule:${relative(join(repoRoot, 'rules'), file).replace(/\.md$/, '')}`;
|
|
191
193
|
const enforcedBy = fm.enforced_by ? fm.enforced_by.split(',').map((s) => s.trim()).filter(Boolean) : [];
|
|
192
194
|
const enforcingTests = bindings.get(id) || [];
|