@geraldmaron/construct 1.2.2 → 1.3.0
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 +7 -10
- package/bin/construct +286 -220
- package/bin/construct-postinstall.mjs +0 -21
- package/commands/work/optimize-prompts.md +1 -1
- package/examples/distribution/sources/adr.md +2 -2
- package/examples/seed-observations/decisions.md +1 -1
- package/lib/artifact-loop-core.mjs +412 -0
- package/lib/artifact-manifest.mjs +6 -0
- package/lib/artifact-release-gate.mjs +115 -49
- package/lib/audit-rules.mjs +2 -4
- package/lib/audit-skills.mjs +32 -20
- package/lib/audit-specialists.mjs +44 -26
- package/lib/auto-docs.mjs +6 -5
- package/lib/brand-prose.mjs +3 -3
- package/lib/brand-tokens.mjs +2 -2
- package/lib/certification/artifact-fixtures.mjs +4 -1
- package/lib/certification/demo-parity.mjs +6 -32
- package/lib/certification/real-llm-scenarios.mjs +58 -13
- package/lib/certification/role-cards.mjs +7 -6
- package/lib/certification/role-overlays.mjs +4 -4
- package/lib/certification/runner.mjs +9 -2
- package/lib/certification/skill-inventory.mjs +34 -22
- package/lib/certification/skill-scenarios.mjs +21 -8
- package/lib/certification/specialist-contracts.mjs +4 -3
- package/lib/certification/specialist-scenarios.mjs +7 -6
- package/lib/certification/status.mjs +5 -4
- package/lib/cli-commands.mjs +56 -57
- package/lib/comment-lint.mjs +1 -5
- package/lib/completions.mjs +20 -2
- package/lib/config/legacy-config-migration.mjs +125 -0
- package/lib/config/schema.mjs +5 -5
- package/lib/config/source-targets.mjs +80 -0
- package/lib/contracts/validate.mjs +119 -51
- package/lib/decisions/golden.mjs +3 -2
- package/lib/decisions/registry.mjs +11 -6
- package/lib/demo-project.mjs +188 -0
- package/lib/demo-recording.mjs +9 -34
- package/lib/demo-script.mjs +2 -2
- package/lib/demo-surface.mjs +13 -131
- package/lib/demo-tour-renderer.mjs +92 -0
- package/lib/demo.mjs +69 -54
- package/lib/diagram-export.mjs +63 -14
- package/lib/doctor/source-checkout.mjs +3 -4
- package/lib/doctor/watchers/consistency.mjs +90 -22
- package/lib/doctor/watchers/credential-parity.mjs +97 -0
- package/lib/doctor/watchers/mcp-protocol.mjs +1 -1
- package/lib/doctor/watchers/service-health.mjs +4 -32
- package/lib/document-export.mjs +73 -9
- package/lib/document-extract.mjs +10 -198
- package/lib/embed/artifact.mjs +2 -3
- package/lib/embed/cli.mjs +1 -1
- package/lib/embed/config.mjs +1 -2
- package/lib/embed/customer-profiles.mjs +1 -1
- package/lib/embed/daemon.mjs +5 -6
- package/lib/embed/demand-fetch.mjs +114 -2
- package/lib/embed/docs-lifecycle.mjs +9 -5
- package/lib/embed/intake-metrics.mjs +2 -2
- package/lib/embed/notifications.mjs +2 -3
- package/lib/embed/recommendation-store.mjs +25 -22
- package/lib/embed/role-framing.mjs +4 -9
- package/lib/embedded-contract/capability.mjs +4 -2
- package/lib/embedded-contract/contract-version.mjs +1 -1
- package/lib/env-config.mjs +2 -2
- package/lib/flavors/loader.mjs +21 -19
- package/lib/graph/build-from-registry.mjs +31 -7
- package/lib/graph/staleness.mjs +1 -1
- package/lib/headhunt.mjs +82 -35
- package/lib/hooks/agent-tracker.mjs +2 -2
- package/lib/hooks/config-protection.mjs +1 -1
- package/lib/hooks/registry-sync.mjs +3 -3
- package/lib/host-capabilities.mjs +5 -5
- package/lib/improvement/controller.mjs +2 -2
- package/lib/init-unified.mjs +19 -21
- package/lib/intake/classify.mjs +26 -0
- package/lib/intake/daemon.mjs +2 -3
- package/lib/intake/prepare.mjs +10 -3
- package/lib/intake/session-prelude.mjs +1 -1
- package/lib/intake/tables/creative.mjs +9 -9
- package/lib/intake/tables/operations.mjs +4 -4
- package/lib/intake/tables/research.mjs +2 -2
- package/lib/integrations/intake-integrations.mjs +9 -10
- package/lib/knowledge/research-store.mjs +2 -2
- package/lib/mcp/server.mjs +104 -61
- package/lib/mcp/tool-budget.mjs +1 -46
- package/lib/mcp/tool-recovery.mjs +44 -0
- package/lib/mcp/tools/artifact-author.mjs +36 -0
- package/lib/mcp/tools/find-tool.mjs +86 -0
- package/lib/mcp/tools/orchestration-run.mjs +107 -69
- package/lib/mcp/tools/project.mjs +4 -3
- package/lib/mcp/tools/{profile.mjs → scope.mjs} +54 -52
- package/lib/mcp/tools/skills.mjs +39 -22
- package/lib/mcp/tools/telemetry.mjs +2 -1
- package/lib/mcp/tools/workflow.mjs +1 -1
- package/lib/mcp-platform-config.mjs +7 -5
- package/lib/migrations/index.mjs +4 -2
- package/lib/migrations/v2-unified-registry.mjs +35 -0
- package/lib/model-router.mjs +203 -36
- package/lib/models/catalog.mjs +25 -9
- package/lib/models/execution-capability-profile.mjs +3 -3
- package/lib/models/execution-policy.mjs +7 -6
- package/lib/models/provider-poll.mjs +18 -4
- package/lib/opencode-config.mjs +56 -1
- package/lib/opencode-runtime-plugin.mjs +13 -10
- package/lib/oracle/artifact-gate.mjs +14 -4
- package/lib/oracle/cli.mjs +2 -0
- package/lib/oracle/dispatch.mjs +18 -1
- package/lib/oracle/execute.mjs +39 -7
- package/lib/oracle/index.mjs +1 -1
- package/lib/oracle/org-graph.mjs +10 -5
- package/lib/oracle/policy.mjs +6 -0
- package/lib/oracle/read-model.mjs +103 -6
- package/lib/oracle/reconcile.mjs +31 -4
- package/lib/oracle/remediation-dispatch.mjs +53 -0
- package/lib/oracle/routing.mjs +5 -0
- package/lib/oracle/synthesize.mjs +80 -3
- package/lib/orchestration/routing-tables.mjs +44 -10
- package/lib/orchestration/runtime.mjs +2 -0
- package/lib/orchestration/worker.mjs +1 -1
- package/lib/orchestration-policy.mjs +351 -62
- package/lib/overrides/resolver.mjs +1 -12
- package/lib/parity.mjs +37 -11
- package/lib/policy/engine.mjs +80 -15
- package/lib/prompt-composer.js +51 -15
- package/lib/prompt-metadata.mjs +3 -2
- package/lib/providers/connection-probe.mjs +58 -0
- package/lib/providers/copilot-auth.mjs +1 -1
- package/lib/providers/credential-bootstrap.mjs +1 -1
- package/lib/providers/op-run.mjs +3 -4
- package/lib/providers/secret-resolver.mjs +31 -0
- package/lib/publish-tooling.mjs +3 -11
- package/lib/publish.mjs +12 -29
- package/lib/reconcile/mcp-entry-reconcile.mjs +14 -9
- package/lib/reflect.mjs +2 -2
- package/lib/registry/agent-manifest.mjs +190 -0
- package/lib/registry/assemble.mjs +133 -0
- package/lib/registry/catalog.mjs +171 -0
- package/lib/registry/cli.mjs +590 -6
- package/lib/registry/consolidation.mjs +5 -4
- package/lib/registry/docs-sync.mjs +67 -0
- package/lib/registry/loader.mjs +147 -0
- package/lib/registry/org-io.mjs +76 -0
- package/lib/registry/retired-paths.mjs +71 -0
- package/lib/registry/surface-map.mjs +5 -7
- package/lib/registry/validator.mjs +438 -0
- package/lib/research-execution-policy.mjs +173 -0
- package/lib/roles/catalog.mjs +4 -9
- package/lib/roles/fence.mjs +107 -1
- package/lib/roles/flavor-bindings.mjs +68 -0
- package/lib/roles/gateway.mjs +140 -29
- package/lib/roles/manifest.mjs +22 -13
- package/lib/roles/router.mjs +1 -1
- package/lib/runtime-env.mjs +1 -1
- package/lib/scopes/enrich.mjs +67 -0
- package/lib/scopes/hooks.mjs +12 -0
- package/lib/{profiles → scopes}/lifecycle.mjs +70 -70
- package/lib/scopes/loader.mjs +104 -0
- package/lib/scopes/rebrand.mjs +32 -0
- package/lib/scopes/research-profile.mjs +16 -0
- package/lib/scopes/teams.mjs +98 -0
- package/lib/service-manager.mjs +1 -117
- package/lib/setup-prompts.mjs +1 -1
- package/lib/setup.mjs +17 -28
- package/lib/skills/composition-graph.mjs +98 -0
- package/lib/skills/router.mjs +80 -0
- package/lib/specialist-contracts.mjs +17 -18
- package/lib/specialists/postconditions.mjs +2 -2
- package/lib/specialists/prompt-schema.mjs +6 -5
- package/lib/specialists/roster.mjs +7 -12
- package/lib/specialists/schema.mjs +9 -6
- package/lib/status.mjs +25 -90
- package/lib/storage/file-lock.mjs +6 -3
- package/lib/telemetry/skill-calls.mjs +1 -1
- package/lib/templates/doc-presentation.mjs +63 -0
- package/lib/templates/visual-requirements.mjs +35 -2
- package/lib/term-format.mjs +107 -2
- package/lib/test-env-setup.mjs +21 -0
- package/lib/ui/components.mjs +42 -0
- package/lib/ui/glyphs.mjs +58 -0
- package/lib/ui/links.mjs +115 -0
- package/lib/ui/theme.mjs +108 -0
- package/lib/uninstall/uninstall.mjs +2 -1
- package/lib/validator.mjs +45 -8
- package/lib/validators/skill-effectiveness.mjs +174 -0
- package/lib/validators/skills.mjs +1 -1
- package/package.json +12 -7
- package/personas/construct.md +12 -3
- package/platforms/claude/CLAUDE.md +1 -1
- package/rules/common/beads-hygiene.md +52 -0
- package/rules/common/neurodivergent-output.md +4 -7
- package/rules/common/research.md +2 -0
- package/scripts/sync-specialists.mjs +260 -49
- package/skills/ai/prompt-optimizer.md +3 -3
- package/skills/brand/output-vibe.md +48 -0
- package/skills/docs/adr-workflow.md +3 -0
- package/skills/docs/backlog-proposal-workflow.md +3 -0
- package/skills/docs/codebase-research-workflow.md +5 -2
- package/skills/docs/customer-profile-workflow.md +3 -0
- package/skills/docs/document-ingest-workflow.md +3 -0
- package/skills/docs/evidence-ingest-workflow.md +4 -1
- package/skills/docs/init-project.md +1 -1
- package/skills/docs/prd-workflow.md +1 -1
- package/skills/docs/prfaq-workflow.md +3 -0
- package/skills/docs/product-intelligence-workflow.md +4 -1
- package/skills/docs/product-signal-workflow.md +3 -0
- package/skills/docs/research-workflow.md +13 -6
- package/skills/docs/runbook-workflow.md +3 -0
- package/skills/docs/strategy-workflow.md +3 -0
- package/skills/docs/user-research-workflow.md +6 -3
- package/skills/operating/orchestration-reference.md +1 -1
- package/skills/roles/{engineer.ai.md → ai-engineer.md} +2 -2
- package/skills/roles/architect.ai-systems.md +1 -1
- package/skills/roles/architect.data.md +1 -1
- package/skills/roles/architect.enterprise.md +1 -1
- package/skills/roles/architect.integration.md +1 -1
- package/skills/roles/architect.md +1 -1
- package/skills/roles/architect.platform.md +1 -1
- package/skills/roles/{product-manager.business-strategy.md → business-strategist.md} +3 -3
- package/skills/roles/data-analyst.experiment.md +1 -1
- package/skills/roles/data-analyst.md +1 -1
- package/skills/roles/data-analyst.product-intelligence.md +1 -1
- package/skills/roles/data-analyst.product.md +1 -1
- package/skills/roles/data-analyst.telemetry.md +1 -1
- package/skills/roles/{engineer.data.md → data-engineer.md} +2 -2
- package/skills/roles/data-engineer.pipeline.md +2 -2
- package/skills/roles/data-engineer.vector-retrieval.md +2 -2
- package/skills/roles/data-engineer.warehouse.md +2 -2
- package/skills/roles/debugger.md +1 -1
- package/skills/roles/designer.accessibility.md +1 -1
- package/skills/roles/designer.md +1 -1
- package/skills/roles/{reviewer.devil-advocate.md → devil-advocate.md} +2 -2
- package/skills/roles/{operator.docs.md → docs-keeper.md} +3 -3
- package/skills/roles/engineer.md +3 -7
- package/skills/roles/{reviewer.evaluator.md → evaluator.md} +2 -2
- package/skills/roles/{researcher.explorer.md → explorer.md} +2 -2
- package/skills/roles/{operator.md → operations.md} +2 -5
- package/skills/roles/orchestrator.md +1 -1
- package/skills/roles/{engineer.platform.md → platform-engineer.md} +2 -2
- package/skills/roles/product-manager.ai-product.md +1 -1
- package/skills/roles/product-manager.enterprise.md +1 -1
- package/skills/roles/product-manager.growth.md +1 -2
- package/skills/roles/product-manager.md +1 -2
- package/skills/roles/product-manager.platform.md +1 -1
- package/skills/roles/product-manager.product.md +1 -1
- package/skills/roles/qa.ai-eval.md +1 -1
- package/skills/roles/qa.api-contract.md +1 -1
- package/skills/roles/qa.data-pipeline.md +1 -1
- package/skills/roles/qa.md +1 -1
- package/skills/roles/qa.web-ui.md +1 -1
- package/skills/roles/{operator.release.md → release-manager.md} +3 -3
- package/skills/roles/researcher.md +1 -1
- package/skills/roles/reviewer.md +1 -1
- package/skills/roles/security.ai.md +1 -1
- package/skills/roles/security.appsec.md +1 -1
- package/skills/roles/security.cloud.md +1 -1
- package/skills/roles/security.legal-compliance.md +1 -1
- package/skills/roles/security.md +1 -1
- package/skills/roles/security.privacy.md +1 -1
- package/skills/roles/security.supply-chain.md +1 -1
- package/skills/roles/{operator.sre.md → sre.md} +3 -3
- package/skills/roles/{qa.test-automation.md → test-automation.md} +2 -2
- package/skills/roles/{reviewer.trace.md → trace-reviewer.md} +2 -2
- package/skills/roles/{researcher.ux.md → ux-researcher.md} +2 -2
- package/skills/routing.json +18 -0
- package/skills/routing.md +1 -1
- package/specialists/artifact-manifest.json +2 -1
- package/specialists/audit-enrichments.json +14 -14
- package/specialists/org/contracts/accessibility-to-qa.json +37 -0
- package/specialists/org/contracts/any-to-business-strategist.json +57 -0
- package/specialists/org/contracts/any-to-debugger.json +38 -0
- package/specialists/org/contracts/any-to-designer.json +33 -0
- package/specialists/org/contracts/any-to-docs-keeper.json +34 -0
- package/specialists/org/contracts/any-to-explorer.json +39 -0
- package/specialists/org/contracts/any-to-sre-incident.json +33 -0
- package/specialists/org/contracts/any-to-trace-reviewer.json +32 -0
- package/specialists/org/contracts/architect-to-ai-engineer.json +32 -0
- package/specialists/org/contracts/architect-to-data-engineer.json +52 -0
- package/specialists/org/contracts/architect-to-devil-advocate.json +38 -0
- package/specialists/org/contracts/architect-to-engineer.json +34 -0
- package/specialists/org/contracts/architect-to-evaluator.json +59 -0
- package/specialists/org/contracts/architect-to-legal-compliance.json +55 -0
- package/specialists/org/contracts/architect-to-operations.json +45 -0
- package/specialists/org/contracts/architect-to-platform-engineer.json +42 -0
- package/specialists/org/contracts/business-strategist-to-product-manager.json +39 -0
- package/specialists/org/contracts/construct-to-orchestrator.json +36 -0
- package/specialists/org/contracts/construct-to-rd-lead.json +53 -0
- package/specialists/org/contracts/data-analyst-to-product-manager.json +43 -0
- package/specialists/org/contracts/data-engineer-to-platform-engineer.json +36 -0
- package/specialists/org/contracts/designer-to-accessibility.json +36 -0
- package/specialists/org/contracts/engineer-to-qa.json +34 -0
- package/specialists/org/contracts/engineer-to-reviewer.json +52 -0
- package/specialists/org/contracts/explorer-to-engineer.json +38 -0
- package/specialists/org/contracts/legal-compliance-to-release-manager.json +43 -0
- package/specialists/org/contracts/platform-engineer-to-engineer.json +31 -0
- package/specialists/org/contracts/product-manager-to-architect.json +71 -0
- package/specialists/org/contracts/product-manager-to-data-analyst.json +49 -0
- package/specialists/org/contracts/product-manager-to-ux-researcher.json +51 -0
- package/specialists/org/contracts/qa-to-release-manager.json +42 -0
- package/specialists/org/contracts/qa-to-test-automation.json +42 -0
- package/specialists/org/contracts/rd-lead-to-architect.json +38 -0
- package/specialists/org/contracts/researcher-to-architect.json +40 -0
- package/specialists/org/contracts/researcher-to-product-manager.json +57 -0
- package/specialists/org/contracts/reviewer-to-security.json +41 -0
- package/specialists/org/contracts/sre-to-release-manager.json +36 -0
- package/specialists/org/contracts/test-automation-to-engineer.json +34 -0
- package/specialists/org/contracts/trace-reviewer-to-sre.json +41 -0
- package/specialists/org/contracts/user-to-construct.json +30 -0
- package/specialists/org/groups/engineering-group.json +43 -0
- package/specialists/org/groups/governance-group.json +38 -0
- package/specialists/org/groups/operations-group.json +41 -0
- package/specialists/org/groups/product-group.json +46 -0
- package/specialists/org/groups/quality-group.json +42 -0
- package/specialists/org/groups/strategy-group.json +41 -0
- package/specialists/org/policies/action-approval.json +13 -0
- package/specialists/org/policies/agents-routing.json +13 -0
- package/specialists/org/policies/architecture.json +20 -0
- package/specialists/org/policies/bash-safety.json +13 -0
- package/specialists/org/policies/beads-hygiene.json +13 -0
- package/specialists/org/policies/bootstrap-state.json +13 -0
- package/specialists/org/policies/code-review.json +13 -0
- package/specialists/org/policies/coding-style.json +13 -0
- package/specialists/org/policies/comments.json +13 -0
- package/specialists/org/policies/commit-approval.json +13 -0
- package/specialists/org/policies/contract-preconditions.json +13 -0
- package/specialists/org/policies/deployment.json +20 -0
- package/specialists/org/policies/description.json +14 -0
- package/specialists/org/policies/design-approval.json +19 -0
- package/specialists/org/policies/doc-ownership.json +13 -0
- package/specialists/org/policies/file-path-fence.json +13 -0
- package/specialists/org/policies/framing.json +13 -0
- package/specialists/org/policies/git-workflow.json +13 -0
- package/specialists/org/policies/incident-response.json +15 -0
- package/specialists/org/policies/intake-triage.json +15 -0
- package/specialists/org/policies/neurodivergent-output.json +13 -0
- package/specialists/org/policies/no-fabrication.json +13 -0
- package/specialists/org/policies/patterns.json +13 -0
- package/specialists/org/policies/quality-gate-approval.json +17 -0
- package/specialists/org/policies/release-gates.json +13 -0
- package/specialists/org/policies/research-evidence.json +13 -0
- package/specialists/org/policies/review-before-change.json +13 -0
- package/specialists/org/policies/rollback.json +15 -0
- package/specialists/org/policies/scope-change.json +20 -0
- package/specialists/org/policies/secret-scan.json +13 -0
- package/specialists/org/policies/security-approval.json +17 -0
- package/specialists/org/policies/security.json +13 -0
- package/specialists/org/policies/session-efficiency.json +13 -0
- package/specialists/org/policies/skill-routing.json +13 -0
- package/specialists/org/policies/strategic-prioritization.json +17 -0
- package/specialists/org/policies/testing.json +13 -0
- package/specialists/org/policies/tool-invisibility.json +14 -0
- package/specialists/org/scopes/creative.json +54 -0
- package/specialists/org/scopes/operations.json +51 -0
- package/specialists/org/scopes/research.json +60 -0
- package/specialists/org/scopes/rnd.json +81 -0
- package/specialists/org/specialists/cx-accessibility.json +69 -0
- package/specialists/org/specialists/cx-ai-engineer.json +75 -0
- package/specialists/org/specialists/cx-architect.json +89 -0
- package/specialists/org/specialists/cx-business-strategist.json +72 -0
- package/specialists/org/specialists/cx-data-analyst.json +68 -0
- package/specialists/org/specialists/cx-data-engineer.json +71 -0
- package/specialists/org/specialists/cx-debugger.json +75 -0
- package/specialists/org/specialists/cx-designer.json +85 -0
- package/specialists/org/specialists/cx-devil-advocate.json +71 -0
- package/specialists/org/specialists/cx-docs-keeper.json +82 -0
- package/specialists/org/specialists/cx-engineer.json +106 -0
- package/specialists/org/specialists/cx-evaluator.json +69 -0
- package/specialists/org/specialists/cx-explorer.json +69 -0
- package/specialists/org/specialists/cx-legal-compliance.json +74 -0
- package/specialists/org/specialists/cx-operations.json +72 -0
- package/specialists/org/specialists/cx-oracle.json +46 -0
- package/specialists/org/specialists/cx-orchestrator.json +81 -0
- package/specialists/org/specialists/cx-platform-engineer.json +80 -0
- package/specialists/org/specialists/cx-product-manager.json +102 -0
- package/specialists/org/specialists/cx-qa.json +79 -0
- package/specialists/org/specialists/cx-rd-lead.json +73 -0
- package/specialists/org/specialists/cx-release-manager.json +77 -0
- package/specialists/org/specialists/cx-researcher.json +82 -0
- package/specialists/org/specialists/cx-reviewer.json +71 -0
- package/specialists/org/specialists/cx-security.json +91 -0
- package/specialists/org/specialists/cx-sre.json +94 -0
- package/specialists/org/specialists/cx-test-automation.json +69 -0
- package/specialists/org/specialists/cx-trace-reviewer.json +69 -0
- package/specialists/org/specialists/cx-ux-researcher.json +68 -0
- package/specialists/org/teams/accessibility-team.json +39 -0
- package/specialists/org/teams/design-team.json +40 -0
- package/specialists/org/teams/engineering-team.json +50 -0
- package/specialists/org/teams/governance-team.json +41 -0
- package/specialists/org/teams/operations-team.json +46 -0
- package/specialists/org/teams/product-management-team.json +45 -0
- package/specialists/org/teams/quality-team.json +49 -0
- package/specialists/org/teams/research-team.json +39 -0
- package/specialists/org/teams/strategy-team.json +48 -0
- package/specialists/org/teams/ux-research-team.json +39 -0
- package/specialists/prompts/_shared/validation-contract.md +1 -1
- package/specialists/prompts/_team-template.md +10 -0
- package/specialists/prompts/cx-accessibility.md +1 -0
- package/specialists/prompts/cx-ai-engineer.md +1 -1
- package/specialists/prompts/cx-business-strategist.md +1 -1
- package/specialists/prompts/cx-data-engineer.md +1 -1
- package/specialists/prompts/cx-designer.md +1 -0
- package/specialists/prompts/cx-devil-advocate.md +1 -1
- package/specialists/prompts/cx-docs-keeper.md +1 -1
- package/specialists/prompts/cx-evaluator.md +1 -1
- package/specialists/prompts/cx-explorer.md +1 -1
- package/specialists/prompts/cx-operations.md +1 -1
- package/specialists/prompts/cx-oracle.md +7 -3
- package/specialists/prompts/cx-orchestrator.md +17 -1
- package/specialists/prompts/cx-platform-engineer.md +1 -1
- package/specialists/prompts/cx-product-manager.md +2 -0
- package/specialists/prompts/cx-release-manager.md +1 -1
- package/specialists/prompts/cx-researcher.md +7 -4
- package/specialists/prompts/cx-sre.md +1 -1
- package/specialists/prompts/cx-test-automation.md +2 -2
- package/specialists/prompts/cx-trace-reviewer.md +3 -3
- package/specialists/prompts/cx-ux-researcher.md +2 -1
- package/templates/demos/scripts/architecture-review-adr.json +30 -0
- package/templates/demos/scripts/capability-contract.json +25 -0
- package/templates/demos/scripts/intake-triage.json +25 -0
- package/templates/demos/scripts/profile-doctor-health.json +25 -0
- package/templates/demos/tapes/agentic-platforms-prd.tape +2 -2
- package/templates/demos/tapes/architecture-review-adr.tape +44 -0
- package/templates/demos/tapes/capability-contract.tape +39 -0
- package/templates/demos/tapes/intake-triage.tape +39 -0
- package/templates/demos/tapes/profile-doctor-health.tape +39 -0
- package/templates/distribution/construct-brand.typ +23 -18
- package/templates/distribution/construct-decision.typ +1 -1
- package/templates/distribution/construct-pdf.typ +1 -1
- package/templates/distribution/construct-prd.typ +1 -1
- package/templates/distribution/construct-research.typ +1 -1
- package/templates/distribution/mermaid-puppeteer.json +8 -0
- package/templates/docs/persona-artifact.md +1 -1
- package/templates/docs/prd.md +6 -0
- package/lib/boundary.mjs +0 -127
- package/lib/certification/dashboard-api.mjs +0 -71
- package/lib/chat/cli.mjs +0 -333
- package/lib/chat/command-suggest.mjs +0 -161
- package/lib/chat/commands.mjs +0 -215
- package/lib/chat/config.mjs +0 -142
- package/lib/chat/context-compactor.mjs +0 -250
- package/lib/chat/context-continuation.mjs +0 -253
- package/lib/chat/continuation-source.mjs +0 -58
- package/lib/chat/demo-guide.mjs +0 -61
- package/lib/chat/design-tokens.mjs +0 -91
- package/lib/chat/desktop-binary.mjs +0 -81
- package/lib/chat/desktop-build.mjs +0 -130
- package/lib/chat/desktop-launcher.mjs +0 -133
- package/lib/chat/evidence.mjs +0 -145
- package/lib/chat/export.mjs +0 -74
- package/lib/chat/harness/driver.mjs +0 -91
- package/lib/chat/list-picker.mjs +0 -112
- package/lib/chat/model-picker.mjs +0 -356
- package/lib/chat/openrouter-fallback.mjs +0 -151
- package/lib/chat/permission-prompt.mjs +0 -33
- package/lib/chat/picker-catalog.mjs +0 -45
- package/lib/chat/policy-telemetry.mjs +0 -34
- package/lib/chat/present.mjs +0 -246
- package/lib/chat/session-context.mjs +0 -39
- package/lib/chat/session-persist.mjs +0 -73
- package/lib/chat/session-restore.mjs +0 -71
- package/lib/chat/session-settings.mjs +0 -53
- package/lib/chat/system-prompt.mjs +0 -52
- package/lib/chat/transparency.mjs +0 -93
- package/lib/chat/tui/color-scheme.mjs +0 -42
- package/lib/chat/tui/markdown.mjs +0 -123
- package/lib/chat/tui/presentation.mjs +0 -100
- package/lib/chat/tui/render.mjs +0 -500
- package/lib/chat/tui/turn-block.mjs +0 -284
- package/lib/chat/tui/turn-present.mjs +0 -18
- package/lib/chat/tui/turn-state.mjs +0 -88
- package/lib/chat/tui/usage.mjs +0 -122
- package/lib/chat/web-commands.mjs +0 -146
- package/lib/chat/web-launcher.mjs +0 -63
- package/lib/chat/web-picker-keys.mjs +0 -46
- package/lib/chat/web-session.mjs +0 -159
- package/lib/config/alias.mjs +0 -57
- package/lib/dashboard-demo.mjs +0 -71
- package/lib/dashboard-static.mjs +0 -175
- package/lib/install/desktop-binary-download.mjs +0 -88
- package/lib/profiles/loader.mjs +0 -123
- package/lib/profiles/rebrand.mjs +0 -46
- package/lib/server/auth.mjs +0 -169
- package/lib/server/chat-loop.mjs +0 -622
- package/lib/server/chat.mjs +0 -336
- package/lib/server/cors.mjs +0 -77
- package/lib/server/csrf.mjs +0 -103
- package/lib/server/demo-preview.mjs +0 -63
- package/lib/server/index.mjs +0 -2641
- package/lib/server/insights.mjs +0 -780
- package/lib/server/langfuse-login.mjs +0 -58
- package/lib/server/rate-limit.mjs +0 -93
- package/lib/server/telemetry-login.mjs +0 -100
- package/lib/server/webhook.mjs +0 -512
- package/specialists/contracts.json +0 -1032
- package/specialists/contracts.schema.json +0 -83
- package/specialists/policy-inventory.json +0 -188
- package/specialists/registry.json +0 -1412
- package/specialists/role-manifests.json +0 -217
- package/specialists/teams.json +0 -94
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/registry/validator.mjs — Validates unified registry invariants.
|
|
3
|
+
*
|
|
4
|
+
* Enforces 13 invariants that guarantee the registry is internally consistent:
|
|
5
|
+
* teams have owners, specialists have teams, no cycles, decisions are covered by
|
|
6
|
+
* policies, contracts reference valid parties, and escalation paths are sound.
|
|
7
|
+
*
|
|
8
|
+
* Export: validate(registry, opts) → { ok, errors, warnings }
|
|
9
|
+
* Each error/warning has { id, severity, message, location }
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Validate a unified registry against all invariants.
|
|
14
|
+
* @param {object} registry - The unified registry object
|
|
15
|
+
* @param {object} opts - Options (reserved for future use)
|
|
16
|
+
* @returns {{ ok: boolean, errors: Array, warnings: Array }}
|
|
17
|
+
*/
|
|
18
|
+
export function validate(registry, opts = {}) {
|
|
19
|
+
const errors = [];
|
|
20
|
+
const warnings = [];
|
|
21
|
+
|
|
22
|
+
if (!registry || typeof registry !== 'object') {
|
|
23
|
+
return {
|
|
24
|
+
ok: false,
|
|
25
|
+
errors: [{ id: 'invalid-input', severity: 'error', message: 'Registry must be a non-null object', location: '#' }],
|
|
26
|
+
warnings: [],
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Schema compliance must pass before graph traversal; structural errors short-circuit.
|
|
31
|
+
|
|
32
|
+
const schemaCheck = checkSchemaCompliance(registry);
|
|
33
|
+
errors.push(...schemaCheck.errors);
|
|
34
|
+
warnings.push(...schemaCheck.warnings);
|
|
35
|
+
if (schemaCheck.errors.length > 0) {
|
|
36
|
+
return { ok: false, errors, warnings };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const teams = registry.teams || {};
|
|
40
|
+
const specialists = registry.specialists || {};
|
|
41
|
+
const contracts = registry.contracts || {};
|
|
42
|
+
const policies = registry.policies || {};
|
|
43
|
+
|
|
44
|
+
// Team and specialist graph: owners, membership, and naming collisions.
|
|
45
|
+
|
|
46
|
+
errors.push(...checkTeamHasOwner(teams, specialists));
|
|
47
|
+
errors.push(...checkSpecialistTeamExists(specialists, teams));
|
|
48
|
+
errors.push(...checkTeamHasSpecialists(teams, specialists));
|
|
49
|
+
errors.push(...checkNoNameCollisions(specialists));
|
|
50
|
+
|
|
51
|
+
// Policy coverage for team decision rights and forbidden actions.
|
|
52
|
+
|
|
53
|
+
warnings.push(...checkDecisionHasPolicy(teams, policies));
|
|
54
|
+
warnings.push(...checkForbiddenDecisions(teams, policies));
|
|
55
|
+
|
|
56
|
+
// Escalation paths must resolve specialists and remain acyclic.
|
|
57
|
+
|
|
58
|
+
errors.push(...checkEscalationPathValid(teams, specialists));
|
|
59
|
+
errors.push(...checkNoCircularEscalation(teams));
|
|
60
|
+
|
|
61
|
+
// Contracts and policies must reference teams and specialists that exist.
|
|
62
|
+
|
|
63
|
+
errors.push(...checkContractPartiesExist(contracts, specialists));
|
|
64
|
+
errors.push(...checkContractTeamBoundaries(contracts, teams));
|
|
65
|
+
errors.push(...checkPolicyTeamOwnerExists(policies, teams));
|
|
66
|
+
errors.push(...checkPolicyApproverTeamsExist(policies, teams));
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
ok: errors.length === 0,
|
|
70
|
+
errors,
|
|
71
|
+
warnings,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// === Individual invariant checks ===
|
|
76
|
+
|
|
77
|
+
function checkSchemaCompliance(registry) {
|
|
78
|
+
const errors = [];
|
|
79
|
+
const warnings = [];
|
|
80
|
+
|
|
81
|
+
if (registry.version !== 2 && registry.version !== 3) {
|
|
82
|
+
errors.push({
|
|
83
|
+
id: 'invalid-version',
|
|
84
|
+
severity: 'error',
|
|
85
|
+
message: `Schema version must be 2 or 3, got ${registry.version}`,
|
|
86
|
+
location: '#/version',
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (typeof registry.teams !== 'object' || registry.teams === null) {
|
|
91
|
+
errors.push({
|
|
92
|
+
id: 'missing-teams',
|
|
93
|
+
severity: 'error',
|
|
94
|
+
message: 'teams must be an object',
|
|
95
|
+
location: '#/teams',
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (typeof registry.specialists !== 'object' || registry.specialists === null) {
|
|
100
|
+
errors.push({
|
|
101
|
+
id: 'missing-specialists',
|
|
102
|
+
severity: 'error',
|
|
103
|
+
message: 'specialists must be an object',
|
|
104
|
+
location: '#/specialists',
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (typeof registry.contracts !== 'object' || registry.contracts === null) {
|
|
109
|
+
errors.push({
|
|
110
|
+
id: 'missing-contracts',
|
|
111
|
+
severity: 'error',
|
|
112
|
+
message: 'contracts must be an object',
|
|
113
|
+
location: '#/contracts',
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (typeof registry.policies !== 'object' || registry.policies === null) {
|
|
118
|
+
errors.push({
|
|
119
|
+
id: 'missing-policies',
|
|
120
|
+
severity: 'error',
|
|
121
|
+
message: 'policies must be an object',
|
|
122
|
+
location: '#/policies',
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return { errors, warnings };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function isGroup(team) {
|
|
130
|
+
return team?.kind === 'group';
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function checkTeamHasOwner(teams, specialists) {
|
|
134
|
+
const errors = [];
|
|
135
|
+
|
|
136
|
+
for (const [teamId, team] of Object.entries(teams)) {
|
|
137
|
+
if (isGroup(team)) continue;
|
|
138
|
+
|
|
139
|
+
const ownerRole = team.owner;
|
|
140
|
+
if (!ownerRole) {
|
|
141
|
+
errors.push({
|
|
142
|
+
id: 'team-missing-owner',
|
|
143
|
+
severity: 'error',
|
|
144
|
+
message: `Team ${teamId} missing owner field`,
|
|
145
|
+
location: `#/teams/${teamId}/owner`,
|
|
146
|
+
});
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const hasOwner = Object.values(specialists).some(
|
|
151
|
+
(spec) => spec.team === teamId && (spec.role === 'owner' || spec.role === ownerRole),
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
if (!hasOwner) {
|
|
155
|
+
errors.push({
|
|
156
|
+
id: 'team-no-owner-specialist',
|
|
157
|
+
severity: 'error',
|
|
158
|
+
message: `Team ${teamId} has owner role ${ownerRole} but no specialist with that role assigned`,
|
|
159
|
+
location: `#/teams/${teamId}`,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return errors;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function checkSpecialistTeamExists(specialists, teams) {
|
|
168
|
+
const errors = [];
|
|
169
|
+
const teamIds = Object.keys(teams);
|
|
170
|
+
|
|
171
|
+
for (const [specId, spec] of Object.entries(specialists)) {
|
|
172
|
+
if (!spec.team) {
|
|
173
|
+
errors.push({
|
|
174
|
+
id: 'specialist-missing-team',
|
|
175
|
+
severity: 'error',
|
|
176
|
+
message: `Specialist ${specId} missing team assignment`,
|
|
177
|
+
location: `#/specialists/${specId}/team`,
|
|
178
|
+
});
|
|
179
|
+
} else if (!teamIds.includes(spec.team) && spec.team !== 'unknown') {
|
|
180
|
+
errors.push({
|
|
181
|
+
id: 'specialist-unknown-team',
|
|
182
|
+
severity: 'error',
|
|
183
|
+
message: `Specialist ${specId} references unknown team ${spec.team}`,
|
|
184
|
+
location: `#/specialists/${specId}/team`,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return errors;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function checkTeamHasSpecialists(teams, specialists) {
|
|
193
|
+
const errors = [];
|
|
194
|
+
const specsByTeam = {};
|
|
195
|
+
|
|
196
|
+
for (const spec of Object.values(specialists)) {
|
|
197
|
+
if (spec.team) {
|
|
198
|
+
if (!specsByTeam[spec.team]) specsByTeam[spec.team] = [];
|
|
199
|
+
specsByTeam[spec.team].push(spec);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
for (const teamId of Object.keys(teams)) {
|
|
204
|
+
const team = teams[teamId];
|
|
205
|
+
if (isGroup(team)) continue;
|
|
206
|
+
|
|
207
|
+
if (!specsByTeam[teamId] || specsByTeam[teamId].length === 0) {
|
|
208
|
+
errors.push({
|
|
209
|
+
id: 'team-no-specialists',
|
|
210
|
+
severity: 'error',
|
|
211
|
+
message: `Team ${teamId} has no assigned specialists`,
|
|
212
|
+
location: `#/teams/${teamId}`,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return errors;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function checkNoNameCollisions(specialists) {
|
|
221
|
+
const errors = [];
|
|
222
|
+
const seen = new Map();
|
|
223
|
+
|
|
224
|
+
for (const [specId, spec] of Object.entries(specialists)) {
|
|
225
|
+
if (seen.has(spec.name)) {
|
|
226
|
+
errors.push({
|
|
227
|
+
id: 'specialist-name-collision',
|
|
228
|
+
severity: 'error',
|
|
229
|
+
message: `Specialist name collision: ${spec.name} appears in ${seen.get(spec.name)} and ${specId}`,
|
|
230
|
+
location: `#/specialists/${specId}/name`,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
seen.set(spec.name, specId);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return errors;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function checkDecisionHasPolicy(teams, policies) {
|
|
240
|
+
const warnings = [];
|
|
241
|
+
const policyIds = Object.keys(policies);
|
|
242
|
+
|
|
243
|
+
for (const [teamId, team] of Object.entries(teams)) {
|
|
244
|
+
const rights = team.decisionRights || [];
|
|
245
|
+
for (const right of rights) {
|
|
246
|
+
if (!policyIds.includes(right)) {
|
|
247
|
+
warnings.push({
|
|
248
|
+
id: 'decision-no-policy',
|
|
249
|
+
severity: 'warning',
|
|
250
|
+
message: `Team ${teamId} claims decision right ${right} but no policy with that id exists`,
|
|
251
|
+
location: `#/teams/${teamId}/decisionRights`,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return warnings;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function checkForbiddenDecisions(teams, policies) {
|
|
261
|
+
const warnings = [];
|
|
262
|
+
|
|
263
|
+
for (const [teamId, team] of Object.entries(teams)) {
|
|
264
|
+
const forbidden = team.forbiddenDecisions || [];
|
|
265
|
+
for (const decision of forbidden) {
|
|
266
|
+
if (!(decision in policies)) {
|
|
267
|
+
warnings.push({
|
|
268
|
+
id: 'forbidden-decision-invalid',
|
|
269
|
+
severity: 'warning',
|
|
270
|
+
message: `Team ${teamId} forbids decision ${decision} but it is not a recognized decision`,
|
|
271
|
+
location: `#/teams/${teamId}/forbiddenDecisions`,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return warnings;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function checkEscalationPathValid(teams, specialists) {
|
|
281
|
+
const errors = [];
|
|
282
|
+
|
|
283
|
+
// Build a set of all valid roles (from all teams)
|
|
284
|
+
const allRoles = new Set();
|
|
285
|
+
for (const team of Object.values(teams)) {
|
|
286
|
+
for (const role of team.roles || []) {
|
|
287
|
+
allRoles.add(role);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
for (const [teamId, team] of Object.entries(teams)) {
|
|
292
|
+
const path = team.escalationPath || [];
|
|
293
|
+
for (let i = 0; i < path.length; i++) {
|
|
294
|
+
const role = path[i];
|
|
295
|
+
if (role === 'orchestrator') continue; // orchestrator is always valid
|
|
296
|
+
|
|
297
|
+
// Check if this role exists in any team's roles
|
|
298
|
+
if (!allRoles.has(role)) {
|
|
299
|
+
errors.push({
|
|
300
|
+
id: 'escalation-path-invalid-role',
|
|
301
|
+
severity: 'error',
|
|
302
|
+
message: `Team ${teamId} escalation path references role ${role} at index ${i} with no team claiming that role`,
|
|
303
|
+
location: `#/teams/${teamId}/escalationPath/${i}`,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return errors;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function checkNoCircularEscalation(teams) {
|
|
313
|
+
const errors = [];
|
|
314
|
+
|
|
315
|
+
for (const [teamId, team] of Object.entries(teams)) {
|
|
316
|
+
const path = team.escalationPath || [];
|
|
317
|
+
const visited = new Set();
|
|
318
|
+
|
|
319
|
+
for (const role of path) {
|
|
320
|
+
if (visited.has(role)) {
|
|
321
|
+
errors.push({
|
|
322
|
+
id: 'circular-escalation',
|
|
323
|
+
severity: 'error',
|
|
324
|
+
message: `Team ${teamId} escalation path contains cycle: ${role} appears twice`,
|
|
325
|
+
location: `#/teams/${teamId}/escalationPath`,
|
|
326
|
+
});
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
visited.add(role);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return errors;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function checkContractPartiesExist(contracts, specialists) {
|
|
337
|
+
const errors = [];
|
|
338
|
+
const specialistIds = new Set(Object.keys(specialists));
|
|
339
|
+
const validParties = new Set(['user', 'construct', '*', ...specialistIds]); // * = wildcard producer
|
|
340
|
+
|
|
341
|
+
for (const [contractId, contract] of Object.entries(contracts)) {
|
|
342
|
+
if (!validParties.has(contract.producer)) {
|
|
343
|
+
errors.push({
|
|
344
|
+
id: 'contract-unknown-producer',
|
|
345
|
+
severity: 'error',
|
|
346
|
+
message: `Contract ${contractId} references unknown producer ${contract.producer}`,
|
|
347
|
+
location: `#/contracts/${contractId}/producer`,
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (!validParties.has(contract.consumer)) {
|
|
352
|
+
errors.push({
|
|
353
|
+
id: 'contract-unknown-consumer',
|
|
354
|
+
severity: 'error',
|
|
355
|
+
message: `Contract ${contractId} references unknown consumer ${contract.consumer}`,
|
|
356
|
+
location: `#/contracts/${contractId}/consumer`,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return errors;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function checkContractTeamBoundaries(contracts, teams) {
|
|
365
|
+
const errors = [];
|
|
366
|
+
const teamIds = Object.keys(teams);
|
|
367
|
+
|
|
368
|
+
for (const [contractId, contract] of Object.entries(contracts)) {
|
|
369
|
+
if (contract.teamBoundary) {
|
|
370
|
+
const { producerTeam, consumerTeam } = contract.teamBoundary;
|
|
371
|
+
|
|
372
|
+
if (producerTeam && !teamIds.includes(producerTeam)) {
|
|
373
|
+
errors.push({
|
|
374
|
+
id: 'contract-unknown-team',
|
|
375
|
+
severity: 'error',
|
|
376
|
+
message: `Contract ${contractId} teamBoundary references unknown team ${producerTeam}`,
|
|
377
|
+
location: `#/contracts/${contractId}/teamBoundary/producerTeam`,
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (consumerTeam && !teamIds.includes(consumerTeam)) {
|
|
382
|
+
errors.push({
|
|
383
|
+
id: 'contract-unknown-team',
|
|
384
|
+
severity: 'error',
|
|
385
|
+
message: `Contract ${contractId} teamBoundary references unknown team ${consumerTeam}`,
|
|
386
|
+
location: `#/contracts/${contractId}/teamBoundary/consumerTeam`,
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return errors;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function checkPolicyTeamOwnerExists(policies, teams) {
|
|
396
|
+
const errors = [];
|
|
397
|
+
const teamIds = Object.keys(teams);
|
|
398
|
+
|
|
399
|
+
for (const [policyId, policy] of Object.entries(policies)) {
|
|
400
|
+
if (policy.owner && !teamIds.includes(policy.owner) && policy.owner !== 'orchestrator') {
|
|
401
|
+
errors.push({
|
|
402
|
+
id: 'policy-unknown-owner',
|
|
403
|
+
severity: 'error',
|
|
404
|
+
message: `Policy ${policyId} owner team ${policy.owner} does not exist`,
|
|
405
|
+
location: `#/policies/${policyId}/owner`,
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return errors;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function checkPolicyApproverTeamsExist(policies, teams) {
|
|
414
|
+
const errors = [];
|
|
415
|
+
const teamIds = Object.keys(teams);
|
|
416
|
+
const policyIds = Object.keys(policies);
|
|
417
|
+
|
|
418
|
+
for (const [policyId, policy] of Object.entries(policies)) {
|
|
419
|
+
const approvers = policy.requiresApprovalFrom || [];
|
|
420
|
+
for (let i = 0; i < approvers.length; i++) {
|
|
421
|
+
const approverId = approvers[i];
|
|
422
|
+
// approvers can be team IDs or policy IDs (for nested approval requirements)
|
|
423
|
+
const isTeamId = teamIds.includes(approverId);
|
|
424
|
+
const isPolicyId = policyIds.includes(approverId);
|
|
425
|
+
|
|
426
|
+
if (!isTeamId && !isPolicyId) {
|
|
427
|
+
errors.push({
|
|
428
|
+
id: 'policy-unknown-approver',
|
|
429
|
+
severity: 'error',
|
|
430
|
+
message: `Policy ${policyId} requiresApprovalFrom references unknown team or policy ${approverId}`,
|
|
431
|
+
location: `#/policies/${policyId}/requiresApprovalFrom/${i}`,
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
return errors;
|
|
438
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/research-execution-policy.mjs — surface-agnostic research routing policy.
|
|
3
|
+
*
|
|
4
|
+
* Research is not tied to one MCP. Hosts can have Context7, plain web search,
|
|
5
|
+
* attached files, or only repo access. This module returns the same evidence
|
|
6
|
+
* ladder to every surface so research remains useful inside a project, outside a
|
|
7
|
+
* project, or in a mixed conversation that turns into artifact drafting.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const LIBRARY_DOC_PATTERNS = [
|
|
11
|
+
/\b(api|sdk|framework|library|package|module|cli|tooling?)\b/i,
|
|
12
|
+
/\bdocs?|documentation|reference|changelog|release notes?|migration guide\b/i,
|
|
13
|
+
/\breact|next\.?js|node\.?js|typescript|python|go|rust|java|spring|django|rails|tailwind|prisma|aws|gcp|azure|kubernetes|terraform\b/i,
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
const SECURITY_PATTERNS = [
|
|
17
|
+
/\bcve\b/i,
|
|
18
|
+
/\bvulnerabilit(?:y|ies)\b/i,
|
|
19
|
+
/\badvisory|exploit|cwe|cvss|owasp|supply chain\b/i,
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
const LITERATURE_PATTERNS = [
|
|
23
|
+
/\bpaper|study|benchmark|arxiv|anthology|proceedings|dataset|experiment|literature\b/i,
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const MARKET_PATTERNS = [
|
|
27
|
+
/\bmarket|pricing|adoption|competition|competitive|vendor|arr|revenue|survey|share\b/i,
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
const REGULATORY_PATTERNS = [
|
|
31
|
+
/\bregulation|regulatory|compliance|standard|policy|law|gdpr|hipaa|soc 2|iso 27001|fedramp\b/i,
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
const REPO_PATTERNS = [
|
|
35
|
+
/\bthis (repo|repository|project|codebase)\b/i,
|
|
36
|
+
/\bin this (repo|repository|project|codebase)\b/i,
|
|
37
|
+
/\bour (repo|repository|project|codebase)\b/i,
|
|
38
|
+
/\bmodule\b|\bfunction\b|\bimplementation\b|\bsource code\b/i,
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
const CONSTRUCT_SELF_PATTERNS = [
|
|
42
|
+
/\bconstruct\b/i,
|
|
43
|
+
/\bthis tool\b/i,
|
|
44
|
+
/\bthis system\b/i,
|
|
45
|
+
/\bthis agent\b/i,
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
export function classifyResearchDomain(request = '') {
|
|
49
|
+
const text = String(request || '');
|
|
50
|
+
if (!text.trim()) return 'general-external';
|
|
51
|
+
if (REPO_PATTERNS.some((pattern) => pattern.test(text))) return 'repo';
|
|
52
|
+
if (CONSTRUCT_SELF_PATTERNS.some((pattern) => pattern.test(text))) return 'construct-self';
|
|
53
|
+
if (SECURITY_PATTERNS.some((pattern) => pattern.test(text))) return 'security';
|
|
54
|
+
if (LITERATURE_PATTERNS.some((pattern) => pattern.test(text))) return 'literature';
|
|
55
|
+
if (MARKET_PATTERNS.some((pattern) => pattern.test(text))) return 'market';
|
|
56
|
+
if (REGULATORY_PATTERNS.some((pattern) => pattern.test(text))) return 'regulatory';
|
|
57
|
+
if (LIBRARY_DOC_PATTERNS.some((pattern) => pattern.test(text))) return 'library-docs';
|
|
58
|
+
return 'general-external';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function domainStartingPoints(domain) {
|
|
62
|
+
switch (domain) {
|
|
63
|
+
case 'construct-self':
|
|
64
|
+
return ['Construct knowledge base and shipped docs'];
|
|
65
|
+
case 'repo':
|
|
66
|
+
return ['repo code', 'repo tests', 'repo docs', 'attached or ingested project files'];
|
|
67
|
+
case 'library-docs':
|
|
68
|
+
return ['official versioned docs', 'official changelog', 'official migration guide', 'release notes'];
|
|
69
|
+
case 'security':
|
|
70
|
+
return ['NVD', 'GitHub Security Advisories', 'OWASP', 'vendor advisories'];
|
|
71
|
+
case 'literature':
|
|
72
|
+
return ['papers', 'conference proceedings', 'official benchmark writeups', 'dataset docs'];
|
|
73
|
+
case 'market':
|
|
74
|
+
return ['company announcements', 'SEC filings', 'official pricing/docs', 'named primary-source reporting'];
|
|
75
|
+
case 'regulatory':
|
|
76
|
+
return ['regulation text', 'issuing authority guidance', 'official standard text'];
|
|
77
|
+
default:
|
|
78
|
+
return ['most authoritative primary source for the claim', 'official docs', 'standards', 'source code', 'first-party announcements'];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function externalLadder(domain) {
|
|
83
|
+
if (domain === 'construct-self') {
|
|
84
|
+
return [
|
|
85
|
+
{
|
|
86
|
+
step: 'construct-knowledge',
|
|
87
|
+
when: 'The question is about Construct itself.',
|
|
88
|
+
action: 'Use Construct knowledge and shipped docs first.',
|
|
89
|
+
preferredTools: ['knowledge_search'],
|
|
90
|
+
},
|
|
91
|
+
];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (domain === 'repo') {
|
|
95
|
+
return [
|
|
96
|
+
{
|
|
97
|
+
step: 'repo-evidence',
|
|
98
|
+
when: 'The question is about the current project/repo or attached materials.',
|
|
99
|
+
action: 'Read code, tests, configs, prior artifacts, and ingested files before any external search.',
|
|
100
|
+
preferredTools: ['repo file search', 'project docs', 'ingested artifact search'],
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
step: 'targeted-external',
|
|
104
|
+
when: 'Local evidence is insufficient and the question depends on an external standard or dependency.',
|
|
105
|
+
action: 'Fetch the exact external primary source that governs the claim.',
|
|
106
|
+
preferredTools: ['context7 when it is a library/API docs question', 'web search + direct fetch of official docs otherwise'],
|
|
107
|
+
},
|
|
108
|
+
];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (domain === 'library-docs') {
|
|
112
|
+
return [
|
|
113
|
+
{
|
|
114
|
+
step: 'context7-preferred',
|
|
115
|
+
when: 'Context7 is available and the topic is a library/framework/API/CLI/cloud-docs question.',
|
|
116
|
+
action: 'Use Context7 for current versioned documentation.',
|
|
117
|
+
preferredTools: ['context7'],
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
step: 'official-web-fallback',
|
|
121
|
+
when: 'Context7 is unavailable, incomplete, or the answer needs confirmation.',
|
|
122
|
+
action: 'Search and fetch official docs, changelogs, migration guides, and release notes directly on the web.',
|
|
123
|
+
preferredTools: ['web search', 'direct fetch/open of official docs'],
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
step: 'secondary-locator',
|
|
127
|
+
when: 'Official docs are hard to locate.',
|
|
128
|
+
action: 'Use secondary sources only to locate the primary source; do not treat them as final evidence.',
|
|
129
|
+
preferredTools: ['web search'],
|
|
130
|
+
},
|
|
131
|
+
];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return [
|
|
135
|
+
{
|
|
136
|
+
step: 'primary-external',
|
|
137
|
+
when: 'The question concerns external facts beyond the current repo.',
|
|
138
|
+
action: 'Go directly to the domain-appropriate primary sources.',
|
|
139
|
+
preferredTools: ['web search', 'direct fetch/open of primary sources'],
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
step: 'secondary-locator',
|
|
143
|
+
when: 'Primary sources need discovery help.',
|
|
144
|
+
action: 'Use secondary/tertiary sources only to locate the primary source, then verify against the primary.',
|
|
145
|
+
preferredTools: ['web search'],
|
|
146
|
+
},
|
|
147
|
+
];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function buildResearchExecutionPolicy({ request = '' } = {}) {
|
|
151
|
+
const domain = classifyResearchDomain(request);
|
|
152
|
+
return {
|
|
153
|
+
mode: 'evidence-first',
|
|
154
|
+
supportsGeneralConversation: true,
|
|
155
|
+
canResearchInsideOrOutsideProject: true,
|
|
156
|
+
domain,
|
|
157
|
+
internalFirst: [
|
|
158
|
+
'user-supplied files and links',
|
|
159
|
+
'current repo code, docs, tests, and configs when relevant',
|
|
160
|
+
'prior project research and ingested knowledge when relevant',
|
|
161
|
+
],
|
|
162
|
+
startingPoints: domainStartingPoints(domain),
|
|
163
|
+
toolRouting: externalLadder(domain),
|
|
164
|
+
sourceRules: [
|
|
165
|
+
'Prefer primary sources for load-bearing claims.',
|
|
166
|
+
'Use Context7 only for library/framework/API/cloud docs when available.',
|
|
167
|
+
'Fall back to web search plus direct fetch of official docs when Context7 is absent or insufficient.',
|
|
168
|
+
'For non-docs research, use domain-appropriate primary sources rather than a generic docs MCP.',
|
|
169
|
+
'Community/blog/forum content may help locate sources or capture sentiment, but not replace primary evidence for factual claims.',
|
|
170
|
+
],
|
|
171
|
+
recencyRule: 'Search most-recent-first. For fast-moving topics, treat sources older than 12 months as stale until reconfirmed.',
|
|
172
|
+
};
|
|
173
|
+
}
|
package/lib/roles/catalog.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* lib/roles/catalog.mjs — role listing and display helpers.
|
|
3
3
|
*
|
|
4
|
-
* Reads the flat `specialists` array in specialists/
|
|
4
|
+
* Reads the flat `specialists` array in specialists/org to produce the
|
|
5
5
|
* list of available roles. Each descriptor carries the registry-prefixed display
|
|
6
6
|
* name (cx-<name>), description, model tier, edit capability, and skill ids — the
|
|
7
7
|
* shape consumed by the `roles:list` CLI command and the embedded capability
|
|
@@ -12,18 +12,13 @@
|
|
|
12
12
|
* the flat list only and never invents grouping that has no source.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import {
|
|
16
|
-
import { dirname, join } from 'node:path';
|
|
17
|
-
import { fileURLToPath } from 'node:url';
|
|
18
|
-
|
|
19
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
20
|
-
const REGISTRY_PATH = join(__dirname, '..', '..', 'specialists', 'registry.json');
|
|
15
|
+
import { loadRegistry as loadAssembledRegistry } from '../registry/loader.mjs';
|
|
21
16
|
|
|
22
17
|
let cached = null;
|
|
23
18
|
|
|
24
19
|
function loadRegistry() {
|
|
25
20
|
if (cached) return cached;
|
|
26
|
-
cached =
|
|
21
|
+
cached = loadAssembledRegistry();
|
|
27
22
|
return cached;
|
|
28
23
|
}
|
|
29
24
|
|
|
@@ -35,7 +30,7 @@ function loadRegistry() {
|
|
|
35
30
|
export function listRoles() {
|
|
36
31
|
const registry = loadRegistry();
|
|
37
32
|
const prefix = registry.prefix || 'cx';
|
|
38
|
-
const specialists =
|
|
33
|
+
const specialists = Object.values(registry.specialists || {});
|
|
39
34
|
|
|
40
35
|
return specialists.map((s) => ({
|
|
41
36
|
id: s.name,
|