@geraldmaron/construct 1.2.3 → 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 +235 -209
- 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 +55 -54
- package/lib/comment-lint.mjs +1 -5
- package/lib/completions.mjs +20 -2
- package/lib/config/legacy-config-migration.mjs +90 -24
- 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/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 +6 -26
- 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 -8
- 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/apps/chat/engine/ai-sdk-agent.mjs +0 -183
- package/apps/chat/engine/loop-driver.mjs +0 -211
- package/apps/chat/engine/models.mjs +0 -122
- package/apps/chat/engine/provider-adapters.mjs +0 -171
- package/apps/chat/engine/tools/permission.mjs +0 -54
- package/apps/chat/engine/tools/primitives.mjs +0 -180
- package/apps/chat/engine/tools/registry.mjs +0 -122
- package/apps/chat/engine/turn-controls.mjs +0 -70
- 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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* lib/
|
|
2
|
+
* lib/scopes/lifecycle.mjs — Scope lifecycle operations.
|
|
3
3
|
*
|
|
4
|
-
* A
|
|
4
|
+
* A scope is a curated description of an org's work loop, intake taxonomy,
|
|
5
5
|
* role set, and rebrand language. Creating one is a research task, not a
|
|
6
6
|
* code task. The module enforces that by producing a draft + a requirements
|
|
7
7
|
* brief that names which existing Construct specialists are expected to
|
|
@@ -9,44 +9,44 @@
|
|
|
9
9
|
* answers, then promote the draft.
|
|
10
10
|
*
|
|
11
11
|
* Lifecycle stages:
|
|
12
|
-
* draft - in `.cx/
|
|
13
|
-
* active - in `
|
|
14
|
-
* archived - in `archive/
|
|
12
|
+
* draft - in `.cx/scopes/draft-<id>/` with requirements.md + scope.json
|
|
13
|
+
* active - in `specialists/org/scopes/<id>.json` (curated) or `.cx/scope.json` (custom)
|
|
14
|
+
* archived - in `archive/scopes/<id>/` with the final state and an
|
|
15
15
|
* archive-note.md explaining why it was retired
|
|
16
16
|
*
|
|
17
|
-
* Health: per-
|
|
18
|
-
* stores, so
|
|
17
|
+
* Health: per-scope observation and outcome counts pulled from the existing
|
|
18
|
+
* stores, so scope health travels alongside the rest of the learning loops.
|
|
19
19
|
*
|
|
20
|
-
* Lifecycle events:
|
|
21
|
-
*
|
|
20
|
+
* Lifecycle events: createDraftScope and archiveScope each fire a
|
|
21
|
+
* scope.updated role event so subscribers can react to taxonomy changes
|
|
22
22
|
* without polling the filesystem.
|
|
23
23
|
*/
|
|
24
24
|
import fs from 'node:fs';
|
|
25
25
|
import path from 'node:path';
|
|
26
26
|
import { fileURLToPath } from 'node:url';
|
|
27
27
|
|
|
28
|
-
import {
|
|
28
|
+
import { listScopes, loadScope } from './loader.mjs';
|
|
29
29
|
import { emitBestEffort as emitRoleEvent } from '../roles/event-bus.mjs';
|
|
30
30
|
|
|
31
31
|
const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
32
32
|
const REPO_ROOT = path.resolve(MODULE_DIR, '..', '..');
|
|
33
33
|
|
|
34
34
|
function draftDir(cwd, id) {
|
|
35
|
-
return path.join(cwd, '.cx', '
|
|
35
|
+
return path.join(cwd, '.cx', 'scopes', `draft-${id}`);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
function archiveDir(cwd, id) {
|
|
39
|
-
return path.join(cwd, 'archive', '
|
|
39
|
+
return path.join(cwd, 'archive', 'scopes', id);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
const REQUIREMENTS_BRIEF = (id, displayName) => `#
|
|
42
|
+
const REQUIREMENTS_BRIEF = (id, displayName) => `# Scope requirements: ${displayName} (${id})
|
|
43
43
|
|
|
44
|
-
This brief is a discovery and design contract for a new Construct
|
|
44
|
+
This brief is a discovery and design contract for a new Construct scope. Methodology lives in \`docs/guides/concepts/persona-research.md\` and lifecycle in \`docs/guides/concepts/scope-lifecycle.md\`. Treat each section as a task to dispatch to the named specialist. A draft scope is not ready to promote until every section has evidence backing it. The aim is research-grounded, not opinion-grounded.
|
|
45
45
|
|
|
46
46
|
Specialists used here are the existing Construct cx-* personas (already in the registry). They are dispatched the standard way; this doc just names which questions each one owns.
|
|
47
47
|
|
|
48
48
|
This draft scaffolding produces:
|
|
49
|
-
- \`
|
|
49
|
+
- \`scope.json\` — the structural definition (departments, roles, intake)
|
|
50
50
|
- \`requirements.md\` — this brief
|
|
51
51
|
- \`personas/<role>.md\` — one persona artifact per role (filled during the discover phase)
|
|
52
52
|
- \`departments/<dept>.md\` — one charter doc per department (filled during the frame phase)
|
|
@@ -77,17 +77,17 @@ Goal: turn discovery into an intake taxonomy and a stage sequence.
|
|
|
77
77
|
|
|
78
78
|
Goal: define the role set and how it interlocks with the curated cx-* registry.
|
|
79
79
|
|
|
80
|
-
- List role ids the
|
|
80
|
+
- List role ids the scope will reference (max 80, grouped into max 12 departments with max 20 roles per department). For each, indicate one of:
|
|
81
81
|
reuse-existing (name the cx-* agent), create-new (specify scope), or compose-overlay (which base role + which flavor).
|
|
82
82
|
- Identify cross-role handoffs. Where can ambiguity stall the loop? Name the orchestrator role.
|
|
83
|
-
- Validate against the per-role cap (max 6 flavors per role per
|
|
84
|
-
- Acceptance: every declared role either exists in specialists/
|
|
83
|
+
- Validate against the per-role cap (max 6 flavors per role per scope).
|
|
84
|
+
- Acceptance: every declared role either exists in specialists/org or has a written scope statement.
|
|
85
85
|
|
|
86
86
|
## 4. Validation (cx-evaluator)
|
|
87
87
|
|
|
88
88
|
Goal: prove the draft works on real signals before promotion.
|
|
89
89
|
|
|
90
|
-
- Run \`construct
|
|
90
|
+
- Run \`construct scope classify --draft=${id} --fixture=<path>\` against at least 5 representative signals.
|
|
91
91
|
- Score: precision (right intake type), recall (no false unknowns), routing-confidence (median).
|
|
92
92
|
- Acceptance: precision >= 0.7, recall >= 0.7, no \`unknown\` for the canonical signals.
|
|
93
93
|
|
|
@@ -95,29 +95,29 @@ Goal: prove the draft works on real signals before promotion.
|
|
|
95
95
|
|
|
96
96
|
Goal: move the draft into the active catalog.
|
|
97
97
|
|
|
98
|
-
- Curated path: hand-edit \`
|
|
99
|
-
- Custom path: copy the draft
|
|
98
|
+
- Curated path: hand-edit \`specialists/org/scopes/${id}.json\` from this draft, open a PR, run \`npm run lint:scopes\`.
|
|
99
|
+
- Custom path: copy the draft scope.json to \`<project>/.cx/scope.json\` with \`"custom": true\`; \`construct scope set\` for switching between curated.
|
|
100
100
|
- Either path requires the validation acceptance criteria above to be met.
|
|
101
101
|
|
|
102
102
|
## 6. Health monitoring (cx-evaluator + cx-trace-reviewer)
|
|
103
103
|
|
|
104
|
-
Goal: keep the
|
|
104
|
+
Goal: keep the scope honest after it ships.
|
|
105
105
|
|
|
106
|
-
- \`construct
|
|
106
|
+
- \`construct scope health ${id}\` reports observation counts, per-role outcome rates, classification confidence over a window.
|
|
107
107
|
- Any role with success-rate < 0.5 across 10+ runs triggers a review: is the role wrong, the prompt wrong, or the routing wrong?
|
|
108
|
-
- Health data is the input for future
|
|
108
|
+
- Health data is the input for future scope revisions; never edit a scope without a health report first.
|
|
109
109
|
|
|
110
110
|
## 7. Archive (cx-docs-keeper + operator)
|
|
111
111
|
|
|
112
|
-
Goal: retire a
|
|
112
|
+
Goal: retire a scope cleanly without losing the learning.
|
|
113
113
|
|
|
114
|
-
- \`construct
|
|
115
|
-
- Observations and outcomes already recorded under this
|
|
114
|
+
- \`construct scope archive ${id}\` moves the scope JSON and the intake table reference into \`archive/scopes/${id}/\`, including the final health report as evidence.
|
|
115
|
+
- Observations and outcomes already recorded under this scope stay in place; they are durable.
|
|
116
116
|
- An archive-note.md explains why it was retired (superseded by, deprecated for, merged into).
|
|
117
117
|
`;
|
|
118
118
|
|
|
119
119
|
const DRAFT_PROFILE_TEMPLATE = (id, displayName) => ({
|
|
120
|
-
$schema: '../schemas/
|
|
120
|
+
$schema: '../schemas/scope.schema.json',
|
|
121
121
|
id,
|
|
122
122
|
displayName: displayName || id,
|
|
123
123
|
tagline: 'Draft. Fill in via the requirements brief.',
|
|
@@ -200,11 +200,11 @@ function departmentTemplate(id, displayName) {
|
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
/**
|
|
203
|
-
* Scaffold a draft
|
|
204
|
-
* to the requirements brief and the draft
|
|
203
|
+
* Scaffold a draft scope under .cx/scopes/draft-<id>/. Returns the paths
|
|
204
|
+
* to the requirements brief and the draft scope JSON.
|
|
205
205
|
*/
|
|
206
206
|
/**
|
|
207
|
-
* Scaffold a draft
|
|
207
|
+
* Scaffold a draft scope + its persona and department research artifacts.
|
|
208
208
|
*
|
|
209
209
|
* @param {object} args
|
|
210
210
|
* @param {string} args.cwd
|
|
@@ -213,24 +213,24 @@ function departmentTemplate(id, displayName) {
|
|
|
213
213
|
* @param {string[]} [args.seedRoles] - role ids to scaffold persona files for
|
|
214
214
|
* @param {Array<{id:string,displayName:string}>} [args.seedDepartments] - departments to scaffold charters for
|
|
215
215
|
*/
|
|
216
|
-
export function
|
|
217
|
-
if (!cwd || !id) throw new Error('
|
|
216
|
+
export function createDraftScope({ cwd, id, displayName, seedRoles = [], seedDepartments = [] }) {
|
|
217
|
+
if (!cwd || !id) throw new Error('createDraftScope: cwd and id are required');
|
|
218
218
|
if (!/^[a-z][a-z0-9-]{1,30}$/.test(id)) {
|
|
219
|
-
throw new Error('
|
|
219
|
+
throw new Error('createDraftScope: id must match ^[a-z][a-z0-9-]{1,30}$');
|
|
220
220
|
}
|
|
221
|
-
if (
|
|
222
|
-
throw new Error(`
|
|
221
|
+
if (listScopes().includes(id)) {
|
|
222
|
+
throw new Error(`createDraftScope: ${id} already exists in the curated catalog`);
|
|
223
223
|
}
|
|
224
224
|
const dir = draftDir(cwd, id);
|
|
225
225
|
if (fs.existsSync(dir)) {
|
|
226
|
-
throw new Error(`
|
|
226
|
+
throw new Error(`createDraftScope: draft already exists at ${path.relative(cwd, dir)}; archive or delete it before recreating`);
|
|
227
227
|
}
|
|
228
228
|
fs.mkdirSync(dir, { recursive: true });
|
|
229
229
|
fs.mkdirSync(path.join(dir, 'personas'), { recursive: true });
|
|
230
230
|
fs.mkdirSync(path.join(dir, 'departments'), { recursive: true });
|
|
231
231
|
|
|
232
232
|
const briefPath = path.join(dir, 'requirements.md');
|
|
233
|
-
const draftPath = path.join(dir, '
|
|
233
|
+
const draftPath = path.join(dir, 'scope.json');
|
|
234
234
|
fs.writeFileSync(briefPath, REQUIREMENTS_BRIEF(id, displayName || id));
|
|
235
235
|
|
|
236
236
|
const draft = DRAFT_PROFILE_TEMPLATE(id, displayName);
|
|
@@ -261,26 +261,26 @@ export function createDraftProfile({ cwd, id, displayName, seedRoles = [], seedD
|
|
|
261
261
|
departmentPaths.push(p);
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
-
|
|
264
|
+
emitScopeUpdated({ id, stage: 'draft', dir });
|
|
265
265
|
return { dir, briefPath, draftPath, personaPaths, departmentPaths };
|
|
266
266
|
}
|
|
267
267
|
|
|
268
|
-
// Lifecycle bridge: emits
|
|
268
|
+
// Lifecycle bridge: emits scope.updated whenever the curated catalog gains
|
|
269
269
|
// or loses an entry, so subscribers (none by default; project overlays can
|
|
270
270
|
// bind) can react to taxonomy changes.
|
|
271
271
|
|
|
272
|
-
function
|
|
273
|
-
emitRoleEvent('
|
|
274
|
-
summary: `
|
|
272
|
+
function emitScopeUpdated(context) {
|
|
273
|
+
emitRoleEvent('scope.updated', {
|
|
274
|
+
summary: `scope ${context.stage}: ${context.id}`,
|
|
275
275
|
context,
|
|
276
276
|
});
|
|
277
277
|
}
|
|
278
278
|
|
|
279
279
|
/**
|
|
280
|
-
* List drafts under .cx/
|
|
280
|
+
* List drafts under .cx/scopes/. Returns [{ id, dir, hasScope, hasBrief }].
|
|
281
281
|
*/
|
|
282
282
|
export function listDrafts(cwd) {
|
|
283
|
-
const root = path.join(cwd, '.cx', '
|
|
283
|
+
const root = path.join(cwd, '.cx', 'scopes');
|
|
284
284
|
if (!fs.existsSync(root)) return [];
|
|
285
285
|
const out = [];
|
|
286
286
|
for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
|
|
@@ -291,7 +291,7 @@ export function listDrafts(cwd) {
|
|
|
291
291
|
out.push({
|
|
292
292
|
id,
|
|
293
293
|
dir,
|
|
294
|
-
|
|
294
|
+
hasScope: fs.existsSync(path.join(dir, 'scope.json')),
|
|
295
295
|
hasBrief: fs.existsSync(path.join(dir, 'requirements.md')),
|
|
296
296
|
});
|
|
297
297
|
}
|
|
@@ -299,27 +299,27 @@ export function listDrafts(cwd) {
|
|
|
299
299
|
}
|
|
300
300
|
|
|
301
301
|
/**
|
|
302
|
-
* Archive a curated
|
|
303
|
-
* repo) into archive/
|
|
302
|
+
* Archive a curated scope by moving it (and any artifacts it owns in the
|
|
303
|
+
* repo) into archive/scopes/<id>/. Observations and outcomes are not
|
|
304
304
|
* touched; they remain as historical record. Requires a non-empty `reason`.
|
|
305
305
|
*/
|
|
306
|
-
export function
|
|
307
|
-
if (!id) throw new Error('
|
|
306
|
+
export function archiveScope({ id, reason }) {
|
|
307
|
+
if (!id) throw new Error('archiveScope: id is required');
|
|
308
308
|
if (!reason || reason.trim().length < 8) {
|
|
309
|
-
throw new Error('
|
|
309
|
+
throw new Error('archiveScope: a substantive reason (>= 8 chars) is required');
|
|
310
310
|
}
|
|
311
|
-
const
|
|
312
|
-
if (!fs.existsSync(
|
|
313
|
-
throw new Error(`
|
|
311
|
+
const srcScope = path.join(REPO_ROOT, 'specialists', 'org', 'scopes', `${id}.json`);
|
|
312
|
+
if (!fs.existsSync(srcScope)) {
|
|
313
|
+
throw new Error(`archiveScope: ${id} not found in specialists/org/scopes/`);
|
|
314
314
|
}
|
|
315
|
-
const
|
|
316
|
-
const tablePath = typeof
|
|
317
|
-
? path.join(REPO_ROOT,
|
|
315
|
+
const scopeJson = JSON.parse(fs.readFileSync(srcScope, 'utf8'));
|
|
316
|
+
const tablePath = typeof scopeJson?.intake?.classificationTable === 'string'
|
|
317
|
+
? path.join(REPO_ROOT, scopeJson.intake.classificationTable)
|
|
318
318
|
: null;
|
|
319
319
|
|
|
320
320
|
const dstDir = archiveDir(REPO_ROOT, id);
|
|
321
321
|
fs.mkdirSync(dstDir, { recursive: true });
|
|
322
|
-
fs.renameSync(
|
|
322
|
+
fs.renameSync(srcScope, path.join(dstDir, `${id}.json`));
|
|
323
323
|
if (tablePath && fs.existsSync(tablePath)) {
|
|
324
324
|
fs.renameSync(tablePath, path.join(dstDir, path.basename(tablePath)));
|
|
325
325
|
}
|
|
@@ -334,23 +334,23 @@ export function archiveProfile({ id, reason }) {
|
|
|
334
334
|
'',
|
|
335
335
|
'## What stayed',
|
|
336
336
|
'',
|
|
337
|
-
'Observations and outcomes recorded under this
|
|
337
|
+
'Observations and outcomes recorded under this scope remain in `.cx/observations/` and `.cx/outcomes/` and continue to be searchable. The intake table and scope JSON were moved into this directory. To restore: move the files back to their original paths and re-run `npm run lint:scopes`.',
|
|
338
338
|
'',
|
|
339
339
|
].join('\n'));
|
|
340
|
-
|
|
340
|
+
emitScopeUpdated({ id, stage: 'archived', dir: dstDir, reason: reason.trim() });
|
|
341
341
|
return { archived: dstDir };
|
|
342
342
|
}
|
|
343
343
|
|
|
344
344
|
/**
|
|
345
|
-
* Per-
|
|
346
|
-
* the
|
|
345
|
+
* Per-scope health rollup. Counts observations and outcomes tagged with
|
|
346
|
+
* the scope id, plus the median classification confidence over a window.
|
|
347
347
|
*
|
|
348
348
|
* @param {string} cwd - project root
|
|
349
|
-
* @param {string}
|
|
349
|
+
* @param {string} scopeId
|
|
350
350
|
* @param {object} [opts]
|
|
351
351
|
* @param {number} [opts.windowDays=30]
|
|
352
352
|
*/
|
|
353
|
-
export function
|
|
353
|
+
export function scopeHealth(cwd, scopeId, { windowDays = 30 } = {}) {
|
|
354
354
|
const cutoff = Date.now() - windowDays * 24 * 60 * 60 * 1000;
|
|
355
355
|
const obsDir = path.join(cwd, '.cx', 'observations');
|
|
356
356
|
let observationCount = 0;
|
|
@@ -361,10 +361,10 @@ export function profileHealth(cwd, profileId, { windowDays = 30 } = {}) {
|
|
|
361
361
|
const idx = JSON.parse(fs.readFileSync(indexPath, 'utf8'));
|
|
362
362
|
for (const entry of idx) {
|
|
363
363
|
if (Date.parse(entry.createdAt) < cutoff) continue;
|
|
364
|
-
// Observations stamp project, not
|
|
365
|
-
//
|
|
364
|
+
// Observations stamp project, not scope. Fall back to project ==
|
|
365
|
+
// scope id for the common case where the operator named them the
|
|
366
366
|
// same; otherwise count under "all" so we never silently report 0.
|
|
367
|
-
observationCount += entry.project ===
|
|
367
|
+
observationCount += entry.project === scopeId ? 1 : 0;
|
|
368
368
|
}
|
|
369
369
|
} catch { /* unreadable index — health stays 0 */ }
|
|
370
370
|
}
|
|
@@ -380,7 +380,7 @@ export function profileHealth(cwd, profileId, { windowDays = 30 } = {}) {
|
|
|
380
380
|
for (const line of lines) {
|
|
381
381
|
let entry;
|
|
382
382
|
try { entry = JSON.parse(line); } catch { continue; }
|
|
383
|
-
if (entry.profile !==
|
|
383
|
+
if (entry.profile !== scopeId) continue;
|
|
384
384
|
if (Date.parse(entry.ts) < cutoff) continue;
|
|
385
385
|
const r = roleHealth[role] || (roleHealth[role] = { count: 0, success: 0 });
|
|
386
386
|
r.count += 1;
|
|
@@ -396,8 +396,8 @@ export function profileHealth(cwd, profileId, { windowDays = 30 } = {}) {
|
|
|
396
396
|
);
|
|
397
397
|
|
|
398
398
|
return {
|
|
399
|
-
|
|
400
|
-
|
|
399
|
+
scope: scopeId,
|
|
400
|
+
scopeExists: !!loadScope(scopeId),
|
|
401
401
|
windowDays,
|
|
402
402
|
observationCount,
|
|
403
403
|
roles: roleSummary,
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/scopes/loader.mjs — Work-scope loader (intake lens + org enrichment).
|
|
3
|
+
*
|
|
4
|
+
* Curated scopes live in specialists/org/scopes/<id>.json. Custom overrides
|
|
5
|
+
* use <cwd>/.cx/scope.json with custom: true. construct.config.json scope
|
|
6
|
+
* field selects the active scope id (default rnd).
|
|
7
|
+
*
|
|
8
|
+
* Teams and roles are not authored in scope files — enrich.mjs attaches them
|
|
9
|
+
* from specialists/org at load time.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
13
|
+
import { dirname, join, resolve } from 'node:path';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
15
|
+
|
|
16
|
+
import { enrichScope } from './enrich.mjs';
|
|
17
|
+
|
|
18
|
+
const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
const REPO_ROOT = resolve(MODULE_DIR, '..', '..');
|
|
20
|
+
const SCOPES_DIR = join(REPO_ROOT, 'specialists', 'org', 'scopes');
|
|
21
|
+
|
|
22
|
+
export const DEFAULT_SCOPE_ID = 'rnd';
|
|
23
|
+
|
|
24
|
+
export function loadScope(id, opts = {}) {
|
|
25
|
+
if (!id || typeof id !== 'string') return null;
|
|
26
|
+
const path = join(SCOPES_DIR, `${id}.json`);
|
|
27
|
+
if (!existsSync(path)) return null;
|
|
28
|
+
try {
|
|
29
|
+
const raw = JSON.parse(readFileSync(path, 'utf8'));
|
|
30
|
+
return enrichScope(raw, opts);
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function listScopes() {
|
|
37
|
+
if (!existsSync(SCOPES_DIR)) return [];
|
|
38
|
+
return readdirSync(SCOPES_DIR)
|
|
39
|
+
.filter((f) => f.endsWith('.json'))
|
|
40
|
+
.map((f) => f.replace(/\.json$/, ''))
|
|
41
|
+
.filter((id) => {
|
|
42
|
+
try {
|
|
43
|
+
const raw = JSON.parse(readFileSync(join(SCOPES_DIR, `${id}.json`), 'utf8'));
|
|
44
|
+
return raw?.id === id && raw?.intake?.types && raw?.intake?.stages;
|
|
45
|
+
} catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
.sort();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function loadCustomScope(cwd) {
|
|
53
|
+
if (!cwd) return null;
|
|
54
|
+
const path = join(cwd, '.cx', 'scope.json');
|
|
55
|
+
if (!existsSync(path)) return null;
|
|
56
|
+
try {
|
|
57
|
+
const raw = JSON.parse(readFileSync(path, 'utf8'));
|
|
58
|
+
if (raw && raw.custom === true) return raw;
|
|
59
|
+
return null;
|
|
60
|
+
} catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function resolveActiveScope(cwd, configScopeId = null) {
|
|
66
|
+
const rootDir = REPO_ROOT;
|
|
67
|
+
const enrichOpts = { rootDir };
|
|
68
|
+
if (configScopeId) {
|
|
69
|
+
const s = loadScope(configScopeId, enrichOpts);
|
|
70
|
+
if (s) return s;
|
|
71
|
+
}
|
|
72
|
+
const custom = loadCustomScope(cwd);
|
|
73
|
+
if (custom) return enrichScope(custom, enrichOpts);
|
|
74
|
+
const fromConfig = readScopeFromProjectConfig(cwd);
|
|
75
|
+
if (fromConfig) {
|
|
76
|
+
const s = loadScope(fromConfig, enrichOpts);
|
|
77
|
+
if (s) return s;
|
|
78
|
+
}
|
|
79
|
+
return loadScope(DEFAULT_SCOPE_ID, enrichOpts) ?? minimalRndFallback();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function readScopeFromProjectConfig(cwd) {
|
|
83
|
+
if (!cwd) return null;
|
|
84
|
+
const p = join(cwd, 'construct.config.json');
|
|
85
|
+
if (!existsSync(p)) return null;
|
|
86
|
+
try {
|
|
87
|
+
const raw = JSON.parse(readFileSync(p, 'utf8'));
|
|
88
|
+
return typeof raw?.scope === 'string' ? raw.scope : null;
|
|
89
|
+
} catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function minimalRndFallback() {
|
|
95
|
+
return {
|
|
96
|
+
id: 'rnd',
|
|
97
|
+
displayName: 'Software R&D',
|
|
98
|
+
roles: [],
|
|
99
|
+
intake: { types: [], stages: [] },
|
|
100
|
+
docTemplates: [],
|
|
101
|
+
hooks: { sessionReflect: 'on', sessionOptimize: 'on' },
|
|
102
|
+
rebrand: { intakeQueueLabel: 'R&D intake queue', signalNoun: 'signal' },
|
|
103
|
+
};
|
|
104
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/scopes/rebrand.mjs — Scope-aware language helper.
|
|
3
|
+
*
|
|
4
|
+
* Each curated scope carries a `rebrand` block: how the active scope
|
|
5
|
+
* wants the intake queue and individual signal items named in user-facing
|
|
6
|
+
* surfaces (CLI output, daemon logs, session-start prelude).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { resolveActiveScope } from './loader.mjs';
|
|
10
|
+
|
|
11
|
+
export const DEFAULT_REBRAND = Object.freeze({
|
|
12
|
+
intakeQueueLabel: 'Intake queue',
|
|
13
|
+
signalNoun: 'signal',
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export function getRebrand(rootDir) {
|
|
17
|
+
if (!rootDir || typeof rootDir !== 'string') return { ...DEFAULT_REBRAND };
|
|
18
|
+
try {
|
|
19
|
+
const scope = resolveActiveScope(rootDir);
|
|
20
|
+
const rb = scope?.rebrand;
|
|
21
|
+
if (!rb || typeof rb !== 'object') return { ...DEFAULT_REBRAND };
|
|
22
|
+
const intakeQueueLabel = typeof rb.intakeQueueLabel === 'string' && rb.intakeQueueLabel.trim()
|
|
23
|
+
? rb.intakeQueueLabel.trim()
|
|
24
|
+
: DEFAULT_REBRAND.intakeQueueLabel;
|
|
25
|
+
const signalNoun = typeof rb.signalNoun === 'string' && rb.signalNoun.trim()
|
|
26
|
+
? rb.signalNoun.trim()
|
|
27
|
+
: DEFAULT_REBRAND.signalNoun;
|
|
28
|
+
return { intakeQueueLabel, signalNoun };
|
|
29
|
+
} catch {
|
|
30
|
+
return { ...DEFAULT_REBRAND };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/scopes/research-profile.mjs — Map intake types to research depth profiles.
|
|
3
|
+
*
|
|
4
|
+
* Scope JSON may declare researchProfiles (user, codebase, external, market).
|
|
5
|
+
* Artifact manifest entries carry a default researchProfile; scope overrides win.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export function resolveResearchProfile({ intakeType, artifactType, scope } = {}) {
|
|
9
|
+
if (scope?.researchProfiles && intakeType && scope.researchProfiles[intakeType]) {
|
|
10
|
+
return scope.researchProfiles[intakeType];
|
|
11
|
+
}
|
|
12
|
+
if (scope?.researchProfiles && artifactType && scope.researchProfiles[artifactType]) {
|
|
13
|
+
return scope.researchProfiles[artifactType];
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/scopes/teams.mjs — Scope-aware routing hints over the single org chart.
|
|
3
|
+
*
|
|
4
|
+
* Scopes change intake taxonomy and terminology, not org membership. Operations-
|
|
5
|
+
* scope projects map objectives to existing rnd squads (operations-team,
|
|
6
|
+
* engineering-team, product-management-team).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const OPERATIONS_OBJECTIVE_RULES = [
|
|
10
|
+
{
|
|
11
|
+
teamId: 'operations-team',
|
|
12
|
+
focus: 'reliability',
|
|
13
|
+
attachTo: ['cx-sre', 'cx-docs-keeper'],
|
|
14
|
+
pattern: /\b(incident|outage|deploy|deployment|rollback|reliability|runbook|postmortem|sre|on-?call)\b/i,
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
teamId: 'product-management-team',
|
|
18
|
+
focus: 'triage',
|
|
19
|
+
attachTo: ['cx-operations', 'cx-product-manager'],
|
|
20
|
+
pattern: /\b(triage|route|routing|priority|queue|intake|request|escalat)/i,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
teamId: 'engineering-team',
|
|
24
|
+
focus: 'implementation',
|
|
25
|
+
attachTo: ['cx-engineer', 'cx-qa'],
|
|
26
|
+
pattern: /\b(implement|build|fix|code|test|review|deliver|patch)\b/i,
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
export const SCOPE_INTENT_TO_TEAM = Object.freeze({
|
|
31
|
+
operations: Object.freeze({
|
|
32
|
+
research: 'product-management-team',
|
|
33
|
+
implementation: 'engineering-team',
|
|
34
|
+
investigation: 'operations-team',
|
|
35
|
+
evaluation: 'engineering-team',
|
|
36
|
+
fix: 'engineering-team',
|
|
37
|
+
}),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
export function scopeTeamsById(scope) {
|
|
41
|
+
if (!Array.isArray(scope?.teams) || scope.teams.length === 0) return null;
|
|
42
|
+
const byId = {};
|
|
43
|
+
for (const team of scope.teams) {
|
|
44
|
+
if (team?.id) byId[team.id] = team;
|
|
45
|
+
}
|
|
46
|
+
return byId;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function resolveIntentTeamForScope(intent, scope) {
|
|
50
|
+
if (!intent || !scope?.id) return null;
|
|
51
|
+
const map = SCOPE_INTENT_TO_TEAM[scope.id];
|
|
52
|
+
return map?.[intent] ?? null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function classifyObjectiveForScope(objective = '', scope) {
|
|
56
|
+
const teams = scopeTeamsById(scope);
|
|
57
|
+
if (!teams) return null;
|
|
58
|
+
|
|
59
|
+
if (scope.id === 'operations') {
|
|
60
|
+
const text = String(objective);
|
|
61
|
+
for (const rule of OPERATIONS_OBJECTIVE_RULES) {
|
|
62
|
+
if (rule.pattern.test(text)) {
|
|
63
|
+
return {
|
|
64
|
+
focus: rule.focus,
|
|
65
|
+
attachTo: rule.attachTo,
|
|
66
|
+
teamFocus: rule.teamId,
|
|
67
|
+
recommendedTeam: rule.teamId,
|
|
68
|
+
scopeTeamSource: scope.id,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
focus: 'triage',
|
|
74
|
+
attachTo: ['cx-operations', 'cx-product-manager'],
|
|
75
|
+
teamFocus: 'product-management-team',
|
|
76
|
+
recommendedTeam: 'product-management-team',
|
|
77
|
+
scopeTeamSource: scope.id,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const firstTeam = scope.teams[0];
|
|
82
|
+
if (!firstTeam?.id) return null;
|
|
83
|
+
const attachTo = Array.isArray(firstTeam.specialists) && firstTeam.specialists.length > 0
|
|
84
|
+
? firstTeam.specialists
|
|
85
|
+
: (firstTeam.roles ?? []).map((role) => (role.startsWith('cx-') ? role : `cx-${role}`));
|
|
86
|
+
return {
|
|
87
|
+
focus: firstTeam.id,
|
|
88
|
+
attachTo,
|
|
89
|
+
teamFocus: firstTeam.id,
|
|
90
|
+
recommendedTeam: firstTeam.id,
|
|
91
|
+
scopeTeamSource: scope.id,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function resolveScopeTeamMeta(teamId, scope) {
|
|
96
|
+
const teams = scopeTeamsById(scope);
|
|
97
|
+
return teams?.[teamId] ?? null;
|
|
98
|
+
}
|