@geraldmaron/construct 1.4.1 → 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 +1029 -65
- package/bin/construct-postinstall.mjs +27 -2
- package/config/tag-vocabulary.json +264 -0
- 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 +7 -30
- 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 +109 -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 +23 -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/embed/worker.mjs +5 -0
- 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 +60 -11
- package/lib/export-validate.mjs +34 -2
- 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/readiness.mjs +109 -0
- package/lib/host-capabilities.mjs +13 -2
- package/lib/host-disposition.mjs +29 -8
- 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/install/stage-project.mjs +41 -4
- package/lib/intake/daemon.mjs +99 -8
- package/lib/intake/git-queue.mjs +110 -38
- package/lib/intake/prepare.mjs +2 -0
- 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-approval.mjs +57 -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 +185 -1268
- 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-rate-limit.mjs +47 -0
- package/lib/mcp/tool-registry.mjs +107 -0
- package/lib/mcp/tool-safety.mjs +95 -0
- package/lib/mcp/tool-surface-parity.mjs +60 -0
- package/lib/mcp/tools/orchestration-delegation-next.tool.mjs +68 -0
- package/lib/mcp/tools/orchestration-run.mjs +197 -19
- package/lib/mcp/tools/orchestration-task-result.tool.mjs +77 -0
- package/lib/mcp/tools/project.mjs +25 -8
- 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 +2 -2
- package/lib/mcp/tools/web-search-governance.mjs +96 -0
- package/lib/mcp/tools/web-search.mjs +5 -76
- 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 +53 -20
- 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/daemon-entry.mjs +6 -0
- 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 +508 -62
- package/lib/orchestration/store.mjs +87 -12
- package/lib/orchestration/trace-store.mjs +125 -0
- package/lib/orchestration/web-capability.mjs +60 -0
- package/lib/orchestration/worker-runtime.mjs +162 -0
- package/lib/orchestration/worker.mjs +763 -80
- package/lib/orchestration-policy.mjs +67 -1111
- package/lib/output-quality.mjs +61 -2
- 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/path-policy.mjs +56 -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 +41 -18
- package/lib/providers/secret-resolver.mjs +263 -31
- 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/catalog.mjs +6 -0
- 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 +15 -4
- 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/registry/validate.mjs +3 -3
- 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/sandbox.mjs +1 -1
- 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 +93 -24
- 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 +84 -7
- 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 +20 -7
- package/personas/construct.md +12 -3
- package/registry/agent-manifest.json +117 -0
- package/registry/capabilities.json +1987 -0
- package/rules/common/comments.md +1 -0
- package/schemas/brand-voice.schema.json +24 -0
- package/schemas/capability-registry.schema.json +72 -0
- package/schemas/certification-run.schema.json +130 -0
- package/schemas/demo-recording.schema.json +46 -0
- package/schemas/eval-dataset.schema.json +79 -0
- package/schemas/execution-capability-profile.schema.json +46 -0
- package/schemas/execution-policy.schema.json +114 -0
- package/schemas/improvement-proposal.schema.json +65 -0
- package/schemas/mcp-tool-output.schema.json +61 -0
- package/schemas/platform-capabilities.schema.json +83 -0
- package/schemas/project-config.schema.json +215 -0
- package/schemas/project-demo.schema.json +60 -0
- package/schemas/provider-behavior-matrix.schema.json +91 -0
- package/schemas/scope.schema.json +197 -0
- package/schemas/specialist-trace.schema.json +107 -0
- package/schemas/team.schema.json +99 -0
- package/schemas/unified-registry.schema.json +546 -0
- package/scripts/sync-specialists.mjs +336 -106
- 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 +41 -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 +27 -31
- 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/vendor/pandoc-ext/README.md +3 -0
- package/vendor/pandoc-ext/diagram.lua +687 -0
- 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,395 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/queue/pg-queue.mjs — optional Postgres kind:'queue' provider.
|
|
3
|
+
*
|
|
4
|
+
* Claiming uses a single UPDATE fed by SELECT ... FOR UPDATE SKIP LOCKED so
|
|
5
|
+
* parallel workers contend inside Postgres instead of racing in process memory.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import crypto from 'node:crypto';
|
|
9
|
+
|
|
10
|
+
import { applyMigrations } from '../db/migrate.mjs';
|
|
11
|
+
|
|
12
|
+
export const PG_QUEUE_DEFAULT_NAME = 'intake';
|
|
13
|
+
export const PG_QUEUE_DEFAULT_LEASE_SECONDS = 120;
|
|
14
|
+
export const PG_QUEUE_DEFAULT_MAX_ATTEMPTS = 3;
|
|
15
|
+
|
|
16
|
+
export function newQueueId(prefix = 'pkt') {
|
|
17
|
+
return `${prefix}-${Date.now().toString(36)}-${crypto.randomBytes(4).toString('hex')}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function nowIso() {
|
|
21
|
+
return new Date().toISOString();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function normalizeEntry(entry, id) {
|
|
25
|
+
return {
|
|
26
|
+
...entry,
|
|
27
|
+
id,
|
|
28
|
+
status: entry?.status || 'pending',
|
|
29
|
+
createdAt: entry?.createdAt || nowIso(),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function rowToEntry(row) {
|
|
34
|
+
return row?.payload || null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function leaseInterval(sql, seconds) {
|
|
38
|
+
return sql`${Number(seconds)} * interval '1 second'`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function backoffInterval(sql, seconds) {
|
|
42
|
+
return sql`${Math.max(0, Number(seconds) || 0)} * interval '1 second'`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export class PostgresIntakeQueue {
|
|
46
|
+
constructor({
|
|
47
|
+
sql,
|
|
48
|
+
project,
|
|
49
|
+
tenantId = 'local',
|
|
50
|
+
queueName = PG_QUEUE_DEFAULT_NAME,
|
|
51
|
+
leaseSeconds = PG_QUEUE_DEFAULT_LEASE_SECONDS,
|
|
52
|
+
} = {}) {
|
|
53
|
+
if (!sql) throw new Error('PostgresIntakeQueue: sql client is required');
|
|
54
|
+
if (!project) throw new Error('PostgresIntakeQueue: project is required');
|
|
55
|
+
this.sql = sql;
|
|
56
|
+
this.project = project;
|
|
57
|
+
this.tenantId = tenantId;
|
|
58
|
+
this.queueName = queueName;
|
|
59
|
+
this.leaseSeconds = Number(leaseSeconds) || PG_QUEUE_DEFAULT_LEASE_SECONDS;
|
|
60
|
+
this._ensured = null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async ensureSchema() {
|
|
64
|
+
if (!this._ensured) this._ensured = applyMigrations(this.sql);
|
|
65
|
+
return this._ensured;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async enqueue(entry) {
|
|
69
|
+
if (!entry?.intake?.sourcePath) throw new Error('enqueue: entry.intake.sourcePath is required');
|
|
70
|
+
await this.ensureSchema();
|
|
71
|
+
const id = entry.id || newQueueId();
|
|
72
|
+
const payload = normalizeEntry(entry, id);
|
|
73
|
+
await this.sql`
|
|
74
|
+
INSERT INTO construct_queue_items (
|
|
75
|
+
project, tenant_id, queue_name, item_id, status, payload, created_at, updated_at, max_attempts
|
|
76
|
+
) VALUES (
|
|
77
|
+
${this.project}, ${this.tenantId}, ${this.queueName}, ${id}, 'pending',
|
|
78
|
+
${this.sql.json(payload)}, ${payload.createdAt}, now(),
|
|
79
|
+
${Number(entry.maxAttempts) > 0 ? Math.floor(Number(entry.maxAttempts)) : PG_QUEUE_DEFAULT_MAX_ATTEMPTS}
|
|
80
|
+
)
|
|
81
|
+
ON CONFLICT (project, tenant_id, queue_name, item_id) DO UPDATE SET
|
|
82
|
+
payload = EXCLUDED.payload,
|
|
83
|
+
status = EXCLUDED.status,
|
|
84
|
+
max_attempts = EXCLUDED.max_attempts,
|
|
85
|
+
updated_at = now()
|
|
86
|
+
`;
|
|
87
|
+
return { id };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async listPending() {
|
|
91
|
+
await this.ensureSchema();
|
|
92
|
+
const rows = await this.sql`
|
|
93
|
+
SELECT payload FROM construct_queue_items
|
|
94
|
+
WHERE project = ${this.project}
|
|
95
|
+
AND tenant_id = ${this.tenantId}
|
|
96
|
+
AND queue_name = ${this.queueName}
|
|
97
|
+
AND status = 'pending'
|
|
98
|
+
ORDER BY created_at ASC, item_id ASC
|
|
99
|
+
`;
|
|
100
|
+
return rows.map(rowToEntry);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async count() {
|
|
104
|
+
await this.ensureSchema();
|
|
105
|
+
const rows = await this.sql`
|
|
106
|
+
SELECT COUNT(*)::int AS count FROM construct_queue_items
|
|
107
|
+
WHERE project = ${this.project}
|
|
108
|
+
AND tenant_id = ${this.tenantId}
|
|
109
|
+
AND queue_name = ${this.queueName}
|
|
110
|
+
AND status = 'pending'
|
|
111
|
+
`;
|
|
112
|
+
return Number(rows[0]?.count || 0);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async read(id) {
|
|
116
|
+
if (!id) return null;
|
|
117
|
+
await this.ensureSchema();
|
|
118
|
+
const rows = await this.sql`
|
|
119
|
+
SELECT payload FROM construct_queue_items
|
|
120
|
+
WHERE project = ${this.project}
|
|
121
|
+
AND tenant_id = ${this.tenantId}
|
|
122
|
+
AND queue_name = ${this.queueName}
|
|
123
|
+
AND item_id = ${id}
|
|
124
|
+
LIMIT 1
|
|
125
|
+
`;
|
|
126
|
+
return rowToEntry(rows[0]);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async claim({ claimedBy, leaseSeconds = this.leaseSeconds } = {}) {
|
|
130
|
+
if (!claimedBy) throw new Error('claim: claimedBy is required');
|
|
131
|
+
await this.ensureSchema();
|
|
132
|
+
const claimId = newQueueId('claim');
|
|
133
|
+
const rows = await this.sql`
|
|
134
|
+
WITH candidate AS (
|
|
135
|
+
SELECT item_id
|
|
136
|
+
FROM construct_queue_items
|
|
137
|
+
WHERE project = ${this.project}
|
|
138
|
+
AND tenant_id = ${this.tenantId}
|
|
139
|
+
AND queue_name = ${this.queueName}
|
|
140
|
+
AND available_at <= now()
|
|
141
|
+
AND (
|
|
142
|
+
status = 'pending'
|
|
143
|
+
OR (status = 'claimed' AND lease_expires_at <= now() AND attempt < max_attempts)
|
|
144
|
+
)
|
|
145
|
+
ORDER BY created_at ASC, item_id ASC
|
|
146
|
+
FOR UPDATE SKIP LOCKED
|
|
147
|
+
LIMIT 1
|
|
148
|
+
),
|
|
149
|
+
updated AS (
|
|
150
|
+
UPDATE construct_queue_items q
|
|
151
|
+
SET status = 'claimed',
|
|
152
|
+
attempt = q.attempt + 1,
|
|
153
|
+
claimed_by = ${claimedBy},
|
|
154
|
+
lease_expires_at = now() + ${leaseInterval(this.sql, leaseSeconds)},
|
|
155
|
+
updated_at = now(),
|
|
156
|
+
payload = jsonb_set(
|
|
157
|
+
jsonb_set(
|
|
158
|
+
jsonb_set(q.payload, '{status}', to_jsonb('claimed'::text), true),
|
|
159
|
+
'{claimedBy}', to_jsonb(${claimedBy}::text), true
|
|
160
|
+
),
|
|
161
|
+
'{claimedAt}', to_jsonb(now()::text), true
|
|
162
|
+
)
|
|
163
|
+
FROM candidate
|
|
164
|
+
WHERE q.project = ${this.project}
|
|
165
|
+
AND q.tenant_id = ${this.tenantId}
|
|
166
|
+
AND q.queue_name = ${this.queueName}
|
|
167
|
+
AND q.item_id = candidate.item_id
|
|
168
|
+
RETURNING q.item_id, q.attempt, q.lease_expires_at, q.payload
|
|
169
|
+
)
|
|
170
|
+
INSERT INTO construct_queue_claims (
|
|
171
|
+
claim_id, project, tenant_id, queue_name, item_id, worker_id, attempt, lease_expires_at
|
|
172
|
+
)
|
|
173
|
+
SELECT ${claimId}, ${this.project}, ${this.tenantId}, ${this.queueName},
|
|
174
|
+
item_id, ${claimedBy}, attempt, lease_expires_at
|
|
175
|
+
FROM updated
|
|
176
|
+
RETURNING item_id, attempt, lease_expires_at,
|
|
177
|
+
(SELECT payload FROM updated WHERE updated.item_id = construct_queue_claims.item_id) AS payload
|
|
178
|
+
`;
|
|
179
|
+
const row = rows[0];
|
|
180
|
+
if (!row) return null;
|
|
181
|
+
return {
|
|
182
|
+
...row.payload,
|
|
183
|
+
id: row.payload?.id || row.item_id,
|
|
184
|
+
status: 'claimed',
|
|
185
|
+
claimedBy,
|
|
186
|
+
attempt: row.attempt,
|
|
187
|
+
leaseExpiresAt: row.lease_expires_at,
|
|
188
|
+
claimId,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async heartbeat(id, { workerId, leaseSeconds = this.leaseSeconds } = {}) {
|
|
193
|
+
if (!id) throw new Error('heartbeat: id is required');
|
|
194
|
+
if (!workerId) throw new Error('heartbeat: workerId is required');
|
|
195
|
+
await this.ensureSchema();
|
|
196
|
+
const rows = await this.sql`
|
|
197
|
+
UPDATE construct_queue_items
|
|
198
|
+
SET lease_expires_at = now() + ${leaseInterval(this.sql, leaseSeconds)},
|
|
199
|
+
updated_at = now()
|
|
200
|
+
WHERE project = ${this.project}
|
|
201
|
+
AND tenant_id = ${this.tenantId}
|
|
202
|
+
AND queue_name = ${this.queueName}
|
|
203
|
+
AND item_id = ${id}
|
|
204
|
+
AND status = 'claimed'
|
|
205
|
+
AND claimed_by = ${workerId}
|
|
206
|
+
AND lease_expires_at > now()
|
|
207
|
+
AND COALESCE((payload->>'cancelRequested')::boolean, false) = false
|
|
208
|
+
RETURNING item_id, lease_expires_at
|
|
209
|
+
`;
|
|
210
|
+
if (!rows[0]) {
|
|
211
|
+
const current = await this.read(id);
|
|
212
|
+
if (current?.cancelRequested) return { id, renewed: false, cancelled: true, reason: current.cancellationReason || null };
|
|
213
|
+
return { id, renewed: false };
|
|
214
|
+
}
|
|
215
|
+
return { id, renewed: true, leaseExpiresAt: rows[0].lease_expires_at };
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async fail(id, { workerId, reason, backoffSeconds = 0 } = {}) {
|
|
219
|
+
if (!id) throw new Error('fail: id is required');
|
|
220
|
+
await this.ensureSchema();
|
|
221
|
+
const rows = await this.sql`
|
|
222
|
+
UPDATE construct_queue_items
|
|
223
|
+
SET status = CASE WHEN attempt >= max_attempts THEN 'dead_letter' ELSE 'pending' END,
|
|
224
|
+
available_at = CASE WHEN attempt >= max_attempts THEN available_at ELSE now() + ${backoffInterval(this.sql, backoffSeconds)} END,
|
|
225
|
+
terminal_reason = ${reason || null},
|
|
226
|
+
claimed_by = NULL,
|
|
227
|
+
lease_expires_at = NULL,
|
|
228
|
+
updated_at = now(),
|
|
229
|
+
payload = jsonb_set(
|
|
230
|
+
jsonb_set(
|
|
231
|
+
jsonb_set(payload, '{status}', to_jsonb(CASE WHEN attempt >= max_attempts THEN 'dead_letter'::text ELSE 'pending'::text END), true),
|
|
232
|
+
'{lastFailureReason}', to_jsonb(${reason || ''}::text), true
|
|
233
|
+
),
|
|
234
|
+
'{failedBy}', to_jsonb(${workerId || ''}::text), true
|
|
235
|
+
)
|
|
236
|
+
WHERE project = ${this.project}
|
|
237
|
+
AND tenant_id = ${this.tenantId}
|
|
238
|
+
AND queue_name = ${this.queueName}
|
|
239
|
+
AND item_id = ${id}
|
|
240
|
+
AND status = 'claimed'
|
|
241
|
+
AND (${workerId || null} IS NULL OR claimed_by = ${workerId || null})
|
|
242
|
+
RETURNING item_id, status, attempt, max_attempts, available_at
|
|
243
|
+
`;
|
|
244
|
+
if (!rows[0]) return null;
|
|
245
|
+
return {
|
|
246
|
+
id,
|
|
247
|
+
status: rows[0].status,
|
|
248
|
+
attempt: rows[0].attempt,
|
|
249
|
+
maxAttempts: rows[0].max_attempts,
|
|
250
|
+
deadLettered: rows[0].status === 'dead_letter',
|
|
251
|
+
availableAt: rows[0].available_at,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async requestCancellation(id, { requestedBy, reason } = {}) {
|
|
256
|
+
if (!id) throw new Error('requestCancellation: id is required');
|
|
257
|
+
await this.ensureSchema();
|
|
258
|
+
const rows = await this.sql`
|
|
259
|
+
UPDATE construct_queue_items
|
|
260
|
+
SET terminal_reason = ${reason || null},
|
|
261
|
+
updated_at = now(),
|
|
262
|
+
payload = jsonb_set(
|
|
263
|
+
jsonb_set(
|
|
264
|
+
jsonb_set(payload, '{cancelRequested}', 'true'::jsonb, true),
|
|
265
|
+
'{cancellationReason}', to_jsonb(${reason || ''}::text), true
|
|
266
|
+
),
|
|
267
|
+
'{cancelRequestedBy}', to_jsonb(${requestedBy || ''}::text), true
|
|
268
|
+
)
|
|
269
|
+
WHERE project = ${this.project}
|
|
270
|
+
AND tenant_id = ${this.tenantId}
|
|
271
|
+
AND queue_name = ${this.queueName}
|
|
272
|
+
AND item_id = ${id}
|
|
273
|
+
AND status IN ('pending', 'claimed', 'awaiting_approval')
|
|
274
|
+
RETURNING item_id
|
|
275
|
+
`;
|
|
276
|
+
return rows[0] ? { id, cancellationRequested: true } : null;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
async markProcessed(id, { processedBy, notes, executionKey } = {}) {
|
|
280
|
+
if (!id) throw new Error('markProcessed: id is required');
|
|
281
|
+
await this.ensureSchema();
|
|
282
|
+
if (executionKey) {
|
|
283
|
+
const current = await this.read(id);
|
|
284
|
+
if (current?.status === 'processed' && current.executionKey === executionKey) {
|
|
285
|
+
return { id, idempotent: true };
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
const rows = await this.sql`
|
|
289
|
+
UPDATE construct_queue_items
|
|
290
|
+
SET status = 'processed',
|
|
291
|
+
processed_at = now(),
|
|
292
|
+
processed_by = ${processedBy || null},
|
|
293
|
+
terminal_reason = ${notes || null},
|
|
294
|
+
claimed_by = NULL,
|
|
295
|
+
lease_expires_at = NULL,
|
|
296
|
+
updated_at = now(),
|
|
297
|
+
payload = jsonb_set(
|
|
298
|
+
jsonb_set(
|
|
299
|
+
jsonb_set(payload, '{status}', to_jsonb('processed'::text), true),
|
|
300
|
+
'{processedAt}', to_jsonb(now()::text), true
|
|
301
|
+
),
|
|
302
|
+
'{executionKey}', to_jsonb(${executionKey || ''}::text), true
|
|
303
|
+
)
|
|
304
|
+
WHERE project = ${this.project}
|
|
305
|
+
AND tenant_id = ${this.tenantId}
|
|
306
|
+
AND queue_name = ${this.queueName}
|
|
307
|
+
AND item_id = ${id}
|
|
308
|
+
AND status <> 'processed'
|
|
309
|
+
RETURNING item_id
|
|
310
|
+
`;
|
|
311
|
+
return rows[0] ? { id } : null;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
async markSkipped(id, { skippedBy, reason } = {}) {
|
|
315
|
+
if (!id) throw new Error('markSkipped: id is required');
|
|
316
|
+
await this.ensureSchema();
|
|
317
|
+
const rows = await this.sql`
|
|
318
|
+
UPDATE construct_queue_items
|
|
319
|
+
SET status = 'skipped',
|
|
320
|
+
skipped_at = now(),
|
|
321
|
+
skipped_by = ${skippedBy || null},
|
|
322
|
+
terminal_reason = ${reason || null},
|
|
323
|
+
claimed_by = NULL,
|
|
324
|
+
lease_expires_at = NULL,
|
|
325
|
+
updated_at = now(),
|
|
326
|
+
payload = jsonb_set(
|
|
327
|
+
jsonb_set(payload, '{status}', to_jsonb('skipped'::text), true),
|
|
328
|
+
'{skippedAt}', to_jsonb(now()::text), true
|
|
329
|
+
)
|
|
330
|
+
WHERE project = ${this.project}
|
|
331
|
+
AND tenant_id = ${this.tenantId}
|
|
332
|
+
AND queue_name = ${this.queueName}
|
|
333
|
+
AND item_id = ${id}
|
|
334
|
+
RETURNING item_id
|
|
335
|
+
`;
|
|
336
|
+
return rows[0] ? { id } : null;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
async reopen(id) {
|
|
340
|
+
if (!id) throw new Error('reopen: id is required');
|
|
341
|
+
await this.ensureSchema();
|
|
342
|
+
const rows = await this.sql`
|
|
343
|
+
UPDATE construct_queue_items
|
|
344
|
+
SET status = 'pending',
|
|
345
|
+
claimed_by = NULL,
|
|
346
|
+
lease_expires_at = NULL,
|
|
347
|
+
processed_at = NULL,
|
|
348
|
+
processed_by = NULL,
|
|
349
|
+
skipped_at = NULL,
|
|
350
|
+
skipped_by = NULL,
|
|
351
|
+
terminal_reason = NULL,
|
|
352
|
+
updated_at = now(),
|
|
353
|
+
payload = jsonb_set(payload, '{status}', to_jsonb('pending'::text), true)
|
|
354
|
+
WHERE project = ${this.project}
|
|
355
|
+
AND tenant_id = ${this.tenantId}
|
|
356
|
+
AND queue_name = ${this.queueName}
|
|
357
|
+
AND item_id = ${id}
|
|
358
|
+
RETURNING item_id
|
|
359
|
+
`;
|
|
360
|
+
return rows[0] ? { id, from: 'postgres' } : null;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async queueStats() {
|
|
364
|
+
await this.ensureSchema();
|
|
365
|
+
const rows = await this.sql`
|
|
366
|
+
SELECT status, COUNT(*)::int AS count
|
|
367
|
+
FROM construct_queue_items
|
|
368
|
+
WHERE project = ${this.project}
|
|
369
|
+
AND tenant_id = ${this.tenantId}
|
|
370
|
+
AND queue_name = ${this.queueName}
|
|
371
|
+
GROUP BY status
|
|
372
|
+
`;
|
|
373
|
+
const byStatus = {
|
|
374
|
+
pending: 0,
|
|
375
|
+
claimed: 0,
|
|
376
|
+
processed: 0,
|
|
377
|
+
skipped: 0,
|
|
378
|
+
awaiting_approval: 0,
|
|
379
|
+
dead_letter: 0,
|
|
380
|
+
};
|
|
381
|
+
for (const row of rows) {
|
|
382
|
+
if (Object.hasOwn(byStatus, row.status)) byStatus[row.status] = Number(row.count || 0);
|
|
383
|
+
}
|
|
384
|
+
return {
|
|
385
|
+
project: this.project,
|
|
386
|
+
tenantId: this.tenantId,
|
|
387
|
+
queueName: this.queueName,
|
|
388
|
+
byStatus,
|
|
389
|
+
pending: byStatus.pending,
|
|
390
|
+
claimed: byStatus.claimed,
|
|
391
|
+
deadLetter: byStatus.dead_letter,
|
|
392
|
+
total: Object.values(byStatus).reduce((sum, n) => sum + n, 0),
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
}
|
package/lib/reflect.mjs
CHANGED
|
@@ -14,6 +14,7 @@ import { KNOWLEDGE_ROOT, KNOWLEDGE_SUBDIRS, inferKnowledgeTarget } from './knowl
|
|
|
14
14
|
import { extractSessionObservation } from './reflect/extractor.mjs';
|
|
15
15
|
import { readContextState, contextSummaryLine } from './context-state.mjs';
|
|
16
16
|
import { configDir } from './config/xdg.mjs';
|
|
17
|
+
import { isMainModule } from './roots.mjs';
|
|
17
18
|
|
|
18
19
|
const HOME = process.env.HOME || process.env.USERPROFILE;
|
|
19
20
|
const USER_ENV_PATH = join(configDir(HOME), 'config.env');
|
|
@@ -253,7 +254,7 @@ function parseTranscript(path) {
|
|
|
253
254
|
/**
|
|
254
255
|
* CLI entry point for direct execution.
|
|
255
256
|
*/
|
|
256
|
-
if (import.meta.url
|
|
257
|
+
if (isMainModule(import.meta.url)) {
|
|
257
258
|
const args = process.argv.slice(2);
|
|
258
259
|
runReflectCli(args).catch((err) => {
|
|
259
260
|
console.error(`Error: ${err.message}`);
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* lib/registry/assemble.mjs — Runtime merge of modular org files into registry v3.
|
|
3
3
|
*
|
|
4
|
-
* Walks specialists/org/{groups,teams,specialists,contracts,policies},
|
|
5
|
-
*
|
|
4
|
+
* Walks specialists/org/{groups,teams,specialists,contracts,policies}, then
|
|
5
|
+
* merges two user/project extension tiers on top, builtin -> user -> project
|
|
6
|
+
* (ADR-0052's precedence model, applied here to the org registry): a home-level
|
|
7
|
+
* custom org tree (~/.construct/org/**, construct-rf26.13 — a user's personal
|
|
8
|
+
* custom specialists/teams shared across all their projects), then the existing
|
|
9
|
+
* .cx/org project overlay (highest precedence). Later tiers win on id collision.
|
|
6
10
|
*/
|
|
7
11
|
|
|
8
12
|
import fs from 'node:fs';
|
|
9
13
|
import path from 'node:path';
|
|
14
|
+
import { homeDir } from '../paths.mjs';
|
|
15
|
+
|
|
16
|
+
function customOrgRoot() {
|
|
17
|
+
return path.join(homeDir(), '.construct', 'org');
|
|
18
|
+
}
|
|
10
19
|
|
|
11
20
|
function readJsonDir(dir) {
|
|
12
21
|
const out = {};
|
|
@@ -66,6 +75,8 @@ export function orgDirMtime(rootDir) {
|
|
|
66
75
|
}
|
|
67
76
|
};
|
|
68
77
|
walk(orgRoot);
|
|
78
|
+
const homeOrg = customOrgRoot();
|
|
79
|
+
if (fs.existsSync(homeOrg)) walk(homeOrg);
|
|
69
80
|
const overlay = path.join(rootDir, '.cx', 'org');
|
|
70
81
|
if (fs.existsSync(overlay)) walk(overlay);
|
|
71
82
|
return max;
|
|
@@ -85,6 +96,21 @@ export function assembleRegistry(rootDir) {
|
|
|
85
96
|
const contracts = readJsonDir(path.join(orgRoot, 'contracts'));
|
|
86
97
|
const policies = readJsonDir(path.join(orgRoot, 'policies'));
|
|
87
98
|
|
|
99
|
+
const homeOrgRoot = customOrgRoot();
|
|
100
|
+
if (fs.existsSync(homeOrgRoot)) {
|
|
101
|
+
for (const [id, val] of Object.entries(readJsonDir(path.join(homeOrgRoot, 'groups')))) {
|
|
102
|
+
groups[id] = { ...groups[id], ...val };
|
|
103
|
+
}
|
|
104
|
+
for (const [id, val] of Object.entries(readJsonDir(path.join(homeOrgRoot, 'teams')))) {
|
|
105
|
+
squads[id] = { ...squads[id], ...val };
|
|
106
|
+
}
|
|
107
|
+
for (const [id, val] of Object.entries(readJsonDir(path.join(homeOrgRoot, 'specialists')))) {
|
|
108
|
+
specialists[id] = { ...specialists[id], ...val };
|
|
109
|
+
}
|
|
110
|
+
mergeOverlaySection(contracts, readJsonDir(path.join(homeOrgRoot, 'contracts')));
|
|
111
|
+
mergeOverlaySection(policies, readJsonDir(path.join(homeOrgRoot, 'policies')));
|
|
112
|
+
}
|
|
113
|
+
|
|
88
114
|
if (fs.existsSync(overlayRoot)) {
|
|
89
115
|
for (const [id, val] of Object.entries(readJsonDir(path.join(overlayRoot, 'groups')))) {
|
|
90
116
|
groups[id] = { ...groups[id], ...val };
|
package/lib/registry/catalog.mjs
CHANGED
|
@@ -71,6 +71,11 @@ function buildCapabilityEdges(cap, { allScripts, pkgScriptsBody }) {
|
|
|
71
71
|
return { cliCommands, npmScripts, workflows };
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
function semanticDigest(cap) {
|
|
75
|
+
const { description = '', criticality = '', verification = {} } = cap;
|
|
76
|
+
return JSON.stringify({ description, criticality, verification });
|
|
77
|
+
}
|
|
78
|
+
|
|
74
79
|
export function buildCatalogSnapshot({ rootDir = REPO_ROOT, now = new Date() } = {}) {
|
|
75
80
|
const pkgPath = path.join(rootDir, 'package.json');
|
|
76
81
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
@@ -79,6 +84,7 @@ export function buildCatalogSnapshot({ rootDir = REPO_ROOT, now = new Date() } =
|
|
|
79
84
|
const raw = loadCapabilityRegistry({ rootDir });
|
|
80
85
|
const capabilities = (raw.capabilities ?? []).map((cap) => ({
|
|
81
86
|
...cap,
|
|
87
|
+
_semanticDigest: semanticDigest(cap),
|
|
82
88
|
edges: buildCapabilityEdges(cap, { allScripts, pkgScriptsBody }),
|
|
83
89
|
}));
|
|
84
90
|
|
|
@@ -9,6 +9,7 @@ import fs from 'node:fs';
|
|
|
9
9
|
import path from 'node:path';
|
|
10
10
|
import { fileURLToPath } from 'node:url';
|
|
11
11
|
import { loadRegistry } from '../registry/loader.mjs';
|
|
12
|
+
import { bindingForSpecialist } from '../roles/flavor-bindings.mjs';
|
|
12
13
|
|
|
13
14
|
const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
14
15
|
const REPO_ROOT = path.resolve(MODULE_DIR, '..', '..');
|
|
@@ -81,12 +82,18 @@ export function triageBoundOrphans({ rootDir = REPO_ROOT } = {}) {
|
|
|
81
82
|
} else if (base && profileRoles.has(base)) {
|
|
82
83
|
category = 'B-composer';
|
|
83
84
|
recommendation = `Profile role "${base}" — may load via profile overlay; verify before merge`;
|
|
85
|
+
} else if (base && bindingForSpecialist(base)?.specialistId) {
|
|
86
|
+
// Base name doesn't match a specialist's own name, but
|
|
87
|
+
// lib/roles/flavor-bindings.mjs still routes it onto a live anchor —
|
|
88
|
+
// reachable via prompt-composer under that anchor's identity.
|
|
89
|
+
category = 'B-composer';
|
|
90
|
+
recommendation = `Role flavor bound to ${bindingForSpecialist(base).specialistId} via lib/roles/flavor-bindings.mjs — reachable via prompt-composer; document in registry or bind explicitly`;
|
|
84
91
|
} else if (skill.startsWith('roles/')) {
|
|
85
92
|
category = 'C-merge';
|
|
86
93
|
recommendation = 'Consider merging into parent roles/<base>.md or binding to specialist';
|
|
87
94
|
} else if (skill.startsWith('docs/')) {
|
|
88
95
|
category = 'A-bind';
|
|
89
|
-
recommendation = 'Bind to cx-
|
|
96
|
+
recommendation = 'Bind to cx-operations or domain owner specialist';
|
|
90
97
|
}
|
|
91
98
|
|
|
92
99
|
items.push({ skill, category, recommendation });
|