@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,103 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Distribution example — operational runbook with diagnostic flow and severity table.
|
|
3
|
+
artifactType: runbook
|
|
4
|
+
subtitle: Publish pipeline degraded — Pandoc or figure tooling missing
|
|
5
|
+
version: "1.1"
|
|
6
|
+
doc_id: RUNBOOK-PUBLISH-001
|
|
7
|
+
classification: internal
|
|
8
|
+
status: accepted
|
|
9
|
+
owner: cx-sre
|
|
10
|
+
last_verified_at: 2026-06-22
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Runbook: construct publish / export failures
|
|
14
|
+
|
|
15
|
+
- **Service**: document-export (`construct publish`, `construct export`)
|
|
16
|
+
- **Owner**: platform on-call
|
|
17
|
+
- **Last tested**: 2026-06-22
|
|
18
|
+
- **Severity**: SEV-2 when exports block release; SEV-3 when examples only
|
|
19
|
+
|
|
20
|
+
## Alert trigger
|
|
21
|
+
|
|
22
|
+
`construct publish` exits 2 with `missing: pandoc` or `Figure tooling` in `construct tools detect --figures` output. CI job `release:check` fails on export certification scenarios.
|
|
23
|
+
|
|
24
|
+
## Symptoms
|
|
25
|
+
|
|
26
|
+
- PDF export fails before Typst invocation
|
|
27
|
+
- `--figures` renders empty diagram placeholders
|
|
28
|
+
- `npm run examples:distribution` reports partial failures in summary
|
|
29
|
+
|
|
30
|
+
## Impact
|
|
31
|
+
|
|
32
|
+
| Audience | Impact |
|
|
33
|
+
|---|---|
|
|
34
|
+
| Release managers | Cannot ship branded PRDs/ADRs |
|
|
35
|
+
| Developers | Local previews blocked |
|
|
36
|
+
| Agents | `publish_run` MCP tool returns remediation hints |
|
|
37
|
+
|
|
38
|
+
## Severity and response
|
|
39
|
+
|
|
40
|
+
| Severity | Trigger condition | Page within | Comms | Error budget |
|
|
41
|
+
|----------|-------------------|-------------|-------|--------------|
|
|
42
|
+
| SEV-1 | All publish paths down in CI | 5 min | #incident + status | breach → freeze |
|
|
43
|
+
| SEV-2 | PDF down, HTML OK | 15 min | #platform | partial spend |
|
|
44
|
+
| SEV-3 | Examples script only | business hours | team channel | none |
|
|
45
|
+
|
|
46
|
+
## Diagnostic steps
|
|
47
|
+
|
|
48
|
+
1. Run `construct tools detect --figures --json` and read `steps.export` + `steps.figures`.
|
|
49
|
+
2. Confirm `pandoc` and `typst` on PATH; confirm `d2` and `mmdc` when `--figures` is set.
|
|
50
|
+
3. Re-run a single fixture: `node bin/construct export examples/distribution/sources/adr.md --to=pdf --figures`.
|
|
51
|
+
|
|
52
|
+
```mermaid
|
|
53
|
+
flowchart TD
|
|
54
|
+
A[Alert fires] --> B{export.present?}
|
|
55
|
+
B -->|No| C[Install pandoc + typst]
|
|
56
|
+
B -->|Yes| D{figures.present?}
|
|
57
|
+
D -->|No| E[brew install d2; npm i -g @mermaid-js/mermaid-cli]
|
|
58
|
+
D -->|Yes| F[Re-run examples:distribution]
|
|
59
|
+
C --> F
|
|
60
|
+
E --> F
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
```d2
|
|
64
|
+
direction: down
|
|
65
|
+
|
|
66
|
+
detect: tools detect
|
|
67
|
+
pandoc: pandoc + typst
|
|
68
|
+
figures: d2 + mmdc
|
|
69
|
+
ok: Green export
|
|
70
|
+
|
|
71
|
+
detect -> pandoc: if missing
|
|
72
|
+
detect -> figures: if --figures
|
|
73
|
+
pandoc -> ok
|
|
74
|
+
figures -> ok
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Remediation
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
brew install pandoc typst d2 graphviz
|
|
81
|
+
npm install -g @mermaid-js/mermaid-cli
|
|
82
|
+
npm run examples:distribution
|
|
83
|
+
open .tmp/distribution-examples/index.html
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Expected: index lists PDF and HTML links for each artifact type; no `FAILED` lines in script output.
|
|
87
|
+
|
|
88
|
+
## Rollback
|
|
89
|
+
|
|
90
|
+
Not applicable — this runbook restores tooling, not application state. If a bad template change caused failures, revert the Typst commit and re-run exports.
|
|
91
|
+
|
|
92
|
+
## Escalation
|
|
93
|
+
|
|
94
|
+
After 30 minutes without restore: page cx-platform-engineer. After 60 minutes with release blocked: page cx-release-manager.
|
|
95
|
+
|
|
96
|
+
## Post-incident
|
|
97
|
+
|
|
98
|
+
Preserve `construct tools detect --json` output and the failing pandoc/typst stderr in the incident ticket.
|
|
99
|
+
|
|
100
|
+
## References
|
|
101
|
+
|
|
102
|
+
- `docs/reference/document-io.md`
|
|
103
|
+
- `docs/cookbook/diagram-and-demo.md`
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Distribution example — strategy memo with goals, metrics, and landscape diagram.
|
|
3
|
+
artifactType: strategy
|
|
4
|
+
subtitle: Make Construct the default governed layer for agentic IDE workflows
|
|
5
|
+
version: "1.0"
|
|
6
|
+
doc_id: STRATEGY-2026-H1
|
|
7
|
+
classification: internal
|
|
8
|
+
status: draft
|
|
9
|
+
owner: cx-product-manager
|
|
10
|
+
last_verified_at: 2026-06-22
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Strategy: Governed agentic workflows for IDE-native teams
|
|
14
|
+
|
|
15
|
+
- **Date**: 2026-06-22
|
|
16
|
+
- **Owner**: cx-product-manager
|
|
17
|
+
- **Status**: draft
|
|
18
|
+
- **Horizon**: H1 2026 – H1 2027
|
|
19
|
+
|
|
20
|
+
> Platform and product teams want specialist routing without losing citation discipline, branded exports, or profile-aware terminology. Construct wins when evaluators can open one gallery of PDFs and see the same ink, typography, and diagram styling across document types.
|
|
21
|
+
|
|
22
|
+
## Problem
|
|
23
|
+
|
|
24
|
+
Host-native agents optimize for single-session chat. Enterprise platform teams need durable invoke plans, manifest-enforced artifacts, and distribution that looks intentional — not markdown attachments with host fonts.
|
|
25
|
+
|
|
26
|
+
## Goals
|
|
27
|
+
|
|
28
|
+
1. **Prove value in five minutes** — `npm run examples:distribution` produces a browsable gallery with figures.
|
|
29
|
+
2. **One brand contract** — `docs/reference/branding.md` + `03d-brand` audit ratchet.
|
|
30
|
+
3. **Profile-aware language** — intake queue labels follow active profile rebrand in CLI, session prelude, and dashboard API.
|
|
31
|
+
|
|
32
|
+
## Non-goals
|
|
33
|
+
|
|
34
|
+
Replacing host LLM execution. Mandating cloud render farms. Re-linting every historical doc for marketing voice.
|
|
35
|
+
|
|
36
|
+
## Landscape
|
|
37
|
+
|
|
38
|
+
```mermaid
|
|
39
|
+
flowchart TB
|
|
40
|
+
subgraph hosts [IDE hosts]
|
|
41
|
+
CC[Claude Code]
|
|
42
|
+
OC[OpenCode]
|
|
43
|
+
CX[Cursor]
|
|
44
|
+
end
|
|
45
|
+
subgraph construct [Construct]
|
|
46
|
+
R[Router]
|
|
47
|
+
V[Validate]
|
|
48
|
+
P[Publish]
|
|
49
|
+
end
|
|
50
|
+
hosts --> R --> V --> P
|
|
51
|
+
P --> PDF[Branded PDF]
|
|
52
|
+
P --> HTML[HTML / deck]
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```d2
|
|
56
|
+
direction: down
|
|
57
|
+
|
|
58
|
+
construct: Construct {
|
|
59
|
+
style.fill: "#f3f4f6"
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
hosts: IDE hosts
|
|
63
|
+
validate: Release gate
|
|
64
|
+
export: Branded export
|
|
65
|
+
|
|
66
|
+
hosts -> construct -> validate -> export
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Success metrics
|
|
70
|
+
|
|
71
|
+
| Metric | Baseline | Target |
|
|
72
|
+
|---|---|---|
|
|
73
|
+
| Brand audit findings | unknown | 0 in `03d-brand` |
|
|
74
|
+
| Example types with figures | 1 (PRD) | ≥6 layouts |
|
|
75
|
+
| Dashboard rebrand coverage | partial | intake API + pages |
|
|
76
|
+
|
|
77
|
+
## Risks and mitigations
|
|
78
|
+
|
|
79
|
+
| Risk | Mitigation |
|
|
80
|
+
|---|---|
|
|
81
|
+
| Toolchain missing on contributor laptops | `construct tools detect --figures` in runbook |
|
|
82
|
+
| Template drift | Functional tests on `construct-brand.typ` + HTML decks |
|
|
83
|
+
| Profile label regressions | `intake-rebrand.functional.test.mjs` |
|
|
84
|
+
|
|
85
|
+
## References
|
|
86
|
+
|
|
87
|
+
- `examples/distribution/manifest.json`
|
|
88
|
+
- https://langchain-ai.github.io/langgraph/ (accessed 2026-06-22)
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/adapters-sync.mjs — project adapter sync for the Construct tool repo and
|
|
3
|
+
* `npm run adapters`. Detects installed hosts, runs sync-specialists --project,
|
|
4
|
+
* and is invoked from postinstall when developing inside the package itself.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { spawnSync } from 'node:child_process';
|
|
8
|
+
import { existsSync } from 'node:fs';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
|
|
12
|
+
import { detectHostCapabilities } from './host-capabilities.mjs';
|
|
13
|
+
import { isConstructPackageRepo } from './host-disposition.mjs';
|
|
14
|
+
import { stageProjectAdapters } from './install/stage-project.mjs';
|
|
15
|
+
|
|
16
|
+
const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const PKG_ROOT = path.resolve(MODULE_DIR, '..');
|
|
18
|
+
|
|
19
|
+
const HOST_ID_MAP = {
|
|
20
|
+
'Claude Code': 'claude',
|
|
21
|
+
OpenCode: 'opencode',
|
|
22
|
+
Codex: 'codex',
|
|
23
|
+
'VS Code': 'vscode',
|
|
24
|
+
Cursor: 'cursor',
|
|
25
|
+
Copilot: 'copilot',
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export function resolveAdapterHosts({ forceAll = false, extra = [] } = {}) {
|
|
29
|
+
if (forceAll) return ['claude', 'opencode', 'codex', 'vscode', 'cursor'];
|
|
30
|
+
const hosts = new Set(extra);
|
|
31
|
+
for (const entry of detectHostCapabilities()) {
|
|
32
|
+
if (entry.availability !== 'installed') continue;
|
|
33
|
+
const id = HOST_ID_MAP[entry.host];
|
|
34
|
+
if (id) hosts.add(id);
|
|
35
|
+
}
|
|
36
|
+
if (hosts.size === 0) hosts.add('claude');
|
|
37
|
+
return [...hosts];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function syncProjectAdapters({
|
|
41
|
+
projectRoot = process.cwd(),
|
|
42
|
+
packageRoot = PKG_ROOT,
|
|
43
|
+
hosts = null,
|
|
44
|
+
log = () => {},
|
|
45
|
+
} = {}) {
|
|
46
|
+
const resolvedHosts = hosts ?? resolveAdapterHosts({ forceAll: isConstructPackageRepo(projectRoot) });
|
|
47
|
+
return stageProjectAdapters({
|
|
48
|
+
projectRoot,
|
|
49
|
+
packageRoot,
|
|
50
|
+
pkgVersion: null,
|
|
51
|
+
log,
|
|
52
|
+
hosts: resolvedHosts,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function runAdaptersScript({ cwd = process.cwd(), hosts = null } = {}) {
|
|
57
|
+
const result = syncProjectAdapters({ projectRoot: cwd, hosts, log: (m) => process.stdout.write(`[adapters] ${m}\n`) });
|
|
58
|
+
return result.synced ? 0 : 1;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
62
|
+
const args = new Set(process.argv.slice(2));
|
|
63
|
+
const forceAll = args.has('--all-hosts');
|
|
64
|
+
const hosts = forceAll ? resolveAdapterHosts({ forceAll: true }) : null;
|
|
65
|
+
const code = runAdaptersScript({ cwd: process.cwd(), hosts });
|
|
66
|
+
process.exit(code);
|
|
67
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/artifact-gate-notice.mjs — Shared PostToolUse artifact structure/visual check.
|
|
3
|
+
*
|
|
4
|
+
* Returns violations for typed docs under docs/** and .cx/research/** without
|
|
5
|
+
* applying TTY/CI suppression (callers decide whether to emit).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { inferArtifactTypeFromPath, isArtifactGatePath } from './artifact-type-from-path.mjs';
|
|
10
|
+
import { lintDocStructure, lintDocVisuals } from './templates/visual-requirements.mjs';
|
|
11
|
+
|
|
12
|
+
export function checkArtifactGateNotice(filePath, { cwd = process.cwd() } = {}) {
|
|
13
|
+
const rel = filePath.startsWith(cwd)
|
|
14
|
+
? filePath.slice(cwd.length).replace(/^\/+/, '')
|
|
15
|
+
: filePath;
|
|
16
|
+
if (!isArtifactGatePath(rel)) return null;
|
|
17
|
+
|
|
18
|
+
const type = inferArtifactTypeFromPath(filePath, { rootDir: cwd });
|
|
19
|
+
if (!type) return null;
|
|
20
|
+
|
|
21
|
+
const errors = [
|
|
22
|
+
...lintDocStructure(filePath, type),
|
|
23
|
+
...lintDocVisuals(filePath, type),
|
|
24
|
+
];
|
|
25
|
+
if (errors.length === 0) return null;
|
|
26
|
+
|
|
27
|
+
return { type, rel, errors };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function formatArtifactGateNotice({ type, rel, errors }) {
|
|
31
|
+
const lines = [
|
|
32
|
+
`\nartifact-release-gate (${type}): ${errors.length} structure/visual issue(s). `
|
|
33
|
+
+ 'Fix before calling the artifact done; full gate: '
|
|
34
|
+
+ `\`construct artifact validate ${rel} --type=${type}\`\n`,
|
|
35
|
+
];
|
|
36
|
+
for (const e of errors) lines.push(` - ${e}\n`);
|
|
37
|
+
return lines.join('');
|
|
38
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/artifact-manifest.mjs — Load and resolve the artifact capability manifest.
|
|
3
|
+
*
|
|
4
|
+
* specialists/artifact-manifest.json is the single source of truth for document
|
|
5
|
+
* type metadata: templates, tone, structure, visuals, and release gates.
|
|
6
|
+
* Consumer projects without a local copy resolve the shipped manifest from the
|
|
7
|
+
* Construct package root.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import fs from 'node:fs';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
|
|
14
|
+
const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
15
|
+
const EMPTY_MANIFEST = { version: 1, artifacts: {} };
|
|
16
|
+
|
|
17
|
+
// Kept here, next to the manifest resolver, so every public workflow surface
|
|
18
|
+
// uses one answer for what can carry Construct styling. Source formats remain
|
|
19
|
+
// deliberately plain unless a caller explicitly asks otherwise.
|
|
20
|
+
export const BRAND_CAPABLE_FORMATS = Object.freeze(['pdf', 'docx', 'doc', 'deck', 'pptx', 'html', 'rtf', 'odt', 'epub', 'tex']);
|
|
21
|
+
export const PLAIN_OUTPUT_FORMATS = Object.freeze(['txt', 'md', 'mdx']);
|
|
22
|
+
|
|
23
|
+
let cached = null;
|
|
24
|
+
let cachedRoot = null;
|
|
25
|
+
|
|
26
|
+
function manifestPathForRoot(root) {
|
|
27
|
+
return path.join(root, 'specialists', 'artifact-manifest.json');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function findConstructRoot(startPath = process.cwd()) {
|
|
31
|
+
let current = path.resolve(startPath);
|
|
32
|
+
while (true) {
|
|
33
|
+
const manifest = manifestPathForRoot(current);
|
|
34
|
+
if (fs.existsSync(manifest)) return current;
|
|
35
|
+
const parent = path.dirname(current);
|
|
36
|
+
if (parent === current) break;
|
|
37
|
+
current = parent;
|
|
38
|
+
}
|
|
39
|
+
if (fs.existsSync(manifestPathForRoot(PACKAGE_ROOT))) return PACKAGE_ROOT;
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function loadArtifactManifest({ rootDir, force = false, cwd = process.cwd() } = {}) {
|
|
44
|
+
const resolvedRoot = rootDir ?? findConstructRoot(cwd) ?? PACKAGE_ROOT;
|
|
45
|
+
if (cached && !force && cachedRoot === resolvedRoot) return cached;
|
|
46
|
+
|
|
47
|
+
const p = manifestPathForRoot(resolvedRoot);
|
|
48
|
+
if (!fs.existsSync(p)) {
|
|
49
|
+
cached = EMPTY_MANIFEST;
|
|
50
|
+
cachedRoot = resolvedRoot;
|
|
51
|
+
return cached;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
cached = JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
55
|
+
cachedRoot = resolvedRoot;
|
|
56
|
+
return cached;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function getArtifactEntry(type, opts = {}) {
|
|
60
|
+
const manifest = loadArtifactManifest(opts);
|
|
61
|
+
return manifest.artifacts?.[type] ?? null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function artifactTypes(opts = {}) {
|
|
65
|
+
const manifest = loadArtifactManifest(opts);
|
|
66
|
+
return Object.keys(manifest.artifacts ?? {});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function isObject(value) {
|
|
70
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function mergeObjects(...values) {
|
|
74
|
+
const out = {};
|
|
75
|
+
for (const value of values) {
|
|
76
|
+
if (!isObject(value)) continue;
|
|
77
|
+
for (const [key, next] of Object.entries(value)) {
|
|
78
|
+
out[key] = isObject(next) && isObject(out[key]) ? mergeObjects(out[key], next) : structuredClone(next);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function unique(items) {
|
|
85
|
+
return [...new Set(items.filter(Boolean))];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Resolve a user-facing type or registered alias. The result intentionally
|
|
90
|
+
* distinguishes an unknown request from a fallback: callers must ask for
|
|
91
|
+
* classification/registration instead of silently turning it into a PRD.
|
|
92
|
+
*/
|
|
93
|
+
export function resolveArtifactType(type, opts = {}) {
|
|
94
|
+
const requestedType = String(type ?? '').trim().toLowerCase();
|
|
95
|
+
const manifest = loadArtifactManifest(opts);
|
|
96
|
+
if (!requestedType) {
|
|
97
|
+
return {
|
|
98
|
+
status: 'unrecognized',
|
|
99
|
+
requestedType: null,
|
|
100
|
+
type: null,
|
|
101
|
+
guidance: 'Specify a registered document class or register one in specialists/artifact-manifest.json.',
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
for (const [registeredType, entry] of Object.entries(manifest.artifacts ?? {})) {
|
|
105
|
+
const aliases = [registeredType, ...(entry.aliases ?? [])].map((value) => String(value).toLowerCase());
|
|
106
|
+
if (aliases.includes(requestedType)) {
|
|
107
|
+
return { status: 'registered', requestedType, type: registeredType, entry };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
status: 'unrecognized',
|
|
112
|
+
requestedType,
|
|
113
|
+
type: null,
|
|
114
|
+
guidance: `Document class '${requestedType}' is not registered. Classify the request or add it to specialists/artifact-manifest.json.`,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Resolve the canonical workflow contract for a registered document class.
|
|
120
|
+
* Precedence is deliberately explicit: invocation > project config > manifest
|
|
121
|
+
* defaults. Existing entries remain compatible: primaryOwners and releaseGate
|
|
122
|
+
* are the fallback author and reviewer/validation declarations.
|
|
123
|
+
*/
|
|
124
|
+
export function resolveArtifactWorkflowContract(type, {
|
|
125
|
+
rootDir,
|
|
126
|
+
cwd = process.cwd(),
|
|
127
|
+
projectConfig = null,
|
|
128
|
+
overrides = null,
|
|
129
|
+
} = {}) {
|
|
130
|
+
const resolved = resolveArtifactType(type, { rootDir, cwd });
|
|
131
|
+
if (resolved.status !== 'registered') return resolved;
|
|
132
|
+
|
|
133
|
+
const manifest = loadArtifactManifest({ rootDir, cwd });
|
|
134
|
+
const entry = resolved.entry;
|
|
135
|
+
const config = projectConfig?.artifactWorkflow ?? {};
|
|
136
|
+
const manifestDefaults = manifest.workflowDefaults ?? {};
|
|
137
|
+
const projectType = config.types?.[resolved.type] ?? {};
|
|
138
|
+
const appliedOverrides = [];
|
|
139
|
+
if (isObject(config.defaults) && Object.keys(config.defaults).length) appliedOverrides.push('project.defaults');
|
|
140
|
+
if (isObject(projectType) && Object.keys(projectType).length) appliedOverrides.push(`project.types.${resolved.type}`);
|
|
141
|
+
if (isObject(overrides) && Object.keys(overrides).length) appliedOverrides.push('invocation');
|
|
142
|
+
|
|
143
|
+
const configured = mergeObjects(manifestDefaults, entry, config.defaults, projectType, overrides);
|
|
144
|
+
const releaseGate = configured.releaseGate ?? {};
|
|
145
|
+
const authorChain = unique(configured.authorChain ?? configured.primaryOwners ?? []);
|
|
146
|
+
const reviewerChain = unique(configured.reviewerChain ?? [
|
|
147
|
+
...(releaseGate.requiredReviewers ?? []),
|
|
148
|
+
...(releaseGate.optionalReviewers ?? []),
|
|
149
|
+
]);
|
|
150
|
+
const outputs = mergeObjects({
|
|
151
|
+
formats: [...BRAND_CAPABLE_FORMATS, ...PLAIN_OUTPUT_FORMATS],
|
|
152
|
+
branding: 'construct',
|
|
153
|
+
}, configured.outputs);
|
|
154
|
+
const validation = mergeObjects({ releaseGate: true }, configured.validation);
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
status: 'registered',
|
|
158
|
+
requestedType: resolved.requestedType,
|
|
159
|
+
type: resolved.type,
|
|
160
|
+
documentClass: configured.documentClass ?? resolved.type,
|
|
161
|
+
template: configured.template ?? null,
|
|
162
|
+
workflowSkill: configured.workflowSkill ?? null,
|
|
163
|
+
authorChain,
|
|
164
|
+
reviewerChain,
|
|
165
|
+
requiredReviewers: unique(releaseGate.requiredReviewers ?? []),
|
|
166
|
+
optionalReviewers: unique(releaseGate.optionalReviewers ?? []),
|
|
167
|
+
researchProfile: configured.researchProfile ?? null,
|
|
168
|
+
validation,
|
|
169
|
+
releaseGate,
|
|
170
|
+
outputs,
|
|
171
|
+
appliedOverrides,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** A dependency-free validator used by startup checks and tests. */
|
|
176
|
+
export function validateArtifactManifest(manifest) {
|
|
177
|
+
const errors = [];
|
|
178
|
+
if (!isObject(manifest)) return { valid: false, errors: ['root: must be an object'] };
|
|
179
|
+
if (!Number.isInteger(manifest.version) || manifest.version < 1) errors.push('version: must be an integer >= 1');
|
|
180
|
+
if (!isObject(manifest.artifacts)) errors.push('artifacts: must be an object');
|
|
181
|
+
for (const [type, entry] of Object.entries(manifest.artifacts ?? {})) {
|
|
182
|
+
const prefix = `artifacts.${type}`;
|
|
183
|
+
if (!isObject(entry)) { errors.push(`${prefix}: must be an object`); continue; }
|
|
184
|
+
if (typeof entry.template !== 'string' || !entry.template) errors.push(`${prefix}.template: required string missing`);
|
|
185
|
+
if (!Array.isArray(entry.primaryOwners) || entry.primaryOwners.some((owner) => typeof owner !== 'string')) {
|
|
186
|
+
errors.push(`${prefix}.primaryOwners: required string array missing`);
|
|
187
|
+
}
|
|
188
|
+
for (const field of ['authorChain', 'reviewerChain', 'aliases']) {
|
|
189
|
+
if (entry[field] !== undefined && (!Array.isArray(entry[field]) || entry[field].some((value) => typeof value !== 'string'))) {
|
|
190
|
+
errors.push(`${prefix}.${field}: must be an array of strings`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (entry.outputs?.formats !== undefined && (!Array.isArray(entry.outputs.formats) || entry.outputs.formats.length === 0)) {
|
|
194
|
+
errors.push(`${prefix}.outputs.formats: must be a non-empty array`);
|
|
195
|
+
}
|
|
196
|
+
if (entry.outputs?.branding !== undefined && !['construct', 'plain'].includes(entry.outputs.branding)) {
|
|
197
|
+
errors.push(`${prefix}.outputs.branding: must be construct or plain`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return errors.length ? { valid: false, errors } : { valid: true, errors: [] };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function structureRequirementsFromManifest(opts = {}) {
|
|
204
|
+
const manifest = loadArtifactManifest(opts);
|
|
205
|
+
const out = {};
|
|
206
|
+
for (const [type, entry] of Object.entries(manifest.artifacts ?? {})) {
|
|
207
|
+
if (entry.structureRequirements?.length) out[type] = entry.structureRequirements;
|
|
208
|
+
}
|
|
209
|
+
return out;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function visualRequirementsFromManifest(opts = {}) {
|
|
213
|
+
const manifest = loadArtifactManifest(opts);
|
|
214
|
+
const out = {};
|
|
215
|
+
for (const [type, entry] of Object.entries(manifest.artifacts ?? {})) {
|
|
216
|
+
if (entry.visualRequirements?.length) out[type] = entry.visualRequirements;
|
|
217
|
+
}
|
|
218
|
+
return out;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export function loadBrandVoice(cwd = process.cwd()) {
|
|
222
|
+
const p = path.join(cwd, '.cx', 'brand-voice.json');
|
|
223
|
+
if (!fs.existsSync(p)) return null;
|
|
224
|
+
try {
|
|
225
|
+
return JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
226
|
+
} catch {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export function resolveToneForArtifact(type, { cwd = process.cwd(), rootDir } = {}) {
|
|
232
|
+
const entry = getArtifactEntry(type, { rootDir, cwd });
|
|
233
|
+
const brand = loadBrandVoice(cwd);
|
|
234
|
+
const override = brand?.toneOverride?.[type];
|
|
235
|
+
if (override) return override;
|
|
236
|
+
return entry?.toneDefault ?? 'direct';
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function templateMetadata(type, { cwd = process.cwd(), rootDir } = {}) {
|
|
240
|
+
const workflow = resolveArtifactWorkflowContract(type, { rootDir, cwd });
|
|
241
|
+
if (workflow.status !== 'registered') return null;
|
|
242
|
+
const entry = getArtifactEntry(workflow.type, { rootDir, cwd });
|
|
243
|
+
return {
|
|
244
|
+
type: workflow.type,
|
|
245
|
+
documentClass: workflow.documentClass,
|
|
246
|
+
tone: resolveToneForArtifact(workflow.type, { cwd, rootDir }),
|
|
247
|
+
toneAllowed: entry.toneAllowed ?? [],
|
|
248
|
+
structureRequirements: entry.structureRequirements ?? [],
|
|
249
|
+
visualRequirements: entry.visualRequirements ?? [],
|
|
250
|
+
primaryOwners: entry.primaryOwners ?? [],
|
|
251
|
+
workflowSkill: entry.workflowSkill ?? null,
|
|
252
|
+
releaseGate: entry.releaseGate ?? null,
|
|
253
|
+
authorChain: workflow.authorChain,
|
|
254
|
+
reviewerChain: workflow.reviewerChain,
|
|
255
|
+
validation: workflow.validation,
|
|
256
|
+
outputs: workflow.outputs,
|
|
257
|
+
};
|
|
258
|
+
}
|