@geraldmaron/construct 1.4.2 → 1.5.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/.env.example +36 -0
- package/README.md +41 -7
- package/bin/construct +1027 -64
- package/examples/distribution/sources/deck-one-pager.md +1 -1
- package/lib/acp/server.mjs +21 -7
- package/lib/adapters-sync.mjs +2 -1
- package/lib/audit-trail.mjs +185 -2
- package/lib/beads/auto-close.mjs +2 -1
- package/lib/beads/drift.mjs +2 -1
- package/lib/bridges/copilot-proxy.mjs +20 -5
- package/lib/certification/skill-inventory.mjs +10 -1
- package/lib/cli/approvals.mjs +147 -0
- package/lib/cli-commands.mjs +105 -14
- package/lib/comment-lint.mjs +127 -8
- package/lib/config/schema.mjs +6 -29
- package/lib/config/source-target-registry.mjs +70 -0
- package/lib/config/source-targets.mjs +179 -205
- package/lib/contracts/coverage.mjs +77 -0
- package/lib/contracts/validate.mjs +102 -13
- package/lib/contracts/violation-log.mjs +50 -4
- package/lib/db/migrate.mjs +69 -0
- package/lib/db/migrations/001_orchestration_runs.sql +9 -0
- package/lib/db/migrations/002_queue_provider.sql +50 -0
- package/lib/db/migrations/003_worker_registry.sql +17 -0
- package/lib/db/migrations/004_trace_events.sql +16 -0
- package/lib/db/migrations/005_shared_memory.sql +15 -0
- package/lib/db/migrations/006_orchestration_runs_tenant.sql +5 -0
- package/lib/decisions/enforced-baseline.json +0 -2
- package/lib/decisions/registry.mjs +3 -2
- package/lib/deployment/parity-contract.mjs +1 -1
- package/lib/deployment-mode.mjs +78 -2
- package/lib/diagram-export.mjs +10 -1
- package/lib/distill.mjs +5 -2
- package/lib/docs-verify.mjs +2 -1
- package/lib/doctor/cli.mjs +1 -0
- package/lib/doctor/command-on-path.mjs +24 -0
- package/lib/doctor/diagnosis.mjs +89 -0
- package/lib/doctor/embedding-health.mjs +50 -0
- package/lib/doctor/engine-health.mjs +93 -0
- package/lib/doctor/graph-validate.mjs +47 -0
- package/lib/doctor/index.mjs +18 -4
- package/lib/doctor/sidecar-providers.mjs +56 -0
- package/lib/doctor/watchers/consistency.mjs +93 -1
- package/lib/doctor/watchers/cx-budget.mjs +1 -1
- package/lib/doctor/watchers/graph-staleness.mjs +1 -1
- package/lib/doctor/watchers/mcp-protocol.mjs +17 -0
- package/lib/doctor/watchers/oracle-liveness.mjs +92 -0
- package/lib/doctor/watchers/orchestration-runs.mjs +108 -0
- package/lib/doctor/watchers/provider-breaker.mjs +78 -0
- package/lib/document-export.mjs +52 -27
- package/lib/document-extract/docling-client.mjs +9 -5
- package/lib/document-extract/docling-sidecar.py +30 -0
- package/lib/document-extract.mjs +1 -1
- package/lib/document-ingest.mjs +9 -0
- package/lib/embed/approval-queue.mjs +184 -88
- package/lib/embed/authority-guard.mjs +65 -2
- package/lib/embed/capability-jobs.mjs +365 -0
- package/lib/embed/capability-lifecycle.mjs +219 -0
- package/lib/embed/capability-loader.mjs +303 -0
- package/lib/embed/capability-runtime.mjs +58 -0
- package/lib/embed/cli.mjs +185 -8
- package/lib/embed/config.mjs +4 -0
- package/lib/embed/daemon.mjs +125 -6
- package/lib/embed/demand-fetch.mjs +190 -54
- package/lib/embed/inbox.mjs +12 -10
- package/lib/embed/presets/ops-triage.mjs +236 -0
- package/lib/embed/presets/pm-feedback.mjs +341 -0
- package/lib/embed/presets/tpm.mjs +401 -0
- package/lib/embed/providers/jira.mjs +72 -1
- package/lib/embed/providers/registry.mjs +140 -33
- package/lib/embedded-contract/capability.mjs +4 -0
- package/lib/embedded-contract/execution.mjs +1 -1
- package/lib/embedded-contract/model-resolve.mjs +53 -3
- package/lib/embedded-contract/workflow-defs.mjs +48 -73
- package/lib/embedded-contract/workflow-invoke.mjs +144 -4
- package/lib/embedded-contract/workflows/architecture-review.manifest.json +15 -0
- package/lib/embedded-contract/workflows/data-structure.manifest.json +14 -0
- package/lib/embedded-contract/workflows/evidence-ingest.manifest.json +14 -0
- package/lib/embedded-contract/workflows/memo-draft.manifest.json +14 -0
- package/lib/embedded-contract/workflows/operations-triage.manifest.json +18 -0
- package/lib/embedded-contract/workflows/operations.manifest.json +18 -0
- package/lib/embedded-contract/workflows/pm-feedback.manifest.json +18 -0
- package/lib/embedded-contract/workflows/prd-draft.manifest.json +15 -0
- package/lib/embedded-contract/workflows/proposal-review.manifest.json +15 -0
- package/lib/embedded-contract/workflows/research-synthesis.manifest.json +14 -0
- package/lib/embedded-contract/workflows/risk-review.manifest.json +15 -0
- package/lib/embedded-contract/workflows/structure-notes.manifest.json +14 -0
- package/lib/embedded-contract/workflows/transcript-process.manifest.json +14 -0
- package/lib/embedded-contract/workflows/triage.manifest.json +14 -0
- package/lib/env-config.mjs +50 -9
- package/lib/extensions/index.mjs +27 -0
- package/lib/extensions/loader.mjs +120 -0
- package/lib/extensions/manifest-schema.mjs +141 -0
- package/lib/extensions/manifests/anthropic.manifest.json +12 -0
- package/lib/extensions/manifests/atlassian-confluence.manifest.json +12 -0
- package/lib/extensions/manifests/atlassian-jira.manifest.json +53 -0
- package/lib/extensions/manifests/directory.manifest.json +12 -0
- package/lib/extensions/manifests/docling.manifest.json +37 -0
- package/lib/extensions/manifests/echo.manifest.json +8 -0
- package/lib/extensions/manifests/feedback.manifest.json +12 -0
- package/lib/extensions/manifests/github-copilot.manifest.json +12 -0
- package/lib/extensions/manifests/github.manifest.json +52 -0
- package/lib/extensions/manifests/linear.manifest.json +50 -0
- package/lib/extensions/manifests/local.manifest.json +12 -0
- package/lib/extensions/manifests/ollama.manifest.json +12 -0
- package/lib/extensions/manifests/openai.manifest.json +12 -0
- package/lib/extensions/manifests/openrouter-anthropic.manifest.json +12 -0
- package/lib/extensions/manifests/openrouter-deepseek.manifest.json +12 -0
- package/lib/extensions/manifests/openrouter-google.manifest.json +12 -0
- package/lib/extensions/manifests/openrouter-llama.manifest.json +12 -0
- package/lib/extensions/manifests/openrouter-qwen.manifest.json +12 -0
- package/lib/extensions/manifests/openrouter.manifest.json +12 -0
- package/lib/extensions/manifests/postgres.manifest.json +12 -0
- package/lib/extensions/manifests/salesforce.manifest.json +12 -0
- package/lib/extensions/manifests/slack.manifest.json +58 -0
- package/lib/extensions/manifests/whisper.manifest.json +36 -0
- package/lib/extensions/validate.mjs +175 -0
- package/lib/features.mjs +13 -9
- package/lib/flows/checkpoint.mjs +184 -0
- package/lib/flows/constants.mjs +27 -0
- package/lib/flows/define.mjs +146 -0
- package/lib/flows/engine.mjs +249 -0
- package/lib/flows/errors.mjs +19 -0
- package/lib/flows/index.mjs +27 -0
- package/lib/flows/joins.mjs +18 -0
- package/lib/flows/schema.mjs +90 -0
- package/lib/flows/state.mjs +31 -0
- package/lib/frameworks/loader.mjs +99 -0
- package/lib/frameworks/schema.mjs +157 -0
- package/lib/graph/build-from-corpus.mjs +67 -0
- package/lib/graph/build-from-embed.mjs +82 -0
- package/lib/graph/build-from-registry.mjs +164 -3
- package/lib/graph/cli.mjs +456 -12
- package/lib/graph/gap-queries.mjs +156 -0
- package/lib/graph/gaps.mjs +41 -0
- package/lib/graph/impacted.mjs +129 -0
- package/lib/graph/runtime-evidence.mjs +177 -0
- package/lib/graph/security-coverage.mjs +113 -0
- package/lib/graph/staleness.mjs +241 -10
- package/lib/graph/store.mjs +24 -7
- package/lib/graph/validate.mjs +161 -0
- package/lib/headhunt.mjs +9 -8
- package/lib/health-check.mjs +15 -7
- package/lib/hook-health.mjs +1 -1
- package/lib/hooks/agent-tracker.mjs +10 -4
- package/lib/hooks/edit-guard.mjs +3 -3
- package/lib/hooks/graph-impact-advisory.mjs +2 -2
- package/lib/hooks/guard-bash.mjs +41 -15
- package/lib/hooks/mcp-health-check.mjs +11 -4
- package/lib/hooks/model-fallback.mjs +50 -6
- package/lib/hooks/orchestration-dispatch-guard.mjs +14 -3
- package/lib/hooks/pre-compact.mjs +181 -173
- package/lib/hooks/rule-verifier.mjs +2 -1
- package/lib/hooks/session-reflect.mjs +2 -1
- package/lib/hooks/session-start.mjs +3 -3
- package/lib/host-capabilities.mjs +13 -2
- package/lib/host-disposition.mjs +19 -7
- package/lib/identity.mjs +92 -0
- package/lib/ingest/degraded-extract.mjs +96 -0
- package/lib/ingest/docling-remote.mjs +2 -1
- package/lib/ingest/provider-extract.mjs +7 -19
- package/lib/ingest/sidecar-providers.mjs +196 -0
- package/lib/ingest-tooling.mjs +11 -5
- package/lib/init-unified.mjs +55 -38
- package/lib/init-update.mjs +2 -1
- package/lib/install/legacy-global-cleanup.mjs +25 -0
- package/lib/intake/daemon.mjs +99 -8
- package/lib/intake/git-queue.mjs +110 -38
- package/lib/intake/queue-registry.mjs +50 -0
- package/lib/intake/queue.mjs +133 -17
- package/lib/intake/session-prelude.mjs +57 -8
- package/lib/integrations/intake-integrations.mjs +61 -111
- package/lib/intent-classifier.mjs +43 -5
- package/lib/libreoffice-export.mjs +8 -8
- package/lib/logging/rotate.mjs +5 -4
- package/lib/mcp/broker.mjs +332 -17
- package/lib/mcp/denied-store.mjs +95 -0
- package/lib/mcp/destructive-gate.mjs +30 -0
- package/lib/mcp/dispatch-envelope.mjs +199 -0
- package/lib/mcp/memory-bridge.mjs +2 -1
- package/lib/mcp/server.mjs +154 -1411
- package/lib/mcp/tool-definitions-memory.mjs +376 -0
- package/lib/mcp/tool-definitions-project.mjs +271 -0
- package/lib/mcp/tool-definitions-skills.mjs +311 -0
- package/lib/mcp/tool-definitions-workflow.mjs +398 -0
- package/lib/mcp/tool-definitions.mjs +25 -0
- package/lib/mcp/tool-registry.mjs +107 -0
- package/lib/mcp/tool-safety.mjs +1 -0
- package/lib/mcp/tools/orchestration-delegation-next.tool.mjs +68 -0
- package/lib/mcp/tools/orchestration-run.mjs +165 -25
- package/lib/mcp/tools/orchestration-task-result.tool.mjs +77 -0
- package/lib/mcp/tools/provider-write.mjs +187 -0
- package/lib/mcp/tools/scope.mjs +0 -3
- package/lib/mcp/tools/skills.mjs +1 -1
- package/lib/mcp/tools/storage.mjs +0 -8
- package/lib/mcp/transport/auth.mjs +238 -0
- package/lib/mcp/transport/http.mjs +136 -0
- package/lib/mcp/transport/mode.mjs +31 -0
- package/lib/mcp/transport/stdio.mjs +22 -0
- package/lib/mcp-catalog.json +4 -4
- package/lib/mcp-manager.mjs +34 -23
- package/lib/mcp-platform-config.mjs +31 -7
- package/lib/mode-capabilities.mjs +109 -0
- package/lib/model-router.mjs +42 -9
- package/lib/models/catalog.mjs +40 -1
- package/lib/net-guard.mjs +247 -0
- package/lib/observation-store.mjs +6 -2
- package/lib/ollama/provision-context.mjs +2 -1
- package/lib/opencode-config.mjs +22 -3
- package/lib/opencode-runtime-plugin.mjs +120 -2
- package/lib/oracle/actions.mjs +204 -10
- package/lib/oracle/cli.mjs +24 -3
- package/lib/oracle/dispatch.mjs +2 -1
- package/lib/oracle/execute.mjs +2 -2
- package/lib/oracle/gaps.mjs +3 -3
- package/lib/oracle/heartbeat.mjs +53 -0
- package/lib/oracle/read-model.mjs +69 -13
- package/lib/oracle/reconcile.mjs +3 -46
- package/lib/oracle/routing.mjs +37 -31
- package/lib/oracle/synthesize.mjs +1 -1
- package/lib/orchestration/classification.mjs +434 -0
- package/lib/orchestration/delegation-flow.mjs +132 -0
- package/lib/orchestration/flow-selection.mjs +418 -0
- package/lib/orchestration/gates.mjs +244 -0
- package/lib/orchestration/host-sampling.mjs +121 -0
- package/lib/orchestration/policy-constants.mjs +48 -0
- package/lib/orchestration/provider-outcome.mjs +197 -0
- package/lib/orchestration/readiness.mjs +114 -13
- package/lib/orchestration/run-store-postgres.mjs +32 -26
- package/lib/orchestration/run-store-sqlite.mjs +8 -14
- package/lib/orchestration/run-store.mjs +25 -12
- package/lib/orchestration/runtime.mjs +446 -39
- package/lib/orchestration/store.mjs +87 -12
- package/lib/orchestration/trace-store.mjs +125 -0
- package/lib/orchestration/web-capability.mjs +3 -2
- package/lib/orchestration/worker-runtime.mjs +162 -0
- package/lib/orchestration/worker.mjs +528 -89
- package/lib/orchestration-policy.mjs +67 -1111
- package/lib/packs/cli.mjs +144 -0
- package/lib/packs/core-pack.mjs +112 -0
- package/lib/packs/enablement.mjs +187 -0
- package/lib/packs/index.mjs +23 -0
- package/lib/packs/loader.mjs +176 -0
- package/lib/packs/manifest-schema.mjs +58 -0
- package/lib/packs/prompts.mjs +120 -0
- package/lib/packs/validate.mjs +208 -0
- package/lib/plugin-registry.mjs +4 -5
- package/lib/policy/audit-gate.mjs +42 -0
- package/lib/policy/consumption-budget.mjs +149 -0
- package/lib/policy/engine.mjs +81 -6
- package/lib/policy/role-authority.mjs +89 -0
- package/lib/prompt-composer.js +19 -3
- package/lib/providers/contract/adapters/confluence/governed-write.mjs +215 -0
- package/lib/providers/contract/adapters/confluence/manifest.json +17 -0
- package/lib/providers/contract/adapters/confluence/transport.mjs +135 -0
- package/lib/providers/contract/adapters/github/governed-write.mjs +121 -0
- package/lib/providers/contract/adapters/github/index.mjs +38 -3
- package/lib/providers/contract/adapters/jira/adf.mjs +151 -0
- package/lib/providers/contract/adapters/jira/createmeta.mjs +143 -0
- package/lib/providers/contract/adapters/jira/governed-write.mjs +182 -0
- package/lib/providers/contract/adapters/jira/manifest.json +17 -0
- package/lib/providers/contract/adapters/jira/transport.mjs +119 -0
- package/lib/providers/contract.mjs +207 -0
- package/lib/providers/credential-bootstrap.mjs +7 -4
- package/lib/providers/credential-sources.mjs +14 -2
- package/lib/providers/directory/index.mjs +261 -0
- package/lib/providers/feedback/index.mjs +392 -0
- package/lib/providers/filter-audit.mjs +68 -0
- package/lib/providers/filter-schema.mjs +54 -0
- package/lib/providers/github/index.mjs +31 -0
- package/lib/providers/instance-config.mjs +215 -0
- package/lib/providers/op-locate.mjs +96 -0
- package/lib/providers/op-run.mjs +64 -9
- package/lib/providers/registry.mjs +26 -3
- package/lib/providers/secret-audit-wiring.mjs +6 -0
- package/lib/providers/secret-resolver.mjs +240 -23
- package/lib/publish-template.mjs +6 -3
- package/lib/publish-tooling.mjs +4 -0
- package/lib/publish.mjs +32 -4
- package/lib/queue/pg-queue.mjs +395 -0
- package/lib/reflect.mjs +2 -1
- package/lib/registry/assemble.mjs +28 -2
- package/lib/registry/consolidation.mjs +8 -1
- package/lib/registry/custom-scaffold.mjs +200 -0
- package/lib/registry/custom-schema.mjs +137 -0
- package/lib/registry/loader.mjs +8 -3
- package/lib/registry/manifests/format-engines.default.json +15 -0
- package/lib/registry/manifests/surface-map.default.json +46 -0
- package/lib/registry/retired-paths.mjs +1 -0
- package/lib/registry/surface-map.mjs +100 -50
- package/lib/render-visual-check.mjs +240 -0
- package/lib/resources/budget.mjs +40 -17
- package/lib/roles/flavor-bindings.mjs +36 -14
- package/lib/roles/gateway.mjs +15 -8
- package/lib/roots.mjs +107 -0
- package/lib/runtime/uv-bootstrap.mjs +32 -6
- package/lib/runtime/whisper-bootstrap.mjs +11 -3
- package/lib/runtime-env.mjs +5 -2
- package/lib/scheduler/index.mjs +8 -20
- package/lib/schema-infer.mjs +31 -2
- package/lib/scopes/lifecycle.mjs +29 -25
- package/lib/scopes/loader.mjs +49 -12
- package/lib/scopes/teams.mjs +1 -1
- package/lib/security/ingest-boundary.mjs +80 -0
- package/lib/security/recall-wrapper.mjs +99 -0
- package/lib/security/trust.mjs +132 -0
- package/lib/service-manager.mjs +38 -19
- package/lib/setup.mjs +41 -23
- package/lib/skills-apply.mjs +4 -2
- package/lib/specialists/postconditions.mjs +2 -2
- package/lib/state-root.mjs +147 -0
- package/lib/status.mjs +597 -6
- package/lib/storage/admin.mjs +77 -5
- package/lib/storage/backend-registry.mjs +73 -0
- package/lib/storage/backend.mjs +63 -9
- package/lib/storage/embeddings-openai.mjs +12 -2
- package/lib/storage/hybrid-query.mjs +11 -3
- package/lib/storage/retrieval-hardening.mjs +384 -0
- package/lib/storage/shared-memory.mjs +158 -0
- package/lib/storage/vector-client.mjs +69 -2
- package/lib/team/health.mjs +72 -0
- package/lib/telemetry/backends/local.mjs +9 -3
- package/lib/telemetry/client.mjs +3 -7
- package/lib/template-registry.mjs +10 -12
- package/lib/tenant/context.mjs +82 -0
- package/lib/tenant/isolation.mjs +129 -0
- package/lib/test-corpus-inventory.mjs +103 -2
- package/lib/uninstall/uninstall.mjs +78 -12
- package/lib/validator.mjs +4 -6
- package/lib/worker/entrypoint.mjs +2 -1
- package/lib/worker/run.mjs +2 -2
- package/lib/worker/trace.mjs +5 -11
- package/lib/workflow-state.mjs +52 -8
- package/lib/workflows/liveness.mjs +203 -0
- package/lib/workflows/loader.mjs +177 -0
- package/lib/workflows/manifest-schema.mjs +71 -0
- package/lib/workflows/surface-parity.mjs +118 -0
- package/lib/workflows/validate.mjs +127 -0
- package/lib/writes/control-plane.mjs +140 -0
- package/lib/writes/envelope.mjs +206 -0
- package/lib/writes/sent-log.mjs +86 -0
- package/lib/writes/write-intent.mjs +109 -0
- package/package.json +16 -8
- package/personas/construct.md +12 -3
- package/registry/capabilities.json +143 -36
- package/rules/common/comments.md +1 -0
- package/schemas/project-config.schema.json +0 -12
- package/schemas/unified-registry.schema.json +2 -4
- package/scripts/sync-specialists.mjs +284 -81
- package/skills/docs/adr-workflow.md +1 -1
- package/skills/docs/codebase-research-workflow.md +3 -3
- package/skills/docs/prd-workflow.md +5 -6
- package/skills/docs/runbook-workflow.md +2 -2
- package/skills/docs/user-research-workflow.md +3 -3
- package/skills/operating/fleet-health-routing.md +77 -0
- package/skills/roles/ai-engineer.md +1 -1
- package/skills/roles/architect.md +0 -1
- package/skills/roles/business-strategist.md +1 -1
- package/skills/roles/data-analyst.telemetry.md +1 -1
- package/skills/roles/data-engineer.md +1 -1
- package/skills/roles/data-engineer.pipeline.md +1 -1
- package/skills/roles/data-engineer.vector-retrieval.md +1 -2
- package/skills/roles/data-engineer.warehouse.md +1 -1
- package/skills/roles/designer.accessibility.md +1 -1
- package/skills/roles/designer.md +0 -1
- package/skills/roles/devil-advocate.md +1 -1
- package/skills/roles/docs-keeper.md +1 -1
- package/skills/roles/evaluator.md +1 -1
- package/skills/roles/explorer.md +1 -1
- package/skills/roles/platform-engineer.md +1 -1
- package/skills/roles/qa.ai-eval.md +1 -2
- package/skills/roles/qa.api-contract.md +0 -1
- package/skills/roles/qa.data-pipeline.md +0 -1
- package/skills/roles/qa.md +0 -1
- package/skills/roles/qa.web-ui.md +0 -1
- package/skills/roles/release-manager.md +1 -1
- package/skills/roles/researcher.md +0 -2
- package/skills/roles/reviewer.md +0 -3
- package/skills/roles/security.ai.md +1 -1
- package/skills/roles/security.legal-compliance.md +1 -1
- package/skills/roles/security.md +0 -1
- package/skills/roles/security.privacy.md +0 -1
- package/skills/roles/security.supply-chain.md +1 -1
- package/skills/roles/sre.md +1 -1
- package/skills/roles/test-automation.md +1 -1
- package/skills/roles/trace-reviewer.md +1 -1
- package/skills/roles/ux-researcher.md +1 -1
- package/specialists/artifact-manifest.json +36 -36
- package/specialists/org/contracts/accessibility-to-qa.json +11 -3
- package/specialists/org/contracts/any-to-business-strategist.json +19 -7
- package/specialists/org/contracts/any-to-debugger.json +7 -1
- package/specialists/org/contracts/any-to-designer.json +7 -1
- package/specialists/org/contracts/any-to-docs-keeper.json +18 -4
- package/specialists/org/contracts/any-to-explorer.json +13 -4
- package/specialists/org/contracts/any-to-sre-incident.json +6 -2
- package/specialists/org/contracts/any-to-trace-reviewer.json +16 -4
- package/specialists/org/contracts/architect-to-ai-engineer.json +6 -2
- package/specialists/org/contracts/architect-to-data-engineer.json +17 -5
- package/specialists/org/contracts/architect-to-devil-advocate.json +11 -3
- package/specialists/org/contracts/architect-to-engineer.json +20 -4
- package/specialists/org/contracts/architect-to-evaluator.json +15 -5
- package/specialists/org/contracts/architect-to-legal-compliance.json +15 -5
- package/specialists/org/contracts/architect-to-operations.json +17 -4
- package/specialists/org/contracts/architect-to-platform-engineer.json +20 -4
- package/specialists/org/contracts/construct-to-orchestrator.json +10 -2
- package/specialists/org/contracts/data-analyst-to-product-manager.json +11 -2
- package/specialists/org/contracts/engineer-to-qa.json +20 -4
- package/specialists/org/contracts/engineer-to-reviewer.json +29 -5
- package/specialists/org/contracts/explorer-to-engineer.json +11 -3
- package/specialists/org/contracts/legal-compliance-to-release-manager.json +12 -4
- package/specialists/org/contracts/operations-to-user.json +53 -0
- package/specialists/org/contracts/operations-triage-output.json +53 -0
- package/specialists/org/contracts/pm-requirements-candidates.json +53 -0
- package/specialists/org/contracts/product-manager-to-architect.json +30 -10
- package/specialists/org/contracts/product-manager-to-data-analyst.json +13 -3
- package/specialists/org/contracts/product-manager-to-ux-researcher.json +15 -5
- package/specialists/org/contracts/qa-to-release-manager.json +11 -3
- package/specialists/org/contracts/researcher-to-architect.json +10 -2
- package/specialists/org/contracts/researcher-to-product-manager.json +16 -5
- package/specialists/org/contracts/reviewer-to-security.json +17 -3
- package/specialists/org/contracts/test-automation-to-engineer.json +11 -3
- package/specialists/org/contracts/trace-reviewer-to-sre.json +12 -4
- package/specialists/org/contracts/user-to-construct.json +11 -4
- package/specialists/org/frameworks/cx-architect-constraint-option-failure.md +66 -0
- package/specialists/org/frameworks/cx-engineer-feasibility-blast-radius.md +66 -0
- package/specialists/org/frameworks/cx-ops-dependency-sequencing.md +74 -0
- package/specialists/org/frameworks/cx-pm-value-tradeoff.md +66 -0
- package/specialists/org/frameworks/cx-qa-risk-based-coverage.md +69 -0
- package/specialists/org/groups/engineering-group.json +2 -6
- package/specialists/org/groups/governance-group.json +2 -3
- package/specialists/org/groups/operations-group.json +5 -8
- package/specialists/org/groups/product-group.json +4 -8
- package/specialists/org/groups/quality-group.json +3 -7
- package/specialists/org/groups/strategy-group.json +6 -9
- package/specialists/org/models.json.example +8 -0
- package/specialists/org/scopes/creative.json +6 -1
- package/specialists/org/scopes/operations.json +7 -1
- package/specialists/org/scopes/research.json +6 -1
- package/specialists/org/scopes/rnd.json +7 -1
- package/specialists/org/specialists/cx-architect.json +27 -8
- package/specialists/org/specialists/cx-designer.json +22 -8
- package/specialists/org/specialists/cx-engineer.json +71 -7
- package/specialists/org/specialists/cx-operations.json +89 -15
- package/specialists/org/specialists/cx-orchestrator.json +16 -4
- package/specialists/org/specialists/cx-product-manager.json +28 -9
- package/specialists/org/specialists/cx-qa.json +16 -6
- package/specialists/org/specialists/cx-researcher.json +40 -7
- package/specialists/org/specialists/cx-reviewer.json +61 -11
- package/specialists/org/specialists/cx-security.json +30 -10
- package/specialists/org/teams/design-team.json +3 -4
- package/specialists/org/teams/engineering-team.json +3 -10
- package/specialists/org/teams/governance-team.json +3 -5
- package/specialists/org/teams/operations-team.json +6 -12
- package/specialists/org/teams/product-management-team.json +2 -3
- package/specialists/org/teams/quality-team.json +4 -12
- package/specialists/org/teams/research-team.json +3 -3
- package/specialists/org/teams/strategy-team.json +7 -14
- package/specialists/prompts/cx-architect.md +20 -1
- package/specialists/prompts/cx-data-analyst.md +1 -1
- package/specialists/prompts/cx-designer.md +12 -4
- package/specialists/prompts/cx-engineer.md +15 -1
- package/specialists/prompts/cx-operations.md +14 -2
- package/specialists/prompts/cx-orchestrator.md +9 -5
- package/specialists/prompts/cx-product-manager.md +5 -1
- package/specialists/prompts/cx-qa.md +6 -2
- package/specialists/prompts/cx-researcher.md +25 -30
- package/specialists/prompts/cx-reviewer.md +19 -1
- package/specialists/prompts/cx-security.md +5 -1
- package/templates/distribution/construct-brand.typ +123 -59
- package/templates/distribution/construct-decision.typ +6 -3
- package/templates/distribution/construct-pdf.typ +11 -4
- package/templates/distribution/construct-prd.typ +6 -3
- package/templates/distribution/construct-research.typ +7 -4
- package/lib/providers/contract/adapters/git/index.mjs +0 -115
- package/lib/providers/contract/adapters/slack/index.mjs +0 -175
- package/specialists/org/contracts/business-strategist-to-product-manager.json +0 -39
- package/specialists/org/contracts/construct-to-rd-lead.json +0 -53
- package/specialists/org/contracts/data-engineer-to-platform-engineer.json +0 -36
- package/specialists/org/contracts/designer-to-accessibility.json +0 -36
- package/specialists/org/contracts/platform-engineer-to-engineer.json +0 -31
- package/specialists/org/contracts/qa-to-test-automation.json +0 -42
- package/specialists/org/contracts/rd-lead-to-architect.json +0 -38
- package/specialists/org/contracts/sre-to-release-manager.json +0 -36
- package/specialists/org/specialists/cx-accessibility.json +0 -69
- package/specialists/org/specialists/cx-ai-engineer.json +0 -75
- package/specialists/org/specialists/cx-business-strategist.json +0 -72
- package/specialists/org/specialists/cx-data-engineer.json +0 -71
- package/specialists/org/specialists/cx-devil-advocate.json +0 -71
- package/specialists/org/specialists/cx-docs-keeper.json +0 -82
- package/specialists/org/specialists/cx-evaluator.json +0 -69
- package/specialists/org/specialists/cx-explorer.json +0 -69
- package/specialists/org/specialists/cx-legal-compliance.json +0 -74
- package/specialists/org/specialists/cx-oracle.json +0 -46
- package/specialists/org/specialists/cx-platform-engineer.json +0 -80
- package/specialists/org/specialists/cx-rd-lead.json +0 -73
- package/specialists/org/specialists/cx-release-manager.json +0 -77
- package/specialists/org/specialists/cx-sre.json +0 -94
- package/specialists/org/specialists/cx-test-automation.json +0 -69
- package/specialists/org/specialists/cx-trace-reviewer.json +0 -69
- package/specialists/org/specialists/cx-ux-researcher.json +0 -68
- package/specialists/org/teams/accessibility-team.json +0 -39
- package/specialists/org/teams/ux-research-team.json +0 -39
- package/specialists/prompts/cx-accessibility.md +0 -55
- package/specialists/prompts/cx-ai-engineer.md +0 -124
- package/specialists/prompts/cx-business-strategist.md +0 -58
- package/specialists/prompts/cx-data-engineer.md +0 -55
- package/specialists/prompts/cx-devil-advocate.md +0 -59
- package/specialists/prompts/cx-docs-keeper.md +0 -164
- package/specialists/prompts/cx-evaluator.md +0 -49
- package/specialists/prompts/cx-explorer.md +0 -71
- package/specialists/prompts/cx-legal-compliance.md +0 -58
- package/specialists/prompts/cx-oracle.md +0 -98
- package/specialists/prompts/cx-platform-engineer.md +0 -97
- package/specialists/prompts/cx-rd-lead.md +0 -59
- package/specialists/prompts/cx-release-manager.md +0 -54
- package/specialists/prompts/cx-sre.md +0 -111
- package/specialists/prompts/cx-test-automation.md +0 -55
- package/specialists/prompts/cx-trace-reviewer.md +0 -101
- package/specialists/prompts/cx-ux-researcher.md +0 -53
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/frameworks/schema.mjs — ADR-0062 persona reasoning framework schema validator.
|
|
3
|
+
*
|
|
4
|
+
* A framework is Markdown with YAML frontmatter (same convention as
|
|
5
|
+
* specialists/prompts/*.md). validateFrameworkFrontmatter() checks the
|
|
6
|
+
* frontmatter object in isolation (required fields, step shape, unique
|
|
7
|
+
* `emits` tokens, `cites` enum, known `appliesToRole`) — it never reads
|
|
8
|
+
* files itself, so it is usable directly against a parsed object in tests.
|
|
9
|
+
* parseFrameworkFile() does the frontmatter extraction plus validation for
|
|
10
|
+
* a file on disk, mirroring lib/packs/prompts.mjs's parseFrontmatter shape.
|
|
11
|
+
*
|
|
12
|
+
* Invalid frameworks fail closed: every error names the offending field and
|
|
13
|
+
* (when available) the file path, per ADR-0062 §4 — no silent skip.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { readFileSync } from 'node:fs';
|
|
17
|
+
import yaml from 'js-yaml';
|
|
18
|
+
|
|
19
|
+
const FRONTMATTER_RE = /^---\n([\s\S]*?)\n---\n?/;
|
|
20
|
+
|
|
21
|
+
const CITES_VALUES = new Set(['source', 'prior-step']);
|
|
22
|
+
|
|
23
|
+
const STEP_REQUIRED_FIELDS = ['id', 'move', 'question', 'emits', 'cites'];
|
|
24
|
+
|
|
25
|
+
const MIN_STEPS = 3;
|
|
26
|
+
const MAX_STEPS = 6;
|
|
27
|
+
|
|
28
|
+
export function knownRolesFromSpecialists(specialists) {
|
|
29
|
+
const roles = new Set();
|
|
30
|
+
for (const spec of specialists || []) {
|
|
31
|
+
if (spec && typeof spec.role === 'string' && spec.role) roles.add(spec.role);
|
|
32
|
+
}
|
|
33
|
+
return roles;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Validate a parsed frontmatter object against the ADR-0062 schema.
|
|
38
|
+
*
|
|
39
|
+
* @param {object} fm parsed YAML frontmatter
|
|
40
|
+
* @param {object} opts
|
|
41
|
+
* @param {string} [opts.filePath] for error-message prefixing
|
|
42
|
+
* @param {Set<string>|string[]} [opts.knownRoles] roles appliesToRole may name; skipped when omitted
|
|
43
|
+
* @returns {{valid: boolean, errors: string[]}}
|
|
44
|
+
*/
|
|
45
|
+
export function validateFrameworkFrontmatter(fm, { filePath, knownRoles } = {}) {
|
|
46
|
+
const prefix = filePath ? `${filePath}: ` : '';
|
|
47
|
+
const errors = [];
|
|
48
|
+
|
|
49
|
+
if (!fm || typeof fm !== 'object' || Array.isArray(fm)) {
|
|
50
|
+
return { valid: false, errors: [`${prefix}frontmatter must be a YAML object`] };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!fm.id || typeof fm.id !== 'string') {
|
|
54
|
+
errors.push(`${prefix}missing required field: id`);
|
|
55
|
+
}
|
|
56
|
+
if (fm.version === undefined || fm.version === null) {
|
|
57
|
+
errors.push(`${prefix}missing required field: version`);
|
|
58
|
+
}
|
|
59
|
+
if (!fm.appliesToRole || typeof fm.appliesToRole !== 'string') {
|
|
60
|
+
errors.push(`${prefix}missing required field: appliesToRole`);
|
|
61
|
+
} else if (knownRoles) {
|
|
62
|
+
const roles = knownRoles instanceof Set ? knownRoles : new Set(knownRoles);
|
|
63
|
+
if (roles.size > 0 && !roles.has(fm.appliesToRole)) {
|
|
64
|
+
errors.push(`${prefix}appliesToRole '${fm.appliesToRole}' is not a known role`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (!fm.summary || typeof fm.summary !== 'string') {
|
|
68
|
+
errors.push(`${prefix}missing required field: summary`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!Array.isArray(fm.steps) || fm.steps.length === 0) {
|
|
72
|
+
errors.push(`${prefix}steps must be a non-empty array`);
|
|
73
|
+
return { valid: errors.length === 0, errors };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (fm.steps.length < MIN_STEPS || fm.steps.length > MAX_STEPS) {
|
|
77
|
+
errors.push(`${prefix}steps must contain ${MIN_STEPS}-${MAX_STEPS} entries (got ${fm.steps.length})`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const emitsSeen = new Map();
|
|
81
|
+
const stepIdsSeen = new Set();
|
|
82
|
+
|
|
83
|
+
fm.steps.forEach((step, idx) => {
|
|
84
|
+
const stepPrefix = `${prefix}steps[${idx}]`;
|
|
85
|
+
|
|
86
|
+
if (!step || typeof step !== 'object' || Array.isArray(step)) {
|
|
87
|
+
errors.push(`${stepPrefix}: must be an object`);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
for (const field of STEP_REQUIRED_FIELDS) {
|
|
92
|
+
if (!step[field] || typeof step[field] !== 'string') {
|
|
93
|
+
errors.push(`${stepPrefix}: missing required field: ${field}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (step.id) {
|
|
98
|
+
if (stepIdsSeen.has(step.id)) {
|
|
99
|
+
errors.push(`${stepPrefix}: duplicate step id '${step.id}'`);
|
|
100
|
+
}
|
|
101
|
+
stepIdsSeen.add(step.id);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (step.emits) {
|
|
105
|
+
if (emitsSeen.has(step.emits)) {
|
|
106
|
+
errors.push(
|
|
107
|
+
`${prefix}emits token '${step.emits}' is not unique within the framework (also used by step '${emitsSeen.get(step.emits)}')`
|
|
108
|
+
);
|
|
109
|
+
} else {
|
|
110
|
+
emitsSeen.set(step.emits, step.id || `steps[${idx}]`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (step.cites && !CITES_VALUES.has(step.cites)) {
|
|
115
|
+
errors.push(`${stepPrefix}: cites must be one of ${[...CITES_VALUES].join(', ')} (got '${step.cites}')`);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
return { valid: errors.length === 0, errors };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function parseFrontmatter(content) {
|
|
123
|
+
const match = content.match(FRONTMATTER_RE);
|
|
124
|
+
if (!match) return null;
|
|
125
|
+
try {
|
|
126
|
+
return yaml.load(match[1]) || {};
|
|
127
|
+
} catch {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Read and validate a framework file from disk.
|
|
134
|
+
*
|
|
135
|
+
* @param {string} filePath
|
|
136
|
+
* @param {object} opts
|
|
137
|
+
* @param {Set<string>|string[]} [opts.knownRoles]
|
|
138
|
+
* @returns {{valid: boolean, errors: string[], frontmatter: object|null, body: string|null}}
|
|
139
|
+
*/
|
|
140
|
+
export function parseFrameworkFile(filePath, { knownRoles } = {}) {
|
|
141
|
+
let raw;
|
|
142
|
+
try {
|
|
143
|
+
raw = readFileSync(filePath, 'utf8');
|
|
144
|
+
} catch (err) {
|
|
145
|
+
return { valid: false, errors: [`${filePath}: failed to read file (${err.message})`], frontmatter: null, body: null };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const fm = parseFrontmatter(raw);
|
|
149
|
+
if (!fm) {
|
|
150
|
+
return { valid: false, errors: [`${filePath}: missing or invalid frontmatter`], frontmatter: null, body: null };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const result = validateFrameworkFrontmatter(fm, { filePath, knownRoles });
|
|
154
|
+
const body = raw.replace(FRONTMATTER_RE, '');
|
|
155
|
+
|
|
156
|
+
return { valid: result.valid, errors: result.errors, frontmatter: fm, body };
|
|
157
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/graph/build-from-corpus.mjs — corpus-annotation edges for the dependency graph.
|
|
3
|
+
*
|
|
4
|
+
* buildFromRegistry only derives test --validates--> capability edges from
|
|
5
|
+
* registry/capabilities.json's verification.functional / hostEmulation pointers,
|
|
6
|
+
* so a capability verified solely by unit/integration tests (e.g. local.model.tier,
|
|
7
|
+
* which declares an explicit untestableRationale for functional coverage) or a
|
|
8
|
+
* capability tracked only in tests/capabilities/ledger.json never gets an edge.
|
|
9
|
+
* Walking @capability annotations across the whole test corpus (via
|
|
10
|
+
* lib/test-corpus-inventory.mjs) and emitting the same validates edge shape,
|
|
11
|
+
* sourced 'corpus-annotation', closes that gap and merges with (rather than
|
|
12
|
+
* replaces) registry-derived edges in the store's dedup-by-key write path.
|
|
13
|
+
*
|
|
14
|
+
* A capability id tagged in a test but absent from both registry/capabilities.json
|
|
15
|
+
* and tests/capabilities/ledger.json is not a fabricated node — the ledger and
|
|
16
|
+
* registry are the id authorities — so annotation-only ids without a known
|
|
17
|
+
* capability node are skipped and reported back to the caller as orphaned tags.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { readFileSync } from 'node:fs';
|
|
21
|
+
import path from 'node:path';
|
|
22
|
+
|
|
23
|
+
import { extractCapabilityTestEdges } from '../test-corpus-inventory.mjs';
|
|
24
|
+
import { loadCapabilityRegistry } from '../registry/validate.mjs';
|
|
25
|
+
import { defaultLedgerPath } from '../capability-ledger.mjs';
|
|
26
|
+
import { nodeId } from './store.mjs';
|
|
27
|
+
|
|
28
|
+
function readJsonSafe(filePath) {
|
|
29
|
+
try { return JSON.parse(readFileSync(filePath, 'utf8')); } catch { return null; }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @param {object} opts
|
|
34
|
+
* @param {string} opts.rootDir
|
|
35
|
+
* @returns {{ nodes: object[], edges: object[], orphanedCapabilityIds: string[] }}
|
|
36
|
+
*/
|
|
37
|
+
export function buildFromCorpus({ rootDir }) {
|
|
38
|
+
const registry = loadCapabilityRegistry({ rootDir });
|
|
39
|
+
const registryIds = new Set((registry.capabilities ?? []).map((cap) => cap.id).filter(Boolean));
|
|
40
|
+
|
|
41
|
+
const ledger = readJsonSafe(defaultLedgerPath(rootDir));
|
|
42
|
+
const ledgerIds = new Set((ledger?.capabilities ?? []).map((cap) => cap.id).filter(Boolean));
|
|
43
|
+
|
|
44
|
+
const nodes = [];
|
|
45
|
+
const edges = [];
|
|
46
|
+
const orphaned = new Set();
|
|
47
|
+
const ledgerNodesEmitted = new Set();
|
|
48
|
+
|
|
49
|
+
for (const { testPath, capabilityId } of extractCapabilityTestEdges({ rootDir })) {
|
|
50
|
+
const known = registryIds.has(capabilityId) || ledgerIds.has(capabilityId);
|
|
51
|
+
if (!known) { orphaned.add(capabilityId); continue; }
|
|
52
|
+
|
|
53
|
+
// Ledger-only capabilities (not in registry/capabilities.json) have no
|
|
54
|
+
// node anywhere else in the graph, so this is the sole place they're created.
|
|
55
|
+
|
|
56
|
+
if (!registryIds.has(capabilityId) && !ledgerNodesEmitted.has(capabilityId)) {
|
|
57
|
+
ledgerNodesEmitted.add(capabilityId);
|
|
58
|
+
nodes.push({ id: nodeId('capability', capabilityId), type: 'capability', name: capabilityId, attrs: { source: 'ledger' } });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const testId = nodeId('test', testPath);
|
|
62
|
+
nodes.push({ id: testId, type: 'test', name: testPath, attrs: { path: testPath, exists: true } });
|
|
63
|
+
edges.push({ from: testId, to: nodeId('capability', capabilityId), rel: 'validates', source: 'corpus-annotation' });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return { nodes, edges, orphanedCapabilityIds: [...orphaned].sort() };
|
|
67
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/graph/build-from-embed.mjs — seed the living graph with embed-capability
|
|
3
|
+
* nodes and edges from loaded embed manifests (LMCP-P6).
|
|
4
|
+
*
|
|
5
|
+
* Embedded specialists (ADR-0061) were invisible to the graph: build-from-
|
|
6
|
+
* registry seeds provider/specialist/contract nodes but knows nothing of the
|
|
7
|
+
* `type:"embed"` manifests, so C8's drift gate could not protect a preset whose
|
|
8
|
+
* test was deleted or whose binding target broke. This seeder closes that gap.
|
|
9
|
+
*
|
|
10
|
+
* Per embed manifest it emits one `embed:<id>` node plus edges into nodes the
|
|
11
|
+
* registry seeder already produces, reusing the existing relation vocabulary
|
|
12
|
+
* rather than inventing one:
|
|
13
|
+
* embed --uses--> provider (each embed.providerBindings entry)
|
|
14
|
+
* embed --owned_by--> specialist (embed.specialist — who runs it)
|
|
15
|
+
* embed --governed_by--> contract (embed.outputContract)
|
|
16
|
+
* test --validates--> embed (`@embed <id>` acceptance-test tag, C10 format)
|
|
17
|
+
*
|
|
18
|
+
* The binding-target nodes are NOT created here — an embed that names a
|
|
19
|
+
* provider/specialist/contract with no node of its own is drift, and
|
|
20
|
+
* lib/graph/validate.mjs reports the dangling target as an error (strict) so
|
|
21
|
+
* the C8 gate fails instead of the graph quietly gaining an orphan endpoint.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { nodeId } from './store.mjs';
|
|
25
|
+
import { loadEmbedCapabilities } from '../embed/capability-loader.mjs';
|
|
26
|
+
import { extractEmbedTestEdges } from '../test-corpus-inventory.mjs';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {{ rootDir: string }} opts
|
|
30
|
+
* @returns {{ nodes: object[], edges: object[], errors: string[] }}
|
|
31
|
+
*/
|
|
32
|
+
export function buildFromEmbed({ rootDir }) {
|
|
33
|
+
const { capabilities, errors } = loadEmbedCapabilities({ rootDir });
|
|
34
|
+
|
|
35
|
+
const nodes = [];
|
|
36
|
+
const edges = [];
|
|
37
|
+
const embedIds = new Set();
|
|
38
|
+
|
|
39
|
+
for (const manifest of capabilities) {
|
|
40
|
+
const embed = manifest.embed ?? {};
|
|
41
|
+
const id = manifest.id;
|
|
42
|
+
if (!id) continue;
|
|
43
|
+
embedIds.add(id);
|
|
44
|
+
|
|
45
|
+
const from = nodeId('embed', id);
|
|
46
|
+
nodes.push({
|
|
47
|
+
id: from,
|
|
48
|
+
type: 'embed',
|
|
49
|
+
name: id,
|
|
50
|
+
attrs: {
|
|
51
|
+
specialist: embed.specialist ?? null,
|
|
52
|
+
outputContract: embed.outputContract ?? null,
|
|
53
|
+
providerBindings: embed.providerBindings ?? [],
|
|
54
|
+
runtime: embed.runtime ?? null,
|
|
55
|
+
proposalAuthority: embed.proposalAuthority ?? null,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
for (const providerId of embed.providerBindings ?? []) {
|
|
60
|
+
edges.push({ from, to: nodeId('provider', providerId), rel: 'uses', source: 'embed-manifest' });
|
|
61
|
+
}
|
|
62
|
+
if (embed.specialist) {
|
|
63
|
+
edges.push({ from, to: nodeId('specialist', embed.specialist), rel: 'owned_by', source: 'embed-manifest' });
|
|
64
|
+
}
|
|
65
|
+
if (embed.outputContract) {
|
|
66
|
+
edges.push({ from, to: nodeId('contract', embed.outputContract), rel: 'governed_by', source: 'embed-manifest' });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// A `@embed <id>` tag on an id with no loaded manifest is a stale annotation,
|
|
71
|
+
// not a fabricated node — skip it rather than mint a phantom embed node. The
|
|
72
|
+
// tagging test may carry no `@capability`, so its node is minted here (the
|
|
73
|
+
// store dedups by id if build-from-corpus also emitted it).
|
|
74
|
+
for (const { testPath, embedId } of extractEmbedTestEdges({ rootDir })) {
|
|
75
|
+
if (!embedIds.has(embedId)) continue;
|
|
76
|
+
const testId = nodeId('test', testPath);
|
|
77
|
+
nodes.push({ id: testId, type: 'test', name: testPath, attrs: { path: testPath, exists: true } });
|
|
78
|
+
edges.push({ from: testId, to: nodeId('embed', embedId), rel: 'validates', source: 'embed-manifest' });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return { nodes, edges, errors };
|
|
82
|
+
}
|
|
@@ -3,19 +3,30 @@
|
|
|
3
3
|
* authoritative declarative catalogs.
|
|
4
4
|
*
|
|
5
5
|
* Ingests registry/capabilities.json, lib/embedded-contract/workflow-defs.mjs,
|
|
6
|
-
*
|
|
6
|
+
* specialists/org into typed nodes and directed edges:
|
|
7
7
|
* capability --embeds--> workflow (embeddedWorkflow)
|
|
8
8
|
* test --validates--> capability (verification.functional / hostEmulation / per-surface test)
|
|
9
9
|
* capability --uses--> skill | rule (skill + dependencies.skills/.rules)
|
|
10
10
|
* capability --governed_by-->contract (contracts[])
|
|
11
11
|
* capability --exposes--> surface (supported surfaces)
|
|
12
|
+
*
|
|
13
|
+
* Extended seeding (LMCP-C1) also produces:
|
|
14
|
+
* provider --owned_by--> specialist (manifest owner)
|
|
15
|
+
* provider --requires--> tool (manifest capabilities/operations)
|
|
16
|
+
* specialist --owned_by--> specialist (team membership)
|
|
17
|
+
* specialist --uses--> skill (specialist skills)
|
|
18
|
+
* doc --documents--> workflow|provider (heuristic linking)
|
|
19
|
+
*
|
|
20
|
+
* Embed binding seeding (LMCP-E4) also produces:
|
|
21
|
+
* specialist --reads--> provider (pack embedBindings[].providers, read/search grant)
|
|
22
|
+
*
|
|
12
23
|
* Node existence on disk (tests, skills, rules) is stamped as an attr so the
|
|
13
24
|
* Oracle collector can later distinguish declared-but-missing from realized.
|
|
14
25
|
* Covers the registry half of the hybrid population; the import-graph half
|
|
15
26
|
* (file/module nodes, imports/covers/realizes) lands in build-import-graph.
|
|
16
27
|
*/
|
|
17
28
|
|
|
18
|
-
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
29
|
+
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
19
30
|
import { loadRegistry } from '../registry/loader.mjs';
|
|
20
31
|
import path from 'node:path';
|
|
21
32
|
import { createHash } from 'node:crypto';
|
|
@@ -23,6 +34,9 @@ import { createHash } from 'node:crypto';
|
|
|
23
34
|
import { listWorkflowDefs } from '../embedded-contract/workflow-defs.mjs';
|
|
24
35
|
import { loadCapabilityRegistry } from '../registry/validate.mjs';
|
|
25
36
|
import { nodeId } from './store.mjs';
|
|
37
|
+
import { loadManifestsFromDir, resolveManifestDirs } from '../extensions/loader.mjs';
|
|
38
|
+
import { assembleRegistry } from '../registry/assemble.mjs';
|
|
39
|
+
import { loadAllPacks } from '../packs/loader.mjs';
|
|
26
40
|
|
|
27
41
|
function readJsonSafe(filePath) {
|
|
28
42
|
try { return JSON.parse(readFileSync(filePath, 'utf8')); } catch { return null; }
|
|
@@ -49,6 +63,23 @@ function declarationImplFiles(cap, rootDir) {
|
|
|
49
63
|
return [...new Set(files.filter((rel) => existsRel(rootDir, rel)))];
|
|
50
64
|
}
|
|
51
65
|
|
|
66
|
+
const DOC_EXTS = new Set(['.md', '.html', '.json']);
|
|
67
|
+
|
|
68
|
+
function scanDocFiles(dir, baseDir) {
|
|
69
|
+
const docs = [];
|
|
70
|
+
let entries;
|
|
71
|
+
try { entries = readdirSync(dir, { withFileTypes: true }); } catch { return []; }
|
|
72
|
+
for (const entry of entries) {
|
|
73
|
+
const full = path.join(dir, entry.name);
|
|
74
|
+
if (entry.isDirectory()) {
|
|
75
|
+
docs.push(...scanDocFiles(full, baseDir));
|
|
76
|
+
} else if (entry.isFile() && DOC_EXTS.has(path.extname(entry.name).toLowerCase())) {
|
|
77
|
+
docs.push(path.relative(baseDir, full));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return docs;
|
|
81
|
+
}
|
|
82
|
+
|
|
52
83
|
function hashFiles(rootDir, rels) {
|
|
53
84
|
const h = createHash('sha256');
|
|
54
85
|
for (const rel of rels) {
|
|
@@ -69,7 +100,7 @@ export function buildFromRegistry({ rootDir }) {
|
|
|
69
100
|
const nodes = [];
|
|
70
101
|
const edges = [];
|
|
71
102
|
const node = (type, key, name, attrs) => nodes.push({ id: nodeId(type, key), type, name: name ?? key, attrs: attrs || {} });
|
|
72
|
-
const edge = (from, rel, to) => edges.push({ from, to, rel, source: 'registry' });
|
|
103
|
+
const edge = (from, rel, to, source) => edges.push({ from, to, rel, source: source || 'registry' });
|
|
73
104
|
|
|
74
105
|
// Workflow catalog: every embedded workflow type is a node regardless of
|
|
75
106
|
// capability coverage, so reverse queries see uncovered workflows.
|
|
@@ -148,10 +179,140 @@ export function buildFromRegistry({ rootDir }) {
|
|
|
148
179
|
}
|
|
149
180
|
}
|
|
150
181
|
|
|
182
|
+
// --- Provider nodes from extension manifests ---
|
|
183
|
+
// Loads builtin manifests and creates provider, tool, and specialist nodes.
|
|
184
|
+
const manifestDirs = resolveManifestDirs({ rootDir });
|
|
185
|
+
const builtinManifests = loadManifestsFromDir(manifestDirs.builtin);
|
|
186
|
+
const manifests = builtinManifests.manifests || [];
|
|
187
|
+
|
|
188
|
+
for (const m of manifests) {
|
|
189
|
+
const provId = nodeId('provider', m.id);
|
|
190
|
+
node('provider', m.id, m.id, {
|
|
191
|
+
kind: m.kind,
|
|
192
|
+
version: m.version,
|
|
193
|
+
owner: m.owner || null,
|
|
194
|
+
capabilities: m.capabilities || [],
|
|
195
|
+
operations: m.operations || [],
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
if (m.owner) {
|
|
199
|
+
const specId = nodeId('specialist', m.owner);
|
|
200
|
+
node('specialist', m.owner, m.owner, { autoCreated: true });
|
|
201
|
+
edge(provId, 'owned_by', specId, 'manifest-loader');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const tokens = new Set([
|
|
205
|
+
...(m.capabilities || []),
|
|
206
|
+
...(m.operations || []),
|
|
207
|
+
]);
|
|
208
|
+
for (const token of tokens) {
|
|
209
|
+
node('tool', token, token, {});
|
|
210
|
+
edge(provId, 'requires', nodeId('tool', token), 'manifest-loader');
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// --- Specialist --reads--> provider edges from pack embedBindings ---
|
|
215
|
+
// Every pack (core + user + project tiers) may declare embedBindings
|
|
216
|
+
// (LMCP-E4); surfacing the read/search grant here makes it visible in
|
|
217
|
+
// `construct graph`/`construct matrix` alongside the authority-guard
|
|
218
|
+
// enforcement in lib/embed/authority-guard.mjs, rather than only living in
|
|
219
|
+
// pack JSON. Errors loading packs are swallowed — a broken project-tier
|
|
220
|
+
// pack should not blank out the whole registry-seeded graph.
|
|
221
|
+
try {
|
|
222
|
+
const { packs } = loadAllPacks({ rootDir });
|
|
223
|
+
for (const pack of packs) {
|
|
224
|
+
for (const [specId, binding] of Object.entries(pack.embedBindings || {})) {
|
|
225
|
+
const specNodeId = nodeId('specialist', specId);
|
|
226
|
+
node('specialist', specId, specId, { autoCreated: true });
|
|
227
|
+
for (const providerBinding of binding.providers || []) {
|
|
228
|
+
if (!providerBinding?.id) continue;
|
|
229
|
+
const provNodeId = nodeId('provider', providerBinding.id);
|
|
230
|
+
node('provider', providerBinding.id, providerBinding.id, {});
|
|
231
|
+
edge(specNodeId, 'reads', provNodeId, 'pack-embed-binding');
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
} catch (err) {
|
|
236
|
+
// pack loading failures should not abort registry graph seeding
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// --- Specialist nodes from role registry ---
|
|
240
|
+
try {
|
|
241
|
+
const orgRegistry = assembleRegistry(rootDir);
|
|
242
|
+
const specialists = orgRegistry.specialists || {};
|
|
243
|
+
const teams = orgRegistry.teams || {};
|
|
244
|
+
|
|
245
|
+
for (const [specId, spec] of Object.entries(specialists)) {
|
|
246
|
+
node('specialist', specId, spec.name || specId, {
|
|
247
|
+
role: spec.role || null,
|
|
248
|
+
modelTier: spec.modelTier || null,
|
|
249
|
+
team: spec.team || null,
|
|
250
|
+
teamId: spec.teamId || null,
|
|
251
|
+
groupId: spec.groupId || null,
|
|
252
|
+
skills: spec.skills || [],
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
if (spec.teamId && teams[spec.teamId]) {
|
|
256
|
+
edge(nodeId('specialist', specId), 'owned_by', nodeId('specialist', spec.teamId), 'registry');
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
for (const skillRef of spec.skills || []) {
|
|
260
|
+
edge(nodeId('specialist', specId), 'uses', nodeId('skill', skillRef), 'registry');
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
} catch (err) {
|
|
264
|
+
// specialists/org may not exist in all contexts; skip gracefully
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// --- Doc nodes from docs/ directory ---
|
|
268
|
+
const docsDir = path.join(rootDir, 'docs');
|
|
269
|
+
const docFiles = scanDocFiles(docsDir, rootDir);
|
|
270
|
+
for (const docRel of docFiles) {
|
|
271
|
+
node('doc', docRel, docRel, { path: docRel });
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// --- Documents edges (heuristic linking) ---
|
|
275
|
+
const providerNodeIds = new Set(nodes.filter(n => n.type === 'provider').map(n => n.id));
|
|
276
|
+
const workflowNodeIds = new Set(nodes.filter(n => n.type === 'workflow').map(n => n.id));
|
|
277
|
+
|
|
278
|
+
for (const dn of nodes) {
|
|
279
|
+
if (dn.type !== 'doc') continue;
|
|
280
|
+
const docPath = dn.attrs?.path || dn.name || '';
|
|
281
|
+
|
|
282
|
+
if (docPath.includes('adr/') && docPath.toLowerCase().includes('workflow')) {
|
|
283
|
+
for (const wfId of workflowNodeIds) {
|
|
284
|
+
edge(dn.id, 'documents', wfId, 'doc-scan');
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (docPath.includes('adr/') && docPath.toLowerCase().includes('provider')) {
|
|
289
|
+
for (const provId of providerNodeIds) {
|
|
290
|
+
edge(dn.id, 'documents', provId, 'doc-scan');
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (docPath.includes('adr/') && docPath.toLowerCase().includes('manifest')) {
|
|
295
|
+
for (const provId of providerNodeIds) {
|
|
296
|
+
edge(dn.id, 'documents', provId, 'doc-scan');
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
151
301
|
const sourceHash = hashFiles(rootDir, [
|
|
152
302
|
'registry/capabilities.json',
|
|
153
303
|
'specialists/org',
|
|
154
304
|
'lib/embedded-contract/workflow-defs.mjs',
|
|
305
|
+
'lib/embedded-contract/workflows',
|
|
306
|
+
'lib/extensions/manifest-schema.mjs',
|
|
307
|
+
'lib/extensions/loader.mjs',
|
|
308
|
+
'lib/extensions/validate.mjs',
|
|
309
|
+
'lib/extensions/manifests',
|
|
310
|
+
'docs',
|
|
311
|
+
'lib/registry/assemble.mjs',
|
|
312
|
+
'specialists/org',
|
|
313
|
+
'lib/packs/manifest-schema.mjs',
|
|
314
|
+
'lib/packs/loader.mjs',
|
|
315
|
+
'lib/packs/core-pack.mjs',
|
|
155
316
|
]);
|
|
156
317
|
|
|
157
318
|
return { nodes, edges, sourceHash };
|