@geraldmaron/construct 1.4.2 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.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 +57 -45
- 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 +273 -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 +104 -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
package/lib/status.mjs
CHANGED
|
@@ -26,6 +26,21 @@ import { resolveTraceBackend, telemetryProviderLabel } from './telemetry/client.
|
|
|
26
26
|
import { doctorRoot } from './config/xdg.mjs';
|
|
27
27
|
import { resolveUiColors } from './ui/theme.mjs';
|
|
28
28
|
import { applyLinks, terminalLinksEnabled } from './ui/links.mjs';
|
|
29
|
+
import { getModeCapabilityStatus, getUnsupportedCapabilities, getCapabilities, categorizeEnterpriseCapability } from './mode-capabilities.mjs';
|
|
30
|
+
import { ProviderRegistry } from './embed/providers/registry.mjs';
|
|
31
|
+
import { resolveProviders } from './providers/registry.mjs';
|
|
32
|
+
import { getState as getBreakerState } from './providers/circuit-breaker.mjs';
|
|
33
|
+
import { ConsumptionBudgetStore } from './policy/consumption-budget.mjs';
|
|
34
|
+
import { loadGraph, nodesByType, nodeId } from './graph/store.mjs';
|
|
35
|
+
import { validateGraph } from './graph/validate.mjs';
|
|
36
|
+
import { computeLastExecutionByWorkflow } from './graph/runtime-evidence.mjs';
|
|
37
|
+
import { summarizeTeamHealth } from './team/health.mjs';
|
|
38
|
+
import { loadProjectConfig } from './config/project-config.mjs';
|
|
39
|
+
import { projectKey } from './orchestration/store.mjs';
|
|
40
|
+
import { resolveTraceStore } from './orchestration/trace-store.mjs';
|
|
41
|
+
import { resolveSharedMemoryStore } from './storage/shared-memory.mjs';
|
|
42
|
+
import { runtimeDir as orchestrationRuntimeDir } from './orchestration/run-store.mjs';
|
|
43
|
+
import { resolveStateDir } from './state-root.mjs';
|
|
29
44
|
const TOTAL_BYTES_WARNING_THRESHOLD = 750_000;
|
|
30
45
|
|
|
31
46
|
function readJSON(path) {
|
|
@@ -190,6 +205,252 @@ export function readSessionUsage(homeDir) {
|
|
|
190
205
|
}
|
|
191
206
|
|
|
192
207
|
|
|
208
|
+
/**
|
|
209
|
+
* readDegradationRecords(cwd)
|
|
210
|
+
*
|
|
211
|
+
* Reads `.cx/degradation.jsonl` (written by `requireTeamCapabilityOrDegrade` in
|
|
212
|
+
* deployment-mode.mjs) and returns an array of structured degradation detail objects.
|
|
213
|
+
*
|
|
214
|
+
* Each record in the JSONL file has the shape written by deployment-mode.mjs:
|
|
215
|
+
* { ts, mode, subsystem, degradedOk: true }
|
|
216
|
+
*
|
|
217
|
+
* Each record is surfaced as:
|
|
218
|
+
* { subsystem, declared, actual, reason }
|
|
219
|
+
*
|
|
220
|
+
* `declared` and `actual` are inferred from the subsystem name when not
|
|
221
|
+
* explicitly present in the record (the deployment-mode writer does not yet emit
|
|
222
|
+
* them). `reason` reflects the CONSTRUCT_DEGRADED_OK value.
|
|
223
|
+
*
|
|
224
|
+
* Returns an empty array when the file is absent or unreadable.
|
|
225
|
+
*/
|
|
226
|
+
function readDegradationRecords(cwd) {
|
|
227
|
+
const filePath = join(cwd, '.cx', 'degradation.jsonl');
|
|
228
|
+
if (!existsSync(filePath)) return [];
|
|
229
|
+
let lines;
|
|
230
|
+
try {
|
|
231
|
+
lines = readFileSync(filePath, 'utf8').split('\n').filter(Boolean);
|
|
232
|
+
} catch {
|
|
233
|
+
return [];
|
|
234
|
+
}
|
|
235
|
+
const records = [];
|
|
236
|
+
for (const line of lines) {
|
|
237
|
+
try {
|
|
238
|
+
const entry = JSON.parse(line);
|
|
239
|
+
if (!entry || typeof entry.subsystem !== 'string') continue;
|
|
240
|
+
// Infer declared/actual from the subsystem id when not explicit.
|
|
241
|
+
// Convention: subsystem ids use the format "<declared>-queue", "<declared>-memory", etc.
|
|
242
|
+
const declared = entry.declared ?? entry.subsystem;
|
|
243
|
+
const actual = entry.actual ?? 'fallback';
|
|
244
|
+
const reason = entry.reason
|
|
245
|
+
?? (entry.degradedOk ? `CONSTRUCT_DEGRADED_OK=${entry.subsystem} set` : 'degraded');
|
|
246
|
+
records.push({
|
|
247
|
+
subsystem: entry.subsystem,
|
|
248
|
+
declared,
|
|
249
|
+
actual,
|
|
250
|
+
reason,
|
|
251
|
+
ts: entry.ts ?? null,
|
|
252
|
+
mode: entry.mode ?? null,
|
|
253
|
+
});
|
|
254
|
+
} catch {
|
|
255
|
+
// malformed line — skip
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return records;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* countPersonaDegradedRuns(cwd)
|
|
263
|
+
*
|
|
264
|
+
* Reads orchestration run records from the machine-scoped state root
|
|
265
|
+
* (`<stateRoot>/runtime/orchestration/runs/`, ADR-0066 — same directory
|
|
266
|
+
* lib/orchestration/run-store.mjs's runtimeDir() writes to) and counts runs
|
|
267
|
+
* carrying at least one task with `personaAvailable === false` (LMCP-E2:
|
|
268
|
+
* solo-mode persona fallback). Reads the filesystem store directly — the
|
|
269
|
+
* same best-effort, no-throw pattern as readDegradationRecords — since
|
|
270
|
+
* status is a read-only surface and must not fail when a run file is
|
|
271
|
+
* corrupt or the sqlite/postgres store is in use instead of the default.
|
|
272
|
+
*
|
|
273
|
+
* Returns { total, runs } where `runs` is the degraded run ids (capped at 20
|
|
274
|
+
* for the summary), and `total` counts every matching run.
|
|
275
|
+
*/
|
|
276
|
+
function countPersonaDegradedRuns(cwd) {
|
|
277
|
+
const dir = join(orchestrationRuntimeDir(cwd), 'runs');
|
|
278
|
+
if (!existsSync(dir)) return { total: 0, runs: [] };
|
|
279
|
+
let entries;
|
|
280
|
+
try {
|
|
281
|
+
entries = readdirSync(dir);
|
|
282
|
+
} catch {
|
|
283
|
+
return { total: 0, runs: [] };
|
|
284
|
+
}
|
|
285
|
+
const runs = [];
|
|
286
|
+
for (const name of entries) {
|
|
287
|
+
if (!name.endsWith('.json')) continue;
|
|
288
|
+
try {
|
|
289
|
+
const run = JSON.parse(readFileSync(join(dir, name), 'utf8'));
|
|
290
|
+
const hasPersonaFallback = (run.tasks || []).some((t) => t.personaAvailable === false);
|
|
291
|
+
if (hasPersonaFallback) runs.push(run.runId || name.replace(/\.json$/, ''));
|
|
292
|
+
} catch {
|
|
293
|
+
// a corrupt run file is skipped, not fatal to the count
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return { total: runs.length, runs: runs.slice(0, 20) };
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* summarizeRecentRunExecutionStates(cwd, opts)
|
|
301
|
+
*
|
|
302
|
+
* Reads orchestration run records from the machine-scoped state root (same
|
|
303
|
+
* `<stateRoot>/runtime/orchestration/runs/` directory as
|
|
304
|
+
* countPersonaDegradedRuns above, ADR-0066) and buckets the most recent runs
|
|
305
|
+
* by their LMCP-F4 run-level `executionState`
|
|
306
|
+
* (prepared|executed|degraded-executed|failed|unknown), so `construct status`
|
|
307
|
+
* can distinguish a run that only prepared specialist work from one that
|
|
308
|
+
* actually executed it — the honesty gap construct-1yhp.2 and incident
|
|
309
|
+
* run-02158a157d53 (construct-neq9.7) both trace back to.
|
|
310
|
+
*
|
|
311
|
+
* A run with no tasks (prompt-only/host-direct) or a pre-F4 legacy record
|
|
312
|
+
* carrying no `executionState` field is bucketed `unknown` rather than guessed
|
|
313
|
+
* into `prepared` or `executed`.
|
|
314
|
+
*
|
|
315
|
+
* Returns { total, byState: {prepared,executed,degraded-executed,failed,unknown},
|
|
316
|
+
* recent: [{runId, executionState, status, createdAt}] } ordered newest-first
|
|
317
|
+
* and capped at `limit` (default 10) for the `recent` list; `byState` counts
|
|
318
|
+
* every readable run regardless of the cap.
|
|
319
|
+
*/
|
|
320
|
+
function summarizeRecentRunExecutionStates(cwd, { limit = 10 } = {}) {
|
|
321
|
+
const dir = join(orchestrationRuntimeDir(cwd), 'runs');
|
|
322
|
+
const empty = { total: 0, byState: { prepared: 0, executed: 0, 'degraded-executed': 0, failed: 0, unknown: 0 }, recent: [] };
|
|
323
|
+
if (!existsSync(dir)) return empty;
|
|
324
|
+
let entries;
|
|
325
|
+
try {
|
|
326
|
+
entries = readdirSync(dir);
|
|
327
|
+
} catch {
|
|
328
|
+
return empty;
|
|
329
|
+
}
|
|
330
|
+
const runs = [];
|
|
331
|
+
for (const name of entries) {
|
|
332
|
+
if (!name.endsWith('.json')) continue;
|
|
333
|
+
try {
|
|
334
|
+
const run = JSON.parse(readFileSync(join(dir, name), 'utf8'));
|
|
335
|
+
runs.push({
|
|
336
|
+
runId: run.runId || name.replace(/\.json$/, ''),
|
|
337
|
+
executionState: run.executionState ?? 'unknown',
|
|
338
|
+
status: run.status ?? 'unknown',
|
|
339
|
+
createdAt: run.createdAt ?? null,
|
|
340
|
+
});
|
|
341
|
+
} catch {
|
|
342
|
+
// a corrupt run file is skipped, not fatal to the summary
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
runs.sort((a, b) => String(b.createdAt || '').localeCompare(String(a.createdAt || '')));
|
|
346
|
+
|
|
347
|
+
const byState = { prepared: 0, executed: 0, 'degraded-executed': 0, failed: 0, unknown: 0 };
|
|
348
|
+
for (const r of runs) {
|
|
349
|
+
const key = Object.hasOwn(byState, r.executionState) ? r.executionState : 'unknown';
|
|
350
|
+
byState[key] += 1;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return { total: runs.length, byState, recent: runs.slice(0, limit) };
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// LMCP-G10: cross-machine read side of team mode's trace/memory boundary.
|
|
357
|
+
// lib/orchestration/runtime.mjs's persistTeamTrace writes every completed
|
|
358
|
+
// run's lifecycle trace into the same postgres-backed trace store this reads
|
|
359
|
+
// back from, keyed on the same projectKey(config, cwd) — a trace worker A's
|
|
360
|
+
// machine wrote is the exact row machine B's `construct status` lists here.
|
|
361
|
+
// Shared memory (lib/storage/shared-memory.mjs) is listed the same way; only
|
|
362
|
+
// records that opted in with visibility 'shared-project' + provenance ever
|
|
363
|
+
// appear. Solo mode (both stores resolve to kind:'none') reports
|
|
364
|
+
// unavailable rather than fabricating an empty list as "no team activity".
|
|
365
|
+
|
|
366
|
+
async function summarizeSharedState(cwd, env, config) {
|
|
367
|
+
const project = projectKey(config, cwd);
|
|
368
|
+
const traceStore = resolveTraceStore({ env, cwd, config });
|
|
369
|
+
const memoryStore = resolveSharedMemoryStore({ env, cwd, config });
|
|
370
|
+
if (traceStore.kind !== 'postgres' && memoryStore.kind !== 'postgres') {
|
|
371
|
+
return { status: 'unavailable', reason: 'no-team-store-configured', recentTraces: [], sharedMemory: [] };
|
|
372
|
+
}
|
|
373
|
+
const [recentTraces, sharedMemory] = await Promise.all([
|
|
374
|
+
traceStore.kind === 'postgres' ? traceStore.listTeamTraces({ project, limit: 5 }).catch(() => []) : Promise.resolve([]),
|
|
375
|
+
memoryStore.kind === 'postgres' ? memoryStore.listSharedMemory({ project, limit: 5 }).catch(() => []) : Promise.resolve([]),
|
|
376
|
+
]);
|
|
377
|
+
return { status: 'healthy', project, recentTraces, sharedMemory };
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* summarizeGraphWorkflows(rootDir)
|
|
382
|
+
*
|
|
383
|
+
* Classifies every workflow node in the living graph (LMCP-C1/C2) as
|
|
384
|
+
* available / degraded / missing, sourced from the same validateGraph() the
|
|
385
|
+
* `graph validate` CLI and the doctor check (lib/doctor/graph-validate.mjs)
|
|
386
|
+
* both call — so status, doctor, and `construct graph validate` can never
|
|
387
|
+
* report three different truths about the same workflow.
|
|
388
|
+
*
|
|
389
|
+
* - missing: no `workflow:<id>` node exists in the graph (or the graph has
|
|
390
|
+
* not been built yet).
|
|
391
|
+
* - degraded: the node exists but validateGraph raised an error or warning
|
|
392
|
+
* that names this workflow, or the most recent runtime-evidence outcome
|
|
393
|
+
* (LMCP-C9, runtime-evidence.mjs) for it is 'failed'.
|
|
394
|
+
* - available: the node exists and neither of the above applies.
|
|
395
|
+
*
|
|
396
|
+
* Read-only against .cx/graph (project-local) and the machine-scoped
|
|
397
|
+
* orchestration run store (ADR-0066) — this function never writes to the
|
|
398
|
+
* graph store or the runtime-evidence store.
|
|
399
|
+
*/
|
|
400
|
+
function summarizeGraphWorkflows(rootDir) {
|
|
401
|
+
const graph = loadGraph(rootDir);
|
|
402
|
+
if (!graph.exists) {
|
|
403
|
+
return { available: [], degraded: [], missing: [], graphBuilt: false, errors: [], warnings: [] };
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const validation = validateGraph(rootDir);
|
|
407
|
+
const graphWorkflowIds = new Set(nodesByType(graph, 'workflow').map((n) => n.id));
|
|
408
|
+
const lastExecutionByWorkflow = computeLastExecutionByWorkflow(rootDir);
|
|
409
|
+
|
|
410
|
+
const findingsFor = (id, name) => {
|
|
411
|
+
const needles = [id, name].filter(Boolean);
|
|
412
|
+
const matches = (msg) => needles.some((needle) => msg.includes(`'${needle}'`));
|
|
413
|
+
return {
|
|
414
|
+
errors: validation.errors.filter(matches),
|
|
415
|
+
warnings: validation.warnings.filter(matches),
|
|
416
|
+
};
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
const available = [];
|
|
420
|
+
const degraded = [];
|
|
421
|
+
const missing = [];
|
|
422
|
+
|
|
423
|
+
for (const type of Object.keys(lastExecutionByWorkflow)) {
|
|
424
|
+
const id = nodeId('workflow', type);
|
|
425
|
+
const evidence = lastExecutionByWorkflow[type];
|
|
426
|
+
if (!graphWorkflowIds.has(id)) {
|
|
427
|
+
missing.push({ type, reason: 'no workflow node in living graph' });
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
const findings = findingsFor(id, type);
|
|
431
|
+
const evidenceFailed = evidence?.outcome === 'failed';
|
|
432
|
+
if (findings.errors.length > 0 || findings.warnings.length > 0 || evidenceFailed) {
|
|
433
|
+
degraded.push({
|
|
434
|
+
type,
|
|
435
|
+
errors: findings.errors,
|
|
436
|
+
warnings: findings.warnings,
|
|
437
|
+
lastExecution: evidence,
|
|
438
|
+
});
|
|
439
|
+
} else {
|
|
440
|
+
available.push({ type, lastExecution: evidence });
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return {
|
|
445
|
+
available,
|
|
446
|
+
degraded,
|
|
447
|
+
missing,
|
|
448
|
+
graphBuilt: true,
|
|
449
|
+
errors: validation.errors,
|
|
450
|
+
warnings: validation.warnings,
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
|
|
193
454
|
function newestMtime(dir) {
|
|
194
455
|
if (!existsSync(dir)) return null;
|
|
195
456
|
let newest = 0;
|
|
@@ -438,6 +699,79 @@ function summarizeIntegrations(features) {
|
|
|
438
699
|
return { counts, summary: summary || 'No integrations detected' };
|
|
439
700
|
}
|
|
440
701
|
|
|
702
|
+
// Embed daemon provider set is the unified extension registry (lib/extensions/*)
|
|
703
|
+
// filtered by capability + credential presence (LMCP-B3) — this reads the same
|
|
704
|
+
// ProviderRegistry.fromEnv() the daemon builds, so status can never drift from
|
|
705
|
+
// what the daemon actually resolves.
|
|
706
|
+
|
|
707
|
+
async function summarizeEmbedProviders(env) {
|
|
708
|
+
const registry = await ProviderRegistry.fromEnv(env);
|
|
709
|
+
const available = registry.names().filter((name, idx, all) => all.indexOf(name) === idx);
|
|
710
|
+
const unavailable = registry.unavailable();
|
|
711
|
+
|
|
712
|
+
const summary = [
|
|
713
|
+
available.length ? `${available.length} available` : null,
|
|
714
|
+
unavailable.length ? `${unavailable.length} unavailable` : null,
|
|
715
|
+
].filter(Boolean).join(' · ') || 'no embed providers configured';
|
|
716
|
+
|
|
717
|
+
return {
|
|
718
|
+
summary,
|
|
719
|
+
available,
|
|
720
|
+
unavailable: unavailable.map((u) => ({ id: u.id, reason: u.reason })),
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// Circuit-breaker state (lib/providers/circuit-breaker.mjs) is per resolved
|
|
725
|
+
// data-source provider (lib/providers/registry.mjs wraps read/search/watch/
|
|
726
|
+
// write/webhook with a breaker keyed 'provider:<id>', failureThreshold=5,
|
|
727
|
+
// cooldownMs=30_000 — LMCP-B9). Reading it here through resolveProviders()
|
|
728
|
+
// means status can never show a provider getBreaker never wrapped.
|
|
729
|
+
|
|
730
|
+
async function summarizeProviderBreakers({ rootDir, env }) {
|
|
731
|
+
const { providers, errors } = await resolveProviders({ rootDir, env });
|
|
732
|
+
const entries = Object.keys(providers).sort().map((id) => {
|
|
733
|
+
const breaker = getBreakerState(`provider:${id}`);
|
|
734
|
+
const state = breaker ? breaker.state : 'CLOSED';
|
|
735
|
+
return {
|
|
736
|
+
id,
|
|
737
|
+
state,
|
|
738
|
+
failures: breaker ? breaker.failures : 0,
|
|
739
|
+
lastFailure: breaker?.lastFailure ? breaker.lastFailure.toISOString() : null,
|
|
740
|
+
open: state === 'OPEN',
|
|
741
|
+
};
|
|
742
|
+
});
|
|
743
|
+
const openCount = entries.filter((e) => e.open).length;
|
|
744
|
+
const summary = entries.length === 0
|
|
745
|
+
? 'No providers configured'
|
|
746
|
+
: openCount > 0
|
|
747
|
+
? `${openCount} of ${entries.length} provider circuit(s) OPEN`
|
|
748
|
+
: `${entries.length} provider(s), all circuits closed`;
|
|
749
|
+
return { entries, openCount, summary, loadErrors: errors };
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// Per-actor/run durable consumption (lib/policy/consumption-budget.mjs,
|
|
753
|
+
// LMCP-N5) — accrued by lib/mcp/broker.mjs's Broker.invoke whenever a caller
|
|
754
|
+
// passes a runId. Rows only exist for runs that have actually consumed
|
|
755
|
+
// something, so an empty list here means no run has recorded consumption
|
|
756
|
+
// yet, not that budgets are unconfigured.
|
|
757
|
+
|
|
758
|
+
function summarizeConsumptionBudgets({ rootDir, env }) {
|
|
759
|
+
const store = new ConsumptionBudgetStore({ rootDir, env });
|
|
760
|
+
const entries = store.allEntries().map((entry) => {
|
|
761
|
+
const overBudget = entry.budget
|
|
762
|
+
? Object.entries(entry.budget).some(([kind, cap]) => Number.isFinite(cap) && entry.consumption[kind] > cap)
|
|
763
|
+
: false;
|
|
764
|
+
return { ...entry, overBudget };
|
|
765
|
+
});
|
|
766
|
+
const overCount = entries.filter((e) => e.overBudget).length;
|
|
767
|
+
const summary = entries.length === 0
|
|
768
|
+
? 'No run consumption recorded yet'
|
|
769
|
+
: overCount > 0
|
|
770
|
+
? `${overCount} of ${entries.length} run(s) over budget`
|
|
771
|
+
: `${entries.length} run(s) tracked, all within budget`;
|
|
772
|
+
return { entries, overCount, summary };
|
|
773
|
+
}
|
|
774
|
+
|
|
441
775
|
function summarizePlugins(pluginRegistry) {
|
|
442
776
|
const plugins = pluginRegistry?.plugins ?? [];
|
|
443
777
|
const external = plugins.filter((plugin) => !plugin.builtIn);
|
|
@@ -497,14 +831,14 @@ function traceBackendDefinition(env) {
|
|
|
497
831
|
return {
|
|
498
832
|
id: 'telemetry',
|
|
499
833
|
name: 'Telemetry',
|
|
500
|
-
url: 'local
|
|
834
|
+
url: 'local://<state-root>/traces',
|
|
501
835
|
runtime: 'local',
|
|
502
836
|
note: backend === 'none' ? 'Remote disabled; local JSONL preserved' : 'Local JSONL traces',
|
|
503
837
|
healthyMessage: 'Local trace capture enabled',
|
|
504
838
|
impactsOverall: false,
|
|
505
839
|
selfCheck: {
|
|
506
840
|
status: backend === 'none' ? 'disabled' : 'healthy',
|
|
507
|
-
message: backend === 'none' ? 'Remote telemetry disabled' : 'Writing
|
|
841
|
+
message: backend === 'none' ? 'Remote telemetry disabled' : 'Writing <state-root>/traces/*.jsonl',
|
|
508
842
|
},
|
|
509
843
|
};
|
|
510
844
|
}
|
|
@@ -572,6 +906,111 @@ function serviceDefinitions(env) {
|
|
|
572
906
|
];
|
|
573
907
|
}
|
|
574
908
|
|
|
909
|
+
// Storage health: real filesystem state plus a live LanceDB open probe (tableNames() call).
|
|
910
|
+
// A corrupted or locked store reports unhealthy. A timeout (default 2000ms) prevents a
|
|
911
|
+
// locked DB from stalling `construct status`. Results are cached with a short TTL.
|
|
912
|
+
|
|
913
|
+
const STORAGE_PROBE_CACHE = new Map();
|
|
914
|
+
const STORAGE_PROBE_CACHE_TTL_MS = 5000;
|
|
915
|
+
|
|
916
|
+
// Exported for testing: allows callers to inject a custom lancedb opener and clock.
|
|
917
|
+
export async function probeStorageHealth(
|
|
918
|
+
cwd,
|
|
919
|
+
{
|
|
920
|
+
env = process.env,
|
|
921
|
+
fsExistsSync = existsSync,
|
|
922
|
+
lancedbOpener = null,
|
|
923
|
+
probeTimeoutMs = 2000,
|
|
924
|
+
now = () => Date.now(),
|
|
925
|
+
} = {},
|
|
926
|
+
) {
|
|
927
|
+
const cxExists = fsExistsSync(join(cwd, '.cx'));
|
|
928
|
+
|
|
929
|
+
// CONSTRUCT_LANCEDB_PATH wins when set (a managed install shares one
|
|
930
|
+
// machine-wide store); otherwise the index lives at the machine-scoped
|
|
931
|
+
// state root (ADR-0066), not a project-relative `.cx/lancedb` — mirrors
|
|
932
|
+
// lib/storage/vector-client.mjs's own resolution so status never reports a
|
|
933
|
+
// location the real store doesn't use.
|
|
934
|
+
|
|
935
|
+
const lancedbPath = env.CONSTRUCT_LANCEDB_PATH || resolveStateDir(cwd, 'lancedb', { ensureDir: false });
|
|
936
|
+
const lancedbExists = fsExistsSync(lancedbPath);
|
|
937
|
+
|
|
938
|
+
const sqlStore = { mode: 'lancedb', label: 'LanceDB + Git-Backed', dbUrl: null, vectorEnabled: true };
|
|
939
|
+
|
|
940
|
+
if (!cxExists) {
|
|
941
|
+
return {
|
|
942
|
+
sqlStore,
|
|
943
|
+
sqlHealth: { status: 'unavailable', message: 'Local store not initialized (.cx/ absent)' },
|
|
944
|
+
vectorStore: { enabled: false, backend: 'lancedb', label: 'Embedded LanceDB' },
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
if (!lancedbExists) {
|
|
949
|
+
return {
|
|
950
|
+
sqlStore,
|
|
951
|
+
sqlHealth: { status: 'degraded', message: 'Local embedded — no vector index yet (no lancedb store found)' },
|
|
952
|
+
vectorStore: { enabled: false, backend: 'lancedb', label: 'Embedded LanceDB' },
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
// Check cache before probing.
|
|
957
|
+
const cacheKey = lancedbPath;
|
|
958
|
+
const cached = STORAGE_PROBE_CACHE.get(cacheKey);
|
|
959
|
+
if (cached && now() - cached.at < STORAGE_PROBE_CACHE_TTL_MS) {
|
|
960
|
+
return cached.result;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
// Probe the LanceDB store: open it and call tableNames() with a timeout.
|
|
964
|
+
let sqlHealth;
|
|
965
|
+
try {
|
|
966
|
+
const opener = lancedbOpener ?? (async (p) => {
|
|
967
|
+
const ldb = await import('@lancedb/lancedb');
|
|
968
|
+
return ldb.connect(p);
|
|
969
|
+
});
|
|
970
|
+
|
|
971
|
+
const probeResult = await Promise.race([
|
|
972
|
+
(async () => {
|
|
973
|
+
const db = await opener(lancedbPath);
|
|
974
|
+
await db.tableNames();
|
|
975
|
+
return { status: 'healthy', message: 'Local embedded' };
|
|
976
|
+
})(),
|
|
977
|
+
new Promise((resolve) =>
|
|
978
|
+
setTimeout(
|
|
979
|
+
() => resolve({ status: 'unhealthy', reason: 'LanceDB open timed out' }),
|
|
980
|
+
probeTimeoutMs,
|
|
981
|
+
),
|
|
982
|
+
),
|
|
983
|
+
]);
|
|
984
|
+
|
|
985
|
+
if (probeResult.status === 'unhealthy') {
|
|
986
|
+
sqlHealth = { status: 'unhealthy', message: probeResult.reason };
|
|
987
|
+
} else {
|
|
988
|
+
sqlHealth = probeResult;
|
|
989
|
+
}
|
|
990
|
+
} catch (err) {
|
|
991
|
+
sqlHealth = { status: 'unhealthy', message: `LanceDB open failed: ${err?.message ?? String(err)}` };
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
const result = {
|
|
995
|
+
sqlStore,
|
|
996
|
+
sqlHealth,
|
|
997
|
+
vectorStore: {
|
|
998
|
+
enabled: sqlHealth.status === 'healthy',
|
|
999
|
+
backend: 'lancedb',
|
|
1000
|
+
label: 'Embedded LanceDB',
|
|
1001
|
+
},
|
|
1002
|
+
};
|
|
1003
|
+
|
|
1004
|
+
STORAGE_PROBE_CACHE.set(cacheKey, { at: now(), result });
|
|
1005
|
+
return result;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
// categorizeEnterpriseCapability (ADR-0057's active/fail-closed/later/absent
|
|
1009
|
+
// partition) lives in mode-capabilities.mjs — the single authoritative registry
|
|
1010
|
+
// module — and is re-exported here so existing status.mjs consumers/tests don't
|
|
1011
|
+
// need a second import path.
|
|
1012
|
+
export { categorizeEnterpriseCapability } from './mode-capabilities.mjs';
|
|
1013
|
+
|
|
575
1014
|
export async function buildStatus({
|
|
576
1015
|
rootDir,
|
|
577
1016
|
cwd = process.cwd(),
|
|
@@ -591,6 +1030,9 @@ export async function buildStatus({
|
|
|
591
1030
|
registry = { teams: {}, specialists: {}, contracts: {}, policies: {} };
|
|
592
1031
|
}
|
|
593
1032
|
const settings = readJSON(join(homeDir, '.claude', 'settings.json')) ?? {};
|
|
1033
|
+
// MCP server definitions live in ~/.claude.json's top-level `mcpServers`, not
|
|
1034
|
+
// settings.json (settings.json carries hooks/permissions only — construct-ranh).
|
|
1035
|
+
const claudeUserConfig = readJSON(join(homeDir, '.claude.json')) ?? {};
|
|
594
1036
|
const features = await checkAllFeatures({ homeDir, cwd, env: mergedEnv });
|
|
595
1037
|
const pluginRegistry = loadPluginRegistry({ cwd, homeDir, rootDir, env: mergedEnv });
|
|
596
1038
|
const services = [];
|
|
@@ -622,6 +1064,9 @@ export async function buildStatus({
|
|
|
622
1064
|
|
|
623
1065
|
const runtime = summarizeRuntime(services);
|
|
624
1066
|
const integrations = summarizeIntegrations(features);
|
|
1067
|
+
const embedProviders = await summarizeEmbedProviders(mergedEnv);
|
|
1068
|
+
const providerBreakers = await summarizeProviderBreakers({ rootDir: cwd, env: mergedEnv });
|
|
1069
|
+
const consumptionBudgets = summarizeConsumptionBudgets({ rootDir: cwd, env: mergedEnv });
|
|
625
1070
|
const plugins = summarizePlugins(pluginRegistry);
|
|
626
1071
|
const contextInspection = inspectContextState(cwd);
|
|
627
1072
|
const sessionEfficiency = readSessionEfficiency(homeDir);
|
|
@@ -634,10 +1079,12 @@ export async function buildStatus({
|
|
|
634
1079
|
triggerAutoBackfillIfSparse(telemetryRichness, { ...mergedEnv });
|
|
635
1080
|
const efficiencyDigest = summarizeEfficiencyData(readEfficiencyLog(homeDir));
|
|
636
1081
|
const activeOverlays = getActiveOverlays(cwd);
|
|
637
|
-
const
|
|
638
|
-
const vectorStore = { enabled: true, backend: 'lancedb', label: 'Embedded LanceDB' };
|
|
639
|
-
const sqlHealth = { status: 'healthy', message: 'Local embedded' };
|
|
1082
|
+
const { sqlStore, vectorStore, sqlHealth } = await probeStorageHealth(cwd, { env: mergedEnv });
|
|
640
1083
|
const promotionRequests = getPromotionRequests(cwd);
|
|
1084
|
+
const degradationDetails = readDegradationRecords(cwd);
|
|
1085
|
+
const personaDegradedRuns = countPersonaDegradedRuns(cwd);
|
|
1086
|
+
const recentRunExecutionStates = summarizeRecentRunExecutionStates(cwd);
|
|
1087
|
+
const workflows = summarizeGraphWorkflows(cwd);
|
|
641
1088
|
const executionContractModel = resolveExecutionContractModelMetadata({
|
|
642
1089
|
envValues: mergedEnv,
|
|
643
1090
|
registryModels: registry.models ?? {},
|
|
@@ -681,6 +1128,37 @@ export async function buildStatus({
|
|
|
681
1128
|
|
|
682
1129
|
const deploymentMode = getDeploymentMode(mergedEnv);
|
|
683
1130
|
const resourceMode = resolveResourceMode(deploymentMode);
|
|
1131
|
+
const capStatus = getModeCapabilityStatus(deploymentMode);
|
|
1132
|
+
const unsupportedCaps = getUnsupportedCapabilities(deploymentMode);
|
|
1133
|
+
const teamHealth = deploymentMode === 'team' || deploymentMode === 'enterprise'
|
|
1134
|
+
? await summarizeTeamHealth({ rootDir: cwd, env: mergedEnv })
|
|
1135
|
+
: null;
|
|
1136
|
+
const sharedState = deploymentMode === 'team' || deploymentMode === 'enterprise'
|
|
1137
|
+
? await summarizeSharedState(cwd, mergedEnv, loadProjectConfig(cwd, mergedEnv).config)
|
|
1138
|
+
: null;
|
|
1139
|
+
|
|
1140
|
+
// Enterprise mode: build a per-capability truth table so status/doctor can show
|
|
1141
|
+
// the real implementation state rather than a single aggregate label.
|
|
1142
|
+
// The overall enterprise verdict is 'unsupported' until all three ADR-0057
|
|
1143
|
+
// IMPLEMENT-NOW capabilities (tenant-isolation, rbac, mandatory-audit) are
|
|
1144
|
+
// marked 'implemented' in mode-capabilities.mjs.
|
|
1145
|
+
const ENTERPRISE_IMPLEMENT_NOW_IDS = new Set(['tenant-isolation', 'rbac', 'mandatory-audit']);
|
|
1146
|
+
let enterpriseCapabilityTable = null;
|
|
1147
|
+
let enterpriseVerdict = null;
|
|
1148
|
+
if (deploymentMode === 'enterprise') {
|
|
1149
|
+
const allCaps = getCapabilities('enterprise');
|
|
1150
|
+
enterpriseCapabilityTable = allCaps.map((cap) => ({
|
|
1151
|
+
id: cap.id,
|
|
1152
|
+
label: cap.label,
|
|
1153
|
+
status: cap.status,
|
|
1154
|
+
category: categorizeEnterpriseCapability(cap),
|
|
1155
|
+
implementNow: ENTERPRISE_IMPLEMENT_NOW_IDS.has(cap.id),
|
|
1156
|
+
}));
|
|
1157
|
+
const implementNowDone = allCaps
|
|
1158
|
+
.filter((cap) => ENTERPRISE_IMPLEMENT_NOW_IDS.has(cap.id))
|
|
1159
|
+
.every((cap) => cap.status === 'implemented');
|
|
1160
|
+
enterpriseVerdict = implementNowDone ? 'supported' : 'unsupported';
|
|
1161
|
+
}
|
|
684
1162
|
|
|
685
1163
|
return {
|
|
686
1164
|
version: pkg.version ?? '1.0.0',
|
|
@@ -689,6 +1167,12 @@ export async function buildStatus({
|
|
|
689
1167
|
mode: deploymentMode,
|
|
690
1168
|
resourceMode,
|
|
691
1169
|
description: describeDeploymentMode(deploymentMode),
|
|
1170
|
+
capabilityStatus: capStatus,
|
|
1171
|
+
unsupportedCapabilities: capStatus !== 'fully-implemented' ? unsupportedCaps : [],
|
|
1172
|
+
...(deploymentMode === 'enterprise' && {
|
|
1173
|
+
enterpriseCapabilityTable,
|
|
1174
|
+
enterpriseVerdict,
|
|
1175
|
+
}),
|
|
692
1176
|
},
|
|
693
1177
|
system: {
|
|
694
1178
|
overall: runtime,
|
|
@@ -697,6 +1181,9 @@ export async function buildStatus({
|
|
|
697
1181
|
plugins,
|
|
698
1182
|
},
|
|
699
1183
|
features,
|
|
1184
|
+
embedProviders,
|
|
1185
|
+
providerBreakers,
|
|
1186
|
+
consumptionBudgets,
|
|
700
1187
|
plugins: {
|
|
701
1188
|
status: plugins.status,
|
|
702
1189
|
summary: plugins.summary,
|
|
@@ -724,7 +1211,7 @@ export async function buildStatus({
|
|
|
724
1211
|
skills: listSkills(rootDir),
|
|
725
1212
|
commands: listCommands(rootDir),
|
|
726
1213
|
cliCommands: CLI_COMMANDS,
|
|
727
|
-
mcpServers: Object.keys(settings.mcpServers ?? {}),
|
|
1214
|
+
mcpServers: Object.keys(claudeUserConfig.mcpServers ?? settings.mcpServers ?? {}),
|
|
728
1215
|
publicHealth,
|
|
729
1216
|
storage: {
|
|
730
1217
|
sql: sqlStore,
|
|
@@ -736,6 +1223,8 @@ export async function buildStatus({
|
|
|
736
1223
|
: { status: 'unavailable', message: 'No vector index configured; using file-state only' },
|
|
737
1224
|
},
|
|
738
1225
|
},
|
|
1226
|
+
teamHealth,
|
|
1227
|
+
sharedState,
|
|
739
1228
|
executionContractModel,
|
|
740
1229
|
sessionEfficiency,
|
|
741
1230
|
efficiencyDigest,
|
|
@@ -743,6 +1232,10 @@ export async function buildStatus({
|
|
|
743
1232
|
telemetryRichness,
|
|
744
1233
|
overlays: activeOverlays,
|
|
745
1234
|
promotionRequests,
|
|
1235
|
+
degradationDetails,
|
|
1236
|
+
personaDegradedRuns,
|
|
1237
|
+
recentRunExecutionStates,
|
|
1238
|
+
workflows,
|
|
746
1239
|
};
|
|
747
1240
|
}
|
|
748
1241
|
|
|
@@ -776,6 +1269,27 @@ export function formatStatusReport(status) {
|
|
|
776
1269
|
if (status.deployment?.mode) {
|
|
777
1270
|
const r = status.deployment.resourceMode || {};
|
|
778
1271
|
lines.push(`Deployment: ${status.deployment.mode} · queue:${r.queue} · workers:${r.workers} · telemetry:${r.telemetry}`);
|
|
1272
|
+
if (status.deployment.capabilityStatus && status.deployment.capabilityStatus !== 'fully-implemented') {
|
|
1273
|
+
const caps = status.deployment.unsupportedCapabilities ?? [];
|
|
1274
|
+
lines.push(`Mode capabilities: ${status.deployment.capabilityStatus} · ${caps.length} not fully implemented: ${caps.map(c => c.label).join(', ')}`);
|
|
1275
|
+
}
|
|
1276
|
+
if (status.deployment.mode === 'enterprise') {
|
|
1277
|
+
lines.push(`Enterprise verdict: ${status.deployment.enterpriseVerdict ?? 'unsupported'}`);
|
|
1278
|
+
lines.push('WARNING: Enterprise mode: most capabilities not yet implemented. See construct status for details.');
|
|
1279
|
+
// ADR-0057 status contract: render only active/fail-closed/later — never a
|
|
1280
|
+
// bare 'not-implemented' that could read as active, and never collapse
|
|
1281
|
+
// fail-closed (hard-errors at runtime) into later (no runtime effect) or
|
|
1282
|
+
// vice versa. A capability categorized 'absent' is not advertised at all.
|
|
1283
|
+
const table = (status.deployment.enterpriseCapabilityTable ?? []).filter((cap) => cap.category !== 'absent');
|
|
1284
|
+
if (table.length > 0) {
|
|
1285
|
+
lines.push('Enterprise capability table:');
|
|
1286
|
+
for (const cap of table) {
|
|
1287
|
+
const icon = cap.category === 'active' ? '✓' : cap.category === 'fail-closed' ? '✗' : '·';
|
|
1288
|
+
const tag = cap.implementNow ? ' [implement-now]' : '';
|
|
1289
|
+
lines.push(` ${icon} ${cap.label} (${cap.category})${tag}`);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
779
1293
|
}
|
|
780
1294
|
lines.push('Coordination: external tracker + plan.md · single-writer per file · cass-memory for recall');
|
|
781
1295
|
if (status.executionContractModel?.selectedTier && status.executionContractModel?.selectedModel) {
|
|
@@ -785,6 +1299,16 @@ export function formatStatusReport(status) {
|
|
|
785
1299
|
lines.push(`Storage: ${status.storage.sql?.mode ?? 'unknown'} SQL · ${status.storage.vector?.mode ?? 'unknown'} vector`);
|
|
786
1300
|
lines.push(`Storage health: SQL ${status.storage.health?.sql?.status ?? 'unknown'} · vector ${status.storage.health?.vector?.status ?? 'unknown'}`);
|
|
787
1301
|
}
|
|
1302
|
+
if (status.teamHealth) {
|
|
1303
|
+
lines.push(`Team health: ${statusLabel(status.teamHealth.status)} · ${status.teamHealth.summary}`);
|
|
1304
|
+
}
|
|
1305
|
+
if (status.sharedState) {
|
|
1306
|
+
if (status.sharedState.status === 'unavailable') {
|
|
1307
|
+
lines.push('Shared state: unavailable · no team trace/memory store configured');
|
|
1308
|
+
} else {
|
|
1309
|
+
lines.push(`Shared state: ${status.sharedState.recentTraces.length} recent team trace(s) · ${status.sharedState.sharedMemory.length} shared memory record(s)`);
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
788
1312
|
if (status.sessionEfficiency) {
|
|
789
1313
|
lines.push(`Efficiency: ${statusLabel(status.sessionEfficiency.status)} · score ${status.sessionEfficiency.score.toFixed(2)} · ${status.sessionEfficiency.summary}`);
|
|
790
1314
|
for (const warning of status.sessionEfficiency.warnings ?? []) {
|
|
@@ -826,6 +1350,43 @@ export function formatStatusReport(status) {
|
|
|
826
1350
|
lines.push(` - ${request.domain} · ${request.status}${challenge}`);
|
|
827
1351
|
}
|
|
828
1352
|
}
|
|
1353
|
+
if ((status.degradationDetails ?? []).length > 0) {
|
|
1354
|
+
lines.push('Degradation details:');
|
|
1355
|
+
for (const d of status.degradationDetails) {
|
|
1356
|
+
lines.push(` ${d.subsystem}: declared=${d.declared} actual=${d.actual} (${d.reason})`);
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
if ((status.personaDegradedRuns?.total ?? 0) > 0) {
|
|
1360
|
+
lines.push(`Persona-degraded runs: ${status.personaDegradedRuns.total} (solo-mode persona-fallback)`);
|
|
1361
|
+
}
|
|
1362
|
+
if ((status.recentRunExecutionStates?.total ?? 0) > 0) {
|
|
1363
|
+
const b = status.recentRunExecutionStates.byState;
|
|
1364
|
+
lines.push(
|
|
1365
|
+
`Recent runs: ${status.recentRunExecutionStates.total} total`
|
|
1366
|
+
+ ` · prepared ${b.prepared}` + ` · executed ${b.executed}`
|
|
1367
|
+
+ ` · degraded-executed ${b['degraded-executed']}` + ` · failed ${b.failed}`
|
|
1368
|
+
+ (b.unknown ? ` · unknown ${b.unknown}` : ''),
|
|
1369
|
+
);
|
|
1370
|
+
for (const r of status.recentRunExecutionStates.recent.slice(0, 5)) {
|
|
1371
|
+
lines.push(` ${r.runId}: executionState=${r.executionState} (status=${r.status})`);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
if (status.workflows) {
|
|
1375
|
+
lines.push('');
|
|
1376
|
+
if (!status.workflows.graphBuilt) {
|
|
1377
|
+
lines.push('Workflows: living graph not built yet — run `construct graph build`');
|
|
1378
|
+
} else {
|
|
1379
|
+
const w = status.workflows;
|
|
1380
|
+
lines.push(`Workflows: ${w.available.length} available · ${w.degraded.length} degraded · ${w.missing.length} missing (source: graph validate)`);
|
|
1381
|
+
for (const entry of w.degraded) {
|
|
1382
|
+
const reason = entry.errors[0] || entry.warnings[0] || (entry.lastExecution?.outcome === 'failed' ? 'last run failed' : 'degraded');
|
|
1383
|
+
lines.push(` ⚠ ${entry.type} — ${reason}`);
|
|
1384
|
+
}
|
|
1385
|
+
for (const entry of w.missing) {
|
|
1386
|
+
lines.push(` ✗ ${entry.type} — ${entry.reason}`);
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
829
1390
|
lines.push('');
|
|
830
1391
|
lines.push('Runtime');
|
|
831
1392
|
for (const service of status.system.services) {
|
|
@@ -838,6 +1399,36 @@ export function formatStatusReport(status) {
|
|
|
838
1399
|
for (const feature of status.features) {
|
|
839
1400
|
lines.push(` ${serviceIcon(feature.status)} ${feature.name.padEnd(22)} ${statusLabel(feature.status)} — ${feature.message}`);
|
|
840
1401
|
}
|
|
1402
|
+
if (status.embedProviders) {
|
|
1403
|
+
lines.push('');
|
|
1404
|
+
lines.push(`Embed providers: ${status.embedProviders.summary}`);
|
|
1405
|
+
for (const name of status.embedProviders.available) {
|
|
1406
|
+
lines.push(` ${serviceIcon('healthy')} ${name.padEnd(22)} configured`);
|
|
1407
|
+
}
|
|
1408
|
+
for (const entry of status.embedProviders.unavailable) {
|
|
1409
|
+
lines.push(` ${serviceIcon('unavailable')} ${entry.id.padEnd(22)} configured but unavailable — ${entry.reason}`);
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
if (status.providerBreakers && status.providerBreakers.entries.length > 0) {
|
|
1413
|
+
lines.push('');
|
|
1414
|
+
lines.push(`Provider circuits: ${status.providerBreakers.summary}`);
|
|
1415
|
+
for (const entry of status.providerBreakers.entries) {
|
|
1416
|
+
const icon = entry.open ? serviceIcon('unavailable') : serviceIcon('healthy');
|
|
1417
|
+
const detail = entry.open ? ` — OPEN since ${entry.lastFailure} (${entry.failures} failures)` : '';
|
|
1418
|
+
lines.push(` ${icon} ${entry.id.padEnd(22)} ${entry.state}${detail}`);
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
if (status.consumptionBudgets && status.consumptionBudgets.entries.length > 0) {
|
|
1422
|
+
lines.push('');
|
|
1423
|
+
lines.push(`Run consumption: ${status.consumptionBudgets.summary}`);
|
|
1424
|
+
for (const entry of status.consumptionBudgets.entries) {
|
|
1425
|
+
const icon = entry.overBudget ? serviceIcon('unavailable') : serviceIcon('healthy');
|
|
1426
|
+
const budgetNote = entry.budget
|
|
1427
|
+
? `tokens ${entry.consumption.tokens}/${entry.budget.tokens} · toolCalls ${entry.consumption.toolCalls}/${entry.budget.toolCalls}`
|
|
1428
|
+
: `tokens ${entry.consumption.tokens} · toolCalls ${entry.consumption.toolCalls} (unbounded)`;
|
|
1429
|
+
lines.push(` ${icon} ${entry.actor}/${entry.runId} — ${budgetNote}`);
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
841
1432
|
if (status.plugins) {
|
|
842
1433
|
lines.push('');
|
|
843
1434
|
lines.push(`Plugins: ${statusLabel(status.plugins.status)} · ${status.plugins.summary}`);
|