@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
|
@@ -55,21 +55,23 @@
|
|
|
55
55
|
#let construct-font-display = ("Space Grotesk",)
|
|
56
56
|
#let construct-font-mono = ("JetBrains Mono",)
|
|
57
57
|
|
|
58
|
-
#let horizontalrule = block(width: 100%, above:
|
|
58
|
+
#let horizontalrule = block(width: 100%, above: 1.3em, below: 1.3em)[
|
|
59
59
|
#line(length: 100%, stroke: 0.6pt + hairline-strong)
|
|
60
60
|
]
|
|
61
61
|
|
|
62
|
-
// Type scale. One modular ramp
|
|
63
|
-
// drifts between a PRD, a research brief, and a
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
#let fs-
|
|
68
|
-
#let fs-
|
|
69
|
-
#let fs-
|
|
70
|
-
#let fs-
|
|
71
|
-
#let fs-
|
|
72
|
-
#let fs-
|
|
62
|
+
// Type scale. One modular ramp (~1.2 steps off a 10.5pt body) shared by every
|
|
63
|
+
// document type so sizing never drifts between a PRD, a research brief, and a
|
|
64
|
+
// decision record. Every step must be visibly distinct from its neighbours at
|
|
65
|
+
// reading distance — heading levels may not differ by weight alone.
|
|
66
|
+
|
|
67
|
+
#let fs-micro = 8pt // running header/footer, figure captions
|
|
68
|
+
#let fs-small = 8.5pt // table headers, bylines
|
|
69
|
+
#let fs-meta = 9pt // table body, block code
|
|
70
|
+
#let fs-body = 10.5pt // body prose
|
|
71
|
+
#let fs-h4 = 9pt // uppercase kicker heading
|
|
72
|
+
#let fs-h3 = 12pt // subsection heading
|
|
73
|
+
#let fs-h2 = 14pt // section heading
|
|
74
|
+
#let fs-h1 = 16.5pt // part heading (the document title belongs to the masthead)
|
|
73
75
|
#let fs-subtitle = 11.5pt
|
|
74
76
|
#let fs-title = 24pt
|
|
75
77
|
|
|
@@ -80,9 +82,25 @@
|
|
|
80
82
|
#let wt-semibold = 600
|
|
81
83
|
#let wt-bold = 700
|
|
82
84
|
|
|
85
|
+
// Shared page geometry. Layout templates import these instead of hardcoding
|
|
86
|
+
// margins so the document classes can never drift apart (they already had,
|
|
87
|
+
// before these tokens existed).
|
|
88
|
+
|
|
89
|
+
#let construct-page-paper = "a4"
|
|
90
|
+
#let construct-page-margin = (x: 2.15cm, top: 2cm, bottom: 2.4cm)
|
|
91
|
+
|
|
83
92
|
#let construct-figure-max-width = 92%
|
|
84
93
|
#let construct-figure-max-height = 3.4in
|
|
85
94
|
|
|
95
|
+
// Pandoc's typst writer encodes smart punctuation as markup ("---", "--", "..."),
|
|
96
|
+
// but template variables land inside Typst *strings*, where markup is never
|
|
97
|
+
// evaluated — without this a subtitle prints a literal "---". Normalize any
|
|
98
|
+
// metadata string before it reaches the page.
|
|
99
|
+
|
|
100
|
+
#let construct-smart-text(s) = {
|
|
101
|
+
str(s).replace("---", "\u{2014}").replace("--", "\u{2013}").replace("...", "\u{2026}")
|
|
102
|
+
}
|
|
103
|
+
|
|
86
104
|
// A document-type word maps to a compact badge so the masthead reads as a
|
|
87
105
|
// labelled artifact rather than a bare title.
|
|
88
106
|
|
|
@@ -185,24 +203,28 @@
|
|
|
185
203
|
)
|
|
186
204
|
if chips != none {
|
|
187
205
|
chips
|
|
188
|
-
v(0.
|
|
206
|
+
v(0.7em)
|
|
189
207
|
}
|
|
190
|
-
|
|
208
|
+
|
|
209
|
+
// Display sizes need display leading: the body's paragraph leading applied at
|
|
210
|
+
// 26pt reads as a gap, not a title, so the title paragraph pins its own.
|
|
211
|
+
|
|
212
|
+
par(leading: 0.42em, text(font: construct-font-display, size: fs-title, weight: wt-semibold, fill: ink, tracking: -0.02em)[#construct-smart-text(title)])
|
|
191
213
|
if subtitle != "" {
|
|
192
|
-
v(0.
|
|
193
|
-
text(font: construct-font-sans, size: fs-subtitle, fill: ink-muted, tracking: -0.005em)[#subtitle]
|
|
214
|
+
v(0.55em)
|
|
215
|
+
par(leading: 0.5em, text(font: construct-font-sans, size: fs-subtitle, fill: ink-muted, tracking: -0.005em)[#construct-smart-text(subtitle)])
|
|
194
216
|
}
|
|
195
217
|
let byline = construct-editorial-line(status, owner, date)
|
|
196
218
|
if byline != none {
|
|
197
|
-
v(0.
|
|
219
|
+
v(0.95em)
|
|
198
220
|
byline
|
|
199
221
|
}
|
|
200
|
-
v(
|
|
222
|
+
v(1.15em)
|
|
201
223
|
block(width: 100%, height: 2pt, {
|
|
202
224
|
place(left + horizon, line(length: 100%, stroke: 0.5pt + hairline))
|
|
203
225
|
place(left + horizon, line(length: 46pt, stroke: 2pt + ink))
|
|
204
226
|
})
|
|
205
|
-
v(1.
|
|
227
|
+
v(1.5em)
|
|
206
228
|
}
|
|
207
229
|
|
|
208
230
|
// Shared callout shell: a grey panel with a black left bar and a small bold
|
|
@@ -216,6 +238,8 @@
|
|
|
216
238
|
inset: (left: 14pt, top: 11pt, bottom: 11pt, right: 13pt),
|
|
217
239
|
width: 100%,
|
|
218
240
|
radius: (right: 3pt),
|
|
241
|
+
above: 1.05em,
|
|
242
|
+
below: 1.05em,
|
|
219
243
|
)[
|
|
220
244
|
#set par(leading: 0.66em, spacing: 0.72em)
|
|
221
245
|
#set text(font: construct-font-sans, size: fs-body, fill: ink-body)
|
|
@@ -223,7 +247,6 @@
|
|
|
223
247
|
#v(0.42em)
|
|
224
248
|
#body
|
|
225
249
|
]
|
|
226
|
-
v(0.85em)
|
|
227
250
|
}
|
|
228
251
|
|
|
229
252
|
#let construct-at-a-glance(body) = construct-callout("At a glance", body, tint: surface-alt)
|
|
@@ -242,12 +265,13 @@
|
|
|
242
265
|
inset: 13pt,
|
|
243
266
|
width: 100%,
|
|
244
267
|
radius: (bottom: 3pt),
|
|
268
|
+
above: 1.05em,
|
|
269
|
+
below: 1.05em,
|
|
245
270
|
)[
|
|
246
271
|
#text(font: construct-font-sans, size: 7.5pt, weight: wt-bold, fill: ink, tracking: 0.09em)[KEY METRICS]
|
|
247
272
|
#v(0.48em)
|
|
248
273
|
#body
|
|
249
274
|
]
|
|
250
|
-
v(0.85em)
|
|
251
275
|
}
|
|
252
276
|
|
|
253
277
|
// Running header appears from page two: short title at left, doc id and page at
|
|
@@ -256,7 +280,11 @@
|
|
|
256
280
|
#let construct-running-header(title, doc-id: "", version: "") = context {
|
|
257
281
|
if counter(page).get().first() > 1 [
|
|
258
282
|
#set text(font: construct-font-sans, size: fs-micro, fill: ink-muted)
|
|
259
|
-
|
|
283
|
+
// Truncate on grapheme clusters, not bytes: slice() panics mid-codepoint on
|
|
284
|
+
// multibyte characters (em dashes, accents) in long titles.
|
|
285
|
+
#let clean-title = construct-smart-text(title)
|
|
286
|
+
#let glyphs = clean-title.clusters()
|
|
287
|
+
#let short-title = if glyphs.len() > 52 { glyphs.slice(0, 49).join("") + "…" } else { clean-title }
|
|
260
288
|
#text(fill: ink-muted)[#short-title]
|
|
261
289
|
#h(1fr)
|
|
262
290
|
#if doc-id != "" [
|
|
@@ -273,19 +301,34 @@
|
|
|
273
301
|
]
|
|
274
302
|
}
|
|
275
303
|
|
|
304
|
+
// Running footer: a quiet wordmark line, not a second headline. The header
|
|
305
|
+
// already carries the strong ink; the footer stays in the muted register so
|
|
306
|
+
// page chrome frames the content instead of competing with it.
|
|
307
|
+
|
|
276
308
|
#let construct-running-footer(footer-label, classification: "") = context {
|
|
277
309
|
if counter(page).get().first() > 1 [
|
|
278
310
|
#v(0.3em)
|
|
279
311
|
#line(length: 100%, stroke: 0.4pt + hairline)
|
|
280
|
-
#v(0.
|
|
312
|
+
#v(0.32em)
|
|
281
313
|
#set text(font: construct-font-sans, size: fs-micro, fill: ink-faint)
|
|
282
314
|
#align(center)[
|
|
283
|
-
#text(weight: wt-
|
|
284
|
-
#
|
|
315
|
+
#text(weight: wt-semibold, fill: ink-muted, tracking: 0.16em)[CONSTRUCT]
|
|
316
|
+
#text(fill: ink-faint)[ #sym.dot.c ]
|
|
317
|
+
#text(fill: ink-muted)[#footer-label]
|
|
318
|
+
#if classification != "" [ #text(fill: ink-faint)[ #sym.dot.c ] #upper(classification)]
|
|
285
319
|
]
|
|
286
320
|
]
|
|
287
321
|
}
|
|
288
322
|
|
|
323
|
+
// Pandoc's typst writer has emitted two different calls for a markdown
|
|
324
|
+
// blockquote across versions: older releases call a bare `blockquote(...)`
|
|
325
|
+
// (relying on their own default template to define it, which ours does not),
|
|
326
|
+
// newer releases call Typst's native `quote(block: true, ...)` (handled by the
|
|
327
|
+
// `show quote:` rule below). Defining `blockquote` as a forward to `quote` keeps
|
|
328
|
+
// both pandoc generations rendering through the same show rule.
|
|
329
|
+
|
|
330
|
+
#let blockquote(body) = quote(block: true, body)
|
|
331
|
+
|
|
289
332
|
// The theme function carries every body-level #set/#show rule. Module-scope set
|
|
290
333
|
// rules do NOT propagate through `#import`, so each layout template applies this
|
|
291
334
|
// with `#show: construct-theme` to wrap the Pandoc $body$ in the styled scope.
|
|
@@ -303,8 +346,14 @@
|
|
|
303
346
|
// preserve Typst's native counter context or every item will render as `1.`.
|
|
304
347
|
// Spacing belongs on the list container, not on a wrapped enum.item block.
|
|
305
348
|
|
|
306
|
-
|
|
307
|
-
|
|
349
|
+
// Indents are em-based so they track the type size, and identical between
|
|
350
|
+
// bullets and numbers so both list kinds share one text column. Item gaps
|
|
351
|
+
// (1em) sit between the in-item line leading (0.9em) and the paragraph gap
|
|
352
|
+
// (1.24em): wrapped lines, items, and blocks stay three distinct rhythms.
|
|
353
|
+
|
|
354
|
+
set list(marker: (text(fill: ink)[•], text(fill: ink-muted)[‣], text(fill: ink-faint)[–]), indent: 0.25em, body-indent: 0.65em, spacing: 1em)
|
|
355
|
+
set enum(numbering: "1.", indent: 0.25em, body-indent: 0.65em, spacing: 1em)
|
|
356
|
+
set terms(hanging-indent: 0.9em, spacing: 1em)
|
|
308
357
|
|
|
309
358
|
// Links carry ink with a hairline underline so citations stay legible without
|
|
310
359
|
// introducing color.
|
|
@@ -317,36 +366,38 @@
|
|
|
317
366
|
// Headings. Level 2 is the working section header (the H1 title is rendered by
|
|
318
367
|
// the masthead and stripped from the body), so it carries a short black rule.
|
|
319
368
|
|
|
369
|
+
// Heading rhythm: space above a heading is always noticeably larger than the
|
|
370
|
+
// space below it, so each heading binds to the section it opens rather than
|
|
371
|
+
// floating between two blocks. Above:below stays near 2:1 at every level.
|
|
372
|
+
// Boundary spacing lives on `block(above:, below:)` — a trailing weak v()
|
|
373
|
+
// inside the block sits at a block boundary, where Typst trims it to zero,
|
|
374
|
+
// so the gap under a heading silently never rendered.
|
|
375
|
+
|
|
320
376
|
show heading.where(level: 1): it => {
|
|
321
|
-
|
|
322
|
-
|
|
377
|
+
block(sticky: true, above: 2em, below: 0.9em, {
|
|
378
|
+
set par(leading: 0.45em)
|
|
323
379
|
text(font: construct-font-display, size: fs-h1, weight: wt-semibold, fill: ink, tracking: -0.015em)[#it.body]
|
|
324
|
-
v(0.
|
|
380
|
+
v(0.3em)
|
|
325
381
|
line(length: 100%, stroke: 0.5pt + hairline)
|
|
326
|
-
v(0.7em, weak: true)
|
|
327
382
|
})
|
|
328
383
|
}
|
|
329
384
|
show heading.where(level: 2): it => {
|
|
330
|
-
|
|
331
|
-
|
|
385
|
+
block(sticky: true, above: 2.6em, below: 0.95em, {
|
|
386
|
+
set par(leading: 0.45em)
|
|
332
387
|
text(font: construct-font-display, size: fs-h2, weight: wt-semibold, fill: ink, tracking: -0.01em)[#it.body]
|
|
333
|
-
v(0.
|
|
388
|
+
v(0.42em)
|
|
334
389
|
line(length: 26pt, stroke: 1.5pt + ink)
|
|
335
|
-
v(0.78em, weak: true)
|
|
336
390
|
})
|
|
337
391
|
}
|
|
338
392
|
show heading.where(level: 3): it => {
|
|
339
|
-
|
|
340
|
-
|
|
393
|
+
block(sticky: true, above: 1.8em, below: 0.9em, {
|
|
394
|
+
set par(leading: 0.45em)
|
|
341
395
|
text(font: construct-font-sans, size: fs-h3, weight: wt-semibold, fill: ink-strong)[#it.body]
|
|
342
|
-
v(0.48em, weak: true)
|
|
343
396
|
})
|
|
344
397
|
}
|
|
345
398
|
show heading.where(level: 4): it => {
|
|
346
|
-
|
|
347
|
-
block(sticky: true, {
|
|
399
|
+
block(sticky: true, above: 1.2em, below: 0.35em, {
|
|
348
400
|
text(font: construct-font-sans, size: fs-h4, weight: wt-bold, fill: ink-muted, tracking: 0.07em)[#upper(it.body)]
|
|
349
|
-
v(0.22em, weak: true)
|
|
350
401
|
})
|
|
351
402
|
}
|
|
352
403
|
|
|
@@ -365,6 +416,8 @@
|
|
|
365
416
|
inset: 11pt,
|
|
366
417
|
radius: 4pt,
|
|
367
418
|
width: 100%,
|
|
419
|
+
above: 1.24em,
|
|
420
|
+
below: 1.24em,
|
|
368
421
|
)[#text(font: construct-font-mono, size: fs-meta, fill: ink-body)[#it]]
|
|
369
422
|
|
|
370
423
|
// Tables use a horizontal-only rule system: a strong black line under the bold
|
|
@@ -396,43 +449,54 @@
|
|
|
396
449
|
|
|
397
450
|
// Pandoc wraps markdown tables in a centered figure; lift the table out and
|
|
398
451
|
// left-align it so it reads as a clean data block aligned to the text column.
|
|
452
|
+
// The lift must re-supply the vertical rhythm the figure wrapper carried —
|
|
453
|
+
// without it a table sits flush against the heading above and prose below.
|
|
454
|
+
// Figures are unbreakable blocks even when a show rule replaces their body,
|
|
455
|
+
// so without the `set block(breakable: true)` a long table jumps whole to
|
|
456
|
+
// the next page and strands half a page of dead space behind it.
|
|
399
457
|
|
|
458
|
+
show figure.where(kind: table): set block(breakable: true)
|
|
400
459
|
show figure.where(kind: table): it => {
|
|
401
|
-
if it.body.has("body") {
|
|
460
|
+
let content = if it.body.has("body") { it.body.body } else { it.body }
|
|
461
|
+
block(above: 1.2em, below: 1.35em, width: 100%, align(left, content))
|
|
402
462
|
}
|
|
403
463
|
|
|
404
|
-
// Figures: a light bordered frame, a
|
|
405
|
-
//
|
|
464
|
+
// Figures: a light bordered frame, a caption that leads with a bold figure
|
|
465
|
+
// number, and measured scaling instead of a forced box. Fixing both width and
|
|
466
|
+
// height with fit:contain letterboxed every image — a wide diagram reserved
|
|
467
|
+
// the full max height and floated tiny inside dead whitespace. Measure the
|
|
468
|
+
// natural size, scale to fit width and the height cap, never upscale, and let
|
|
469
|
+
// the frame hug the result.
|
|
406
470
|
|
|
407
471
|
set figure(supplement: "Figure")
|
|
408
|
-
show figure.where(kind: image): it => {
|
|
409
|
-
v(0.95em, weak: true)
|
|
472
|
+
show figure.where(kind: image): it => block(above: 1.2em, below: 1.2em, breakable: false, {
|
|
410
473
|
block(
|
|
411
474
|
stroke: 0.5pt + hairline,
|
|
412
475
|
fill: white,
|
|
413
|
-
inset:
|
|
476
|
+
inset: 10pt,
|
|
414
477
|
width: 100%,
|
|
415
478
|
radius: 4pt,
|
|
416
479
|
)[
|
|
417
|
-
#align(center
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
480
|
+
#align(center, layout(avail => {
|
|
481
|
+
let natural = measure(it.body)
|
|
482
|
+
if natural.width == 0pt or natural.height == 0pt { it.body } else {
|
|
483
|
+
let f = calc.min(
|
|
484
|
+
(avail.width * construct-figure-max-width) / natural.width,
|
|
485
|
+
construct-figure-max-height / natural.height,
|
|
486
|
+
1,
|
|
487
|
+
)
|
|
488
|
+
scale(x: f * 100%, y: f * 100%, reflow: true, it.body)
|
|
489
|
+
}
|
|
490
|
+
}))
|
|
426
491
|
]
|
|
427
492
|
if it.caption != none {
|
|
428
|
-
v(0.
|
|
493
|
+
v(0.4em)
|
|
429
494
|
align(center)[
|
|
430
495
|
#text(font: construct-font-sans, size: fs-micro, weight: wt-bold, fill: ink, tracking: 0.03em)[#it.supplement #it.counter.display()]
|
|
431
496
|
#text(font: construct-font-sans, size: fs-micro, fill: ink-muted)[ — #it.caption.body]
|
|
432
497
|
]
|
|
433
498
|
}
|
|
434
|
-
|
|
435
|
-
}
|
|
499
|
+
})
|
|
436
500
|
|
|
437
501
|
// Blockquotes route to callouts: a quote carrying a Metric/Baseline table
|
|
438
502
|
// becomes the key-metrics card, anything else becomes the at-a-glance aside.
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* templates/distribution/construct-decision.typ —
|
|
2
|
+
* templates/distribution/construct-decision.typ — layout for decision-class
|
|
3
|
+
* artifacts (adr, rfc, rfc-platform, security-audit-report). Same brand system
|
|
4
|
+
* as every other class; only the footer label differs. Metadata contract
|
|
5
|
+
* documented in construct-pdf.typ.
|
|
3
6
|
*/
|
|
4
7
|
|
|
5
8
|
#import "construct-brand.typ": *
|
|
@@ -7,8 +10,8 @@
|
|
|
7
10
|
#show: construct-theme
|
|
8
11
|
|
|
9
12
|
#set page(
|
|
10
|
-
paper:
|
|
11
|
-
margin:
|
|
13
|
+
paper: construct-page-paper,
|
|
14
|
+
margin: construct-page-margin,
|
|
12
15
|
numbering: "1",
|
|
13
16
|
header: construct-running-header(
|
|
14
17
|
"$if(title)$$title$$endif$",
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* templates/distribution/construct-pdf.typ —
|
|
2
|
+
* templates/distribution/construct-pdf.typ — fallback layout for any artifact
|
|
3
|
+
* type without a mapped class (see lib/publish-template.mjs ARTIFACT_TEMPLATE_MAP).
|
|
4
|
+
*
|
|
5
|
+
* Metadata contract (Pandoc -M vars, sourced from artifact YAML frontmatter by
|
|
6
|
+
* lib/publish-template.mjs parseArtifactMetadata): title, subtitle, date, status,
|
|
7
|
+
* owner, artifactType, version, docId, classification. All optional; the masthead
|
|
8
|
+
* and running chrome degrade gracefully when a field is absent. Page geometry and
|
|
9
|
+
* all type/spacing tokens come from construct-brand.typ — never hardcode them here.
|
|
3
10
|
*/
|
|
4
11
|
|
|
5
12
|
#import "construct-brand.typ": *
|
|
@@ -7,8 +14,8 @@
|
|
|
7
14
|
#show: construct-theme
|
|
8
15
|
|
|
9
16
|
#set page(
|
|
10
|
-
paper:
|
|
11
|
-
margin:
|
|
17
|
+
paper: construct-page-paper,
|
|
18
|
+
margin: construct-page-margin,
|
|
12
19
|
numbering: "1",
|
|
13
20
|
header: construct-running-header(
|
|
14
21
|
"$if(title)$$title$$endif$",
|
|
@@ -16,7 +23,7 @@
|
|
|
16
23
|
version: "$if(version)$$version$$endif$",
|
|
17
24
|
),
|
|
18
25
|
footer: construct-running-footer(
|
|
19
|
-
"
|
|
26
|
+
"Document",
|
|
20
27
|
classification: "$if(classification)$$classification$$endif$",
|
|
21
28
|
),
|
|
22
29
|
)
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* templates/distribution/construct-prd.typ —
|
|
2
|
+
* templates/distribution/construct-prd.typ — layout for editorial/product-class
|
|
3
|
+
* artifacts (prd, meta-prd, strategy, runbook, memo, one-pager, postmortem, …).
|
|
4
|
+
* Same brand system as every other class; only the footer label differs.
|
|
5
|
+
* Metadata contract documented in construct-pdf.typ.
|
|
3
6
|
*/
|
|
4
7
|
|
|
5
8
|
#import "construct-brand.typ": *
|
|
@@ -7,8 +10,8 @@
|
|
|
7
10
|
#show: construct-theme
|
|
8
11
|
|
|
9
12
|
#set page(
|
|
10
|
-
paper:
|
|
11
|
-
margin:
|
|
13
|
+
paper: construct-page-paper,
|
|
14
|
+
margin: construct-page-margin,
|
|
12
15
|
numbering: "1",
|
|
13
16
|
header: construct-running-header(
|
|
14
17
|
"$if(title)$$title$$endif$",
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* templates/distribution/construct-research.typ —
|
|
2
|
+
* templates/distribution/construct-research.typ — layout for research-class
|
|
3
|
+
* artifacts (research-brief, evidence-brief, signal-brief, research-finding,
|
|
4
|
+
* product-intelligence-report). Same brand system as every other class; only
|
|
5
|
+
* the footer label differs. Metadata contract documented in construct-pdf.typ.
|
|
3
6
|
*/
|
|
4
7
|
|
|
5
8
|
#import "construct-brand.typ": *
|
|
@@ -7,8 +10,8 @@
|
|
|
7
10
|
#show: construct-theme
|
|
8
11
|
|
|
9
12
|
#set page(
|
|
10
|
-
paper:
|
|
11
|
-
margin:
|
|
13
|
+
paper: construct-page-paper,
|
|
14
|
+
margin: construct-page-margin,
|
|
12
15
|
numbering: "1",
|
|
13
16
|
header: construct-running-header(
|
|
14
17
|
"$if(title)$$title$$endif$",
|
|
@@ -16,7 +19,7 @@
|
|
|
16
19
|
version: "$if(version)$$version$$endif$",
|
|
17
20
|
),
|
|
18
21
|
footer: construct-running-footer(
|
|
19
|
-
"Research
|
|
22
|
+
"Research brief",
|
|
20
23
|
classification: "$if(classification)$$classification$$endif$",
|
|
21
24
|
),
|
|
22
25
|
)
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* providers/git/index.mjs — git repo provider.
|
|
3
|
-
*
|
|
4
|
-
* Transport: git CLI (via child_process). No external deps.
|
|
5
|
-
*
|
|
6
|
-
* Capabilities: read, search
|
|
7
|
-
*
|
|
8
|
-
* read refs:
|
|
9
|
-
* "commits" → recent commits (default 20)
|
|
10
|
-
* "commits:<n>" → last n commits
|
|
11
|
-
* "branches" → all branch names
|
|
12
|
-
* "file:<path>" → file contents at HEAD
|
|
13
|
-
* "status" → working tree status
|
|
14
|
-
* "log:<path>" → commit log for a specific path
|
|
15
|
-
*
|
|
16
|
-
* search: grep across tracked files (git grep)
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import { spawnSync } from 'node:child_process';
|
|
20
|
-
import path from 'node:path';
|
|
21
|
-
import { AuthError, NotFoundError } from '../../errors.mjs';
|
|
22
|
-
|
|
23
|
-
function git(args, cwd) {
|
|
24
|
-
const result = spawnSync('git', args, { cwd, encoding: 'utf8' });
|
|
25
|
-
if (result.error) throw new Error(`git spawn error: ${result.error.message}`);
|
|
26
|
-
return { stdout: result.stdout ?? '', stderr: result.stderr ?? '', status: result.status };
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export default {
|
|
30
|
-
name: 'git',
|
|
31
|
-
capabilities: ['read', 'search'],
|
|
32
|
-
|
|
33
|
-
_cwd: null,
|
|
34
|
-
|
|
35
|
-
async init(config = {}) {
|
|
36
|
-
const cwd = config.cwd ?? process.cwd();
|
|
37
|
-
// Verify this is actually a git repo
|
|
38
|
-
const check = git(['rev-parse', '--git-dir'], cwd);
|
|
39
|
-
if (check.status !== 0) {
|
|
40
|
-
throw new AuthError(`Not a git repository: ${cwd}`, { provider: 'git' });
|
|
41
|
-
}
|
|
42
|
-
this._cwd = cwd;
|
|
43
|
-
},
|
|
44
|
-
|
|
45
|
-
async read(ref, _opts = {}) {
|
|
46
|
-
const cwd = this._cwd ?? process.cwd();
|
|
47
|
-
|
|
48
|
-
if (ref === 'branches') {
|
|
49
|
-
const r = git(['branch', '-a', '--format=%(refname:short)'], cwd);
|
|
50
|
-
return r.stdout.trim().split('\n').filter(Boolean).map((name) => ({ type: 'branch', name }));
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (ref === 'status') {
|
|
54
|
-
const r = git(['status', '--porcelain=v1'], cwd);
|
|
55
|
-
return r.stdout.trim().split('\n').filter(Boolean).map((line) => ({
|
|
56
|
-
type: 'status-entry',
|
|
57
|
-
code: line.slice(0, 2).trim(),
|
|
58
|
-
path: line.slice(3),
|
|
59
|
-
}));
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (ref.startsWith('commits') || ref === 'commits') {
|
|
63
|
-
const n = ref.includes(':') ? parseInt(ref.split(':')[1], 10) : 20;
|
|
64
|
-
const r = git(['log', `--max-count=${n}`, '--pretty=format:%H\t%an\t%ae\t%ai\t%s'], cwd);
|
|
65
|
-
return r.stdout.trim().split('\n').filter(Boolean).map((line) => {
|
|
66
|
-
const [hash, author, email, date, ...subjectParts] = line.split('\t');
|
|
67
|
-
return { type: 'commit', hash, author, email, date, subject: subjectParts.join('\t') };
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (ref.startsWith('file:')) {
|
|
72
|
-
const filePath = ref.slice(5);
|
|
73
|
-
const r = git(['show', `HEAD:${filePath}`], cwd);
|
|
74
|
-
if (r.status !== 0) {
|
|
75
|
-
throw new NotFoundError(`File not found in HEAD: ${filePath}`, { provider: 'git' });
|
|
76
|
-
}
|
|
77
|
-
return [{ type: 'file', path: filePath, content: r.stdout }];
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (ref.startsWith('log:')) {
|
|
81
|
-
const filePath = ref.slice(4);
|
|
82
|
-
const r = git(['log', '--follow', '--pretty=format:%H\t%an\t%ai\t%s', '--', filePath], cwd);
|
|
83
|
-
return r.stdout.trim().split('\n').filter(Boolean).map((line) => {
|
|
84
|
-
const [hash, author, date, ...subjectParts] = line.split('\t');
|
|
85
|
-
return { type: 'commit', hash, author, date, subject: subjectParts.join('\t'), path: filePath };
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
throw new NotFoundError(`Unknown git read ref: "${ref}"`, { provider: 'git' });
|
|
90
|
-
},
|
|
91
|
-
|
|
92
|
-
async search(query, opts = {}) {
|
|
93
|
-
const cwd = this._cwd ?? process.cwd();
|
|
94
|
-
const args = ['grep', '--line-number', '-I'];
|
|
95
|
-
if (opts.ignoreCase) args.push('-i');
|
|
96
|
-
args.push(query);
|
|
97
|
-
if (opts.paths) args.push('--', ...opts.paths);
|
|
98
|
-
const r = git(args, cwd);
|
|
99
|
-
if (r.status === 1 && r.stdout === '') return []; // no matches
|
|
100
|
-
if (r.status !== 0 && r.status !== 1) {
|
|
101
|
-
throw new Error(`git grep failed: ${r.stderr}`);
|
|
102
|
-
}
|
|
103
|
-
return r.stdout.trim().split('\n').filter(Boolean).map((line) => {
|
|
104
|
-
const colonIdx = line.indexOf(':');
|
|
105
|
-
const rest = line.slice(colonIdx + 1);
|
|
106
|
-
const lineNumIdx = rest.indexOf(':');
|
|
107
|
-
return {
|
|
108
|
-
type: 'grep-match',
|
|
109
|
-
file: line.slice(0, colonIdx),
|
|
110
|
-
lineNumber: parseInt(rest.slice(0, lineNumIdx), 10),
|
|
111
|
-
text: rest.slice(lineNumIdx + 1),
|
|
112
|
-
};
|
|
113
|
-
});
|
|
114
|
-
},
|
|
115
|
-
};
|