@devtrack-solution/codesdd 1.2.2 → 1.2.3
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/.sdd/skills/curated/api-clean-flask-langgraph/SKILL.md +17 -17
- package/.sdd/skills/curated/devtrack-api/SKILL.md +160 -28
- package/.sdd/skills/curated/devtrack-api/agents/openai.yaml +1 -1
- package/.sdd/skills/curated/devtrack-api/references/architecture-governance.md +8 -7
- package/.sdd/skills/curated/devtrack-api/references/consumer-sync-policy.md +93 -0
- package/.sdd/skills/curated/devtrack-api/references/contract-pack.yaml +317 -0
- package/.sdd/skills/curated/devtrack-api/references/field-validation-protocol.md +95 -0
- package/.sdd/skills/curated/devtrack-api/references/foundation-layout.md +295 -0
- package/.sdd/skills/curated/devtrack-api/references/implementation-checklist.md +4 -4
- package/.sdd/skills/curated/devtrack-api/references/imports-lint.md +4 -0
- package/.sdd/skills/curated/devtrack-api/references/testing-validation.md +2 -2
- package/LICENSE +1 -1
- package/README.md +243 -51
- package/bin/codesdd.js +3 -2
- package/dist/cli/index.d.ts +2 -2
- package/dist/cli/index.js +11 -558
- package/dist/cli/program.d.ts +14 -0
- package/dist/cli/program.js +645 -0
- package/dist/commands/change.js +5 -5
- package/dist/commands/completion.d.ts +1 -1
- package/dist/commands/completion.js +9 -2
- package/dist/commands/config.js +159 -20
- package/dist/commands/feedback.js +1 -1
- package/dist/commands/schema.d.ts +63 -0
- package/dist/commands/schema.js +12 -12
- package/dist/commands/sdd/backlog.d.ts +3 -0
- package/dist/commands/sdd/backlog.js +54 -0
- package/dist/commands/sdd/execution.js +147 -16
- package/dist/commands/sdd/plugin.d.ts +3 -0
- package/dist/commands/sdd/plugin.js +153 -0
- package/dist/commands/sdd/shared.js +2 -23
- package/dist/commands/sdd/skills.js +7 -0
- package/dist/commands/sdd.js +69 -12
- package/dist/commands/spec.js +9 -9
- package/dist/commands/validate.js +6 -6
- package/dist/commands/workflow/instructions.js +6 -6
- package/dist/commands/workflow/new-change.js +3 -3
- package/dist/commands/workflow/shared.d.ts +1 -1
- package/dist/commands/workflow/shared.js +4 -4
- package/dist/core/archive.js +15 -5
- package/dist/core/artifact-graph/instruction-loader.d.ts +1 -1
- package/dist/core/artifact-graph/instruction-loader.js +3 -3
- package/dist/core/artifact-graph/resolver.d.ts +4 -4
- package/dist/core/artifact-graph/resolver.js +6 -6
- package/dist/core/branding.js +3 -3
- package/dist/core/cli/command-matrix.js +10 -1
- package/dist/core/cli-command-quality.d.ts +27 -0
- package/dist/core/cli-command-quality.js +171 -0
- package/dist/core/command-generation/adapters/costrict.d.ts +1 -1
- package/dist/core/command-generation/adapters/costrict.js +2 -2
- package/dist/core/command-generation/types.d.ts +1 -1
- package/dist/core/completions/command-registry.d.ts +1 -1
- package/dist/core/completions/command-registry.js +155 -12
- package/dist/core/completions/completion-provider.d.ts +14 -1
- package/dist/core/completions/completion-provider.js +29 -1
- package/dist/core/completions/generators/bash-generator.d.ts +1 -1
- package/dist/core/completions/generators/bash-generator.js +20 -12
- package/dist/core/completions/generators/fish-generator.d.ts +9 -1
- package/dist/core/completions/generators/fish-generator.js +39 -25
- package/dist/core/completions/generators/powershell-generator.d.ts +1 -1
- package/dist/core/completions/generators/powershell-generator.js +21 -11
- package/dist/core/completions/generators/zsh-generator.d.ts +3 -6
- package/dist/core/completions/generators/zsh-generator.js +21 -42
- package/dist/core/completions/installers/bash-installer.js +6 -6
- package/dist/core/completions/installers/fish-installer.js +1 -1
- package/dist/core/completions/installers/powershell-installer.js +14 -14
- package/dist/core/completions/installers/zsh-installer.d.ts +7 -1
- package/dist/core/completions/installers/zsh-installer.js +36 -8
- package/dist/core/completions/templates/bash-templates.d.ts +1 -1
- package/dist/core/completions/templates/bash-templates.js +12 -6
- package/dist/core/completions/templates/fish-templates.d.ts +2 -2
- package/dist/core/completions/templates/fish-templates.js +20 -9
- package/dist/core/completions/templates/powershell-templates.d.ts +1 -1
- package/dist/core/completions/templates/powershell-templates.js +13 -4
- package/dist/core/completions/templates/zsh-templates.d.ts +1 -1
- package/dist/core/completions/templates/zsh-templates.js +18 -9
- package/dist/core/config-schema.d.ts +3 -1
- package/dist/core/config-schema.js +26 -1
- package/dist/core/config.d.ts +3 -3
- package/dist/core/config.js +4 -4
- package/dist/core/global-config.d.ts +41 -12
- package/dist/core/global-config.js +344 -27
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +2 -2
- package/dist/core/init.d.ts +6 -1
- package/dist/core/init.js +99 -77
- package/dist/core/legacy-cleanup.d.ts +17 -17
- package/dist/core/legacy-cleanup.js +96 -79
- package/dist/core/list.js +18 -4
- package/dist/core/migration.d.ts +3 -1
- package/dist/core/migration.js +7 -8
- package/dist/core/parsers/change-parser.js +1 -1
- package/dist/core/parsers/markdown-parser.js +2 -2
- package/dist/core/profile-sync-drift.d.ts +1 -1
- package/dist/core/profile-sync-drift.js +13 -13
- package/dist/core/project-config.d.ts +4 -4
- package/dist/core/project-config.js +11 -11
- package/dist/core/schemas/change.schema.d.ts +1 -1
- package/dist/core/schemas/change.schema.js +1 -1
- package/dist/core/schemas/spec.schema.d.ts +1 -1
- package/dist/core/schemas/spec.schema.js +1 -1
- package/dist/core/sdd/adr.js +23 -1
- package/dist/core/sdd/agent-binding.d.ts +346 -0
- package/dist/core/sdd/agent-binding.js +343 -0
- package/dist/core/sdd/backlog-cli.d.ts +16 -0
- package/dist/core/sdd/backlog-cli.js +146 -0
- package/dist/core/sdd/backlog-conflict-policy.d.ts +58 -0
- package/dist/core/sdd/backlog-conflict-policy.js +230 -0
- package/dist/core/sdd/backlog-projection.d.ts +8 -0
- package/dist/core/sdd/backlog-projection.js +89 -0
- package/dist/core/sdd/backlog-provider-contract.d.ts +252 -0
- package/dist/core/sdd/backlog-provider-contract.js +158 -0
- package/dist/core/sdd/bootstrap.js +2 -2
- package/dist/core/sdd/check.d.ts +42 -0
- package/dist/core/sdd/check.js +22 -22
- package/dist/core/sdd/contract.d.ts +13 -0
- package/dist/core/sdd/contract.js +36 -0
- package/dist/core/sdd/coordination/coordination-adapters.d.ts +38 -0
- package/dist/core/sdd/coordination/coordination-adapters.js +139 -1
- package/dist/core/sdd/deepagent-contracts.d.ts +276 -0
- package/dist/core/sdd/deepagent-contracts.js +173 -0
- package/dist/core/sdd/deepagents/adr-governor.d.ts +2 -0
- package/dist/core/sdd/deepagents/adr-governor.js +30 -0
- package/dist/core/sdd/deepagents/backend.d.ts +63 -0
- package/dist/core/sdd/deepagents/backend.js +174 -0
- package/dist/core/sdd/deepagents/codesdd-tools.d.ts +39 -0
- package/dist/core/sdd/deepagents/codesdd-tools.js +83 -0
- package/dist/core/sdd/deepagents/evidence-mapper.d.ts +86 -0
- package/dist/core/sdd/deepagents/evidence-mapper.js +178 -0
- package/dist/core/sdd/deepagents/model-provider.d.ts +53 -0
- package/dist/core/sdd/deepagents/model-provider.js +379 -0
- package/dist/core/sdd/deepagents/policy-enforcement.d.ts +30 -0
- package/dist/core/sdd/deepagents/policy-enforcement.js +90 -0
- package/dist/core/sdd/deepagents/policy.d.ts +75 -0
- package/dist/core/sdd/deepagents/policy.js +358 -0
- package/dist/core/sdd/deepagents/quality-witness.d.ts +3 -0
- package/dist/core/sdd/deepagents/quality-witness.js +77 -0
- package/dist/core/sdd/deepagents/reversa-subagents.d.ts +75 -0
- package/dist/core/sdd/deepagents/reversa-subagents.js +182 -0
- package/dist/core/sdd/deepagents/runtime-factory.d.ts +90 -0
- package/dist/core/sdd/deepagents/runtime-factory.js +231 -0
- package/dist/core/sdd/deepagents/runtime-loader.d.ts +16 -0
- package/dist/core/sdd/deepagents/runtime-loader.js +65 -0
- package/dist/core/sdd/default-bootstrap-files.d.ts +2 -2
- package/dist/core/sdd/default-bootstrap-files.js +36 -2
- package/dist/core/sdd/default-skills.d.ts +30 -0
- package/dist/core/sdd/default-skills.js +181 -5
- package/dist/core/sdd/devtrack-api-appliance.d.ts +84 -0
- package/dist/core/sdd/devtrack-api-appliance.js +257 -0
- package/dist/core/sdd/devtrack-api-architecture.d.ts +31 -0
- package/dist/core/sdd/devtrack-api-architecture.js +608 -0
- package/dist/core/sdd/devtrack-api-import-boundary.d.ts +19 -0
- package/dist/core/sdd/devtrack-api-import-boundary.js +32 -0
- package/dist/core/sdd/diagnose.d.ts +59 -0
- package/dist/core/sdd/diagnose.js +37 -37
- package/dist/core/sdd/docs-sync.js +33 -5
- package/dist/core/sdd/domain/post-active-validation.d.ts +7 -0
- package/dist/core/sdd/domain/post-active-validation.js +61 -0
- package/dist/core/sdd/domain/transition-engine.js +1 -0
- package/dist/core/sdd/entity-reference.d.ts +5 -0
- package/dist/core/sdd/entity-reference.js +22 -0
- package/dist/core/sdd/governance-backfill.d.ts +31 -0
- package/dist/core/sdd/governance-backfill.js +359 -0
- package/dist/core/sdd/governance-parser.d.ts +21 -0
- package/dist/core/sdd/governance-parser.js +91 -0
- package/dist/core/sdd/governance-schemas.d.ts +245 -0
- package/dist/core/sdd/governance-schemas.js +143 -0
- package/dist/core/sdd/{import-openspec.d.ts → import-legacy-spec.d.ts} +7 -7
- package/dist/core/sdd/{import-openspec.js → import-legacy-spec.js} +21 -29
- package/dist/core/sdd/init.d.ts +3 -0
- package/dist/core/sdd/init.js +6 -3
- package/dist/core/sdd/json-schema.js +100 -6
- package/dist/core/sdd/knowledge-graph.d.ts +45 -0
- package/dist/core/sdd/knowledge-graph.js +288 -0
- package/dist/core/sdd/legacy-operations.js +431 -43
- package/dist/core/sdd/lenses.d.ts +1 -0
- package/dist/core/sdd/lenses.js +29 -1
- package/dist/core/sdd/migrate-workspace.js +56 -2
- package/dist/core/sdd/migrate.d.ts +1 -1
- package/dist/core/sdd/migrate.js +36 -2
- package/dist/core/sdd/package-structure-gate.d.ts +83 -0
- package/dist/core/sdd/package-structure-gate.js +362 -0
- package/dist/core/sdd/parallel-feat-automation.d.ts +152 -0
- package/dist/core/sdd/parallel-feat-automation.js +212 -0
- package/dist/core/sdd/plugin-broker.d.ts +558 -0
- package/dist/core/sdd/plugin-broker.js +482 -0
- package/dist/core/sdd/plugin-certification.d.ts +79 -0
- package/dist/core/sdd/plugin-certification.js +453 -0
- package/dist/core/sdd/plugin-cli.d.ts +109 -0
- package/dist/core/sdd/plugin-cli.js +198 -0
- package/dist/core/sdd/plugin-evidence.d.ts +275 -0
- package/dist/core/sdd/plugin-evidence.js +307 -0
- package/dist/core/sdd/plugin-manifest.d.ts +164 -0
- package/dist/core/sdd/plugin-manifest.js +215 -0
- package/dist/core/sdd/plugin-policy-pack.d.ts +88 -0
- package/dist/core/sdd/plugin-policy-pack.js +236 -0
- package/dist/core/sdd/plugin-policy.d.ts +68 -0
- package/dist/core/sdd/plugin-policy.js +212 -0
- package/dist/core/sdd/plugin-registry.d.ts +311 -0
- package/dist/core/sdd/plugin-registry.js +138 -0
- package/dist/core/sdd/plugin-skill-binding.d.ts +151 -0
- package/dist/core/sdd/plugin-skill-binding.js +339 -0
- package/dist/core/sdd/quality-artifact-manifest-validator.d.ts +28 -0
- package/dist/core/sdd/quality-artifact-manifest-validator.js +167 -0
- package/dist/core/sdd/quality-evidence-renderer.d.ts +65 -0
- package/dist/core/sdd/quality-evidence-renderer.js +218 -0
- package/dist/core/sdd/quality-scenario-runner.d.ts +42 -0
- package/dist/core/sdd/quality-scenario-runner.js +613 -0
- package/dist/core/sdd/quality-validation.d.ts +547 -0
- package/dist/core/sdd/quality-validation.js +239 -0
- package/dist/core/sdd/resolve-project-root.d.ts +2 -2
- package/dist/core/sdd/resolve-project-root.js +11 -5
- package/dist/core/sdd/sanitize.d.ts +30 -1
- package/dist/core/sdd/sanitize.js +23 -23
- package/dist/core/sdd/services/agent-run.service.d.ts +65 -0
- package/dist/core/sdd/services/agent-run.service.js +189 -0
- package/dist/core/sdd/services/breakdown.service.js +2 -1
- package/dist/core/sdd/services/context.service.js +18 -16
- package/dist/core/sdd/services/debate.service.js +15 -2
- package/dist/core/sdd/services/feature-lint.service.d.ts +22 -0
- package/dist/core/sdd/services/feature-lint.service.js +105 -5
- package/dist/core/sdd/services/finalize.service.d.ts +80 -0
- package/dist/core/sdd/services/finalize.service.js +323 -24
- package/dist/core/sdd/services/frontend-gap.service.js +22 -7
- package/dist/core/sdd/services/governance-control-plane-runtime-adapters.d.ts +17 -0
- package/dist/core/sdd/services/governance-control-plane-runtime-adapters.js +38 -0
- package/dist/core/sdd/services/governance-control-plane.service.d.ts +66 -0
- package/dist/core/sdd/services/governance-control-plane.service.js +134 -0
- package/dist/core/sdd/services/ingest-deposito.service.js +1 -1
- package/dist/core/sdd/services/legacy-capability.service.d.ts +10 -7
- package/dist/core/sdd/services/legacy-capability.service.js +38 -21
- package/dist/core/sdd/services/mcp-runtime.service.d.ts +123 -8
- package/dist/core/sdd/services/mcp-runtime.service.js +1085 -33
- package/dist/core/sdd/services/onboard.service.js +2 -1
- package/dist/core/sdd/services/rebuild.service.js +6 -1
- package/dist/core/sdd/services/skills-sync.service.d.ts +17 -5
- package/dist/core/sdd/services/skills-sync.service.js +55 -2
- package/dist/core/sdd/services/start.service.js +6 -4
- package/dist/core/sdd/skill-bundles-curation-schema.d.ts +66 -0
- package/dist/core/sdd/skill-bundles-curation-schema.js +52 -0
- package/dist/core/sdd/skill-evidence.d.ts +19 -0
- package/dist/core/sdd/skill-evidence.js +38 -0
- package/dist/core/sdd/skill-policy-pool.d.ts +46 -0
- package/dist/core/sdd/skill-policy-pool.js +185 -0
- package/dist/core/sdd/state.d.ts +22 -0
- package/dist/core/sdd/state.js +66 -41
- package/dist/core/sdd/structural-health.d.ts +42 -42
- package/dist/core/sdd/types.d.ts +33 -7
- package/dist/core/sdd/types.js +17 -0
- package/dist/core/sdd/upgrade-to-codesdd.d.ts +45 -0
- package/dist/core/sdd/upgrade-to-codesdd.js +179 -0
- package/dist/core/sdd/workspace-schemas.d.ts +285 -14
- package/dist/core/sdd/workspace-schemas.js +148 -0
- package/dist/core/sdd/write-manifest.js +22 -4
- package/dist/core/shared/skill-generation.d.ts +1 -1
- package/dist/core/shared/skill-generation.js +15 -15
- package/dist/core/shared/tool-detection.d.ts +3 -3
- package/dist/core/shared/tool-detection.js +14 -14
- package/dist/core/specs-apply.js +6 -6
- package/dist/core/templates/index.d.ts +1 -1
- package/dist/core/templates/index.js +1 -1
- package/dist/core/templates/workflows/apply-change.js +14 -14
- package/dist/core/templates/workflows/archive-change.js +32 -32
- package/dist/core/templates/workflows/bulk-archive-change.js +25 -25
- package/dist/core/templates/workflows/continue-change.js +12 -12
- package/dist/core/templates/workflows/explore.js +29 -29
- package/dist/core/templates/workflows/feedback.js +6 -6
- package/dist/core/templates/workflows/ff-change.js +24 -24
- package/dist/core/templates/workflows/new-change.js +20 -20
- package/dist/core/templates/workflows/onboard.js +33 -33
- package/dist/core/templates/workflows/propose.js +23 -23
- package/dist/core/templates/workflows/sdd.js +8 -8
- package/dist/core/templates/workflows/sync-specs.js +19 -19
- package/dist/core/templates/workflows/verify-change.js +17 -17
- package/dist/core/update.d.ts +2 -2
- package/dist/core/update.js +16 -15
- package/dist/core/validation/constants.d.ts +1 -1
- package/dist/core/validation/constants.js +1 -1
- package/dist/core/view.js +11 -11
- package/dist/telemetry/config.d.ts +2 -1
- package/dist/telemetry/config.js +17 -8
- package/dist/telemetry/index.d.ts +10 -2
- package/dist/telemetry/index.js +40 -7
- package/dist/ui/ascii-patterns.d.ts +2 -2
- package/dist/ui/ascii-patterns.js +2 -2
- package/dist/ui/welcome-screen.js +2 -2
- package/dist/utils/change-metadata.d.ts +4 -4
- package/dist/utils/change-metadata.js +6 -6
- package/dist/utils/change-utils.d.ts +3 -3
- package/dist/utils/change-utils.js +5 -5
- package/dist/utils/file-system.js +1 -1
- package/dist/utils/interactive.js +1 -1
- package/dist/utils/item-discovery.js +4 -4
- package/dist/utils/legacy-spec-compat.d.ts +2 -0
- package/dist/utils/legacy-spec-compat.js +2 -0
- package/dist/utils/shell-detection.d.ts +1 -0
- package/dist/utils/shell-detection.js +16 -0
- package/package.json +27 -17
- package/schemas/sdd/1-spec.schema.json +1 -1
- package/schemas/sdd/2-plan.schema.json +73 -1
- package/schemas/sdd/3-tasks.schema.json +73 -1
- package/schemas/sdd/4-changelog.schema.json +1 -1
- package/schemas/sdd/5-quality.schema.json +442 -2
- package/schemas/sdd/adr.schema.json +148 -0
- package/schemas/sdd/agent-binding-adapter.schema.json +210 -0
- package/schemas/sdd/agent-binding-resolution.schema.json +338 -0
- package/schemas/sdd/backlog-projection-plan.schema.json +180 -0
- package/schemas/sdd/backlog-provider-contract.schema.json +260 -0
- package/schemas/sdd/codesdd-plugin.schema.json +474 -0
- package/schemas/sdd/debate.schema.json +244 -0
- package/schemas/sdd/deepagent-decision-evidence.schema.json +58 -0
- package/schemas/sdd/deepagent-env-contract.schema.json +143 -0
- package/schemas/sdd/deepagent-quality-evidence.schema.json +108 -0
- package/schemas/sdd/deepagent-run-evidence.schema.json +192 -0
- package/schemas/sdd/deepagent-run-plan.schema.json +197 -0
- package/schemas/sdd/deepagent-run-request.schema.json +321 -0
- package/schemas/sdd/deepagent-subagent-evidence.schema.json +110 -0
- package/schemas/sdd/deepagent-tool-call-evidence.schema.json +78 -0
- package/schemas/sdd/discarded.schema.json +127 -0
- package/schemas/sdd/epic.schema.json +147 -0
- package/schemas/sdd/insight.schema.json +136 -0
- package/schemas/sdd/parallel-feat-automation-plan.schema.json +215 -0
- package/schemas/sdd/parallel-feat-automation-request.schema.json +109 -0
- package/schemas/sdd/plugin-artifact-manifest.schema.json +150 -0
- package/schemas/sdd/plugin-compliance-index.schema.json +136 -0
- package/schemas/sdd/plugin-dry-run-plan.schema.json +260 -0
- package/schemas/sdd/plugin-evidence-manifest.schema.json +569 -0
- package/schemas/sdd/plugin-policy-evaluation.schema.json +92 -0
- package/schemas/sdd/plugin-policy-pack-evaluation.schema.json +94 -0
- package/schemas/sdd/plugin-policy-pack.schema.json +196 -0
- package/schemas/sdd/plugin-registry.schema.json +558 -0
- package/schemas/sdd/plugin-rollback-manifest.schema.json +87 -0
- package/schemas/sdd/plugin-runtime-invocation-plan.schema.json +845 -0
- package/schemas/sdd/plugin-skill-binding-resolution.schema.json +305 -0
- package/schemas/sdd/plugin-skill-binding.schema.json +88 -0
- package/schemas/sdd/plugin-validation-manifest.schema.json +123 -0
- package/schemas/sdd/quality-architecture-schema.schema.json +216 -0
- package/schemas/sdd/quality-evidence-bundle.schema.json +1228 -0
- package/schemas/sdd/quality-run.schema.json +197 -0
- package/schemas/sdd/quality-scenario.schema.json +252 -0
- package/schemas/sdd/workspace-catalog.schema.json +9841 -22
- package/schemas/spec-driven/schema.yaml +4 -4
- package/schemas/spec-driven/templates/proposal.md +1 -1
- package/dist/utils/openspec-compat.d.ts +0 -2
- package/dist/utils/openspec-compat.js +0 -2
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { existsSync, promises as fs } from "node:fs";
|
|
3
|
-
import { LENSES, validateDocumentAgainstLens } from "../lenses.js";
|
|
4
3
|
import { adrFileName } from "../adr.js";
|
|
4
|
+
import { evaluateAdrGovernorDocument } from "../deepagents/adr-governor.js";
|
|
5
|
+
import { evaluateQualityWitnessMatrix } from "../deepagents/quality-witness.js";
|
|
5
6
|
import { evaluateFeatureFinalizeGuardrails } from "../domain/lifecycle-guardrails.js";
|
|
6
7
|
import { runLifecycleHooks } from "../domain/lifecycle-hooks.js";
|
|
7
8
|
import { evaluateWorkspaceTraceability } from "../domain/traceability.js";
|
|
8
|
-
import { validatePostActiveFeaturePlacement, validatePreFinalizeArchiveCanonicalization, } from "../domain/post-active-validation.js";
|
|
9
|
+
import { normalizeArchivedFeatureActiveReferences, validatePostActiveFeaturePlacement, validatePreFinalizeArchiveCanonicalization, } from "../domain/post-active-validation.js";
|
|
9
10
|
import { loadStateSnapshot, nowIso, saveStateTransaction } from "../state.js";
|
|
10
11
|
import { syncSddGuideDocs } from "../docs-sync.js";
|
|
11
12
|
import { mergeArchitectureNode, mergeFrontendDecisionRecord, mergeRepoMapRecord, mergeServiceRecord, mergeTechStackRecord, stableUniqueStrings, upsertByKey } from "../merge-catalog.js";
|
|
@@ -13,7 +14,8 @@ import { getRuntime, persistAndRender, relProjectPath, coreDocRef, activeDocName
|
|
|
13
14
|
import { SddWriteTransaction } from "../write-manifest.js";
|
|
14
15
|
import { withStateLock } from "../state-lock.js";
|
|
15
16
|
import { activeWorkspaceDeclaresMandatoryAdrImpact } from "../adr-policy.js";
|
|
16
|
-
import {
|
|
17
|
+
import { normalizeFeatRef } from "../entity-reference.js";
|
|
18
|
+
import { parseWorkspaceYamlDocument, stringifyWorkspaceYamlDocument, } from "../workspace-schemas.js";
|
|
17
19
|
export class FinalizeService {
|
|
18
20
|
stores;
|
|
19
21
|
constructor(stores) {
|
|
@@ -197,6 +199,14 @@ export class FinalizeService {
|
|
|
197
199
|
}
|
|
198
200
|
docWarnings.push(`${feature.id} finalized with --force-transition despite privacy compliance guardrail warnings.`);
|
|
199
201
|
}
|
|
202
|
+
const devtrackApiPolicyGuardrail = await evaluateDevTrackApiPolicyGates(paths, config, feature);
|
|
203
|
+
if (!devtrackApiPolicyGuardrail.ok) {
|
|
204
|
+
docWarnings.push(`${feature.id} devtrack-api policy gate blocked finalize: ${devtrackApiPolicyGuardrail.reasons.join(' | ')}`);
|
|
205
|
+
if (!options?.forceTransition) {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
docWarnings.push(`${feature.id} finalized with --force-transition despite devtrack-api policy gate warnings.`);
|
|
209
|
+
}
|
|
200
210
|
if (!feature.requires_adr && (await activeWorkspaceDeclaresMandatoryAdrImpact(paths, feature))) {
|
|
201
211
|
feature.requires_adr = true;
|
|
202
212
|
}
|
|
@@ -204,20 +214,14 @@ export class FinalizeService {
|
|
|
204
214
|
const requiredAdrPath = path.join(paths.coreDir, 'adrs', adrFileName(feature.id));
|
|
205
215
|
const adrContent = await fs.readFile(requiredAdrPath, 'utf-8').catch(() => '');
|
|
206
216
|
if (!adrContent.trim()) {
|
|
207
|
-
docWarnings.push(`${feature.id}
|
|
208
|
-
|
|
209
|
-
continue;
|
|
210
|
-
}
|
|
211
|
-
docWarnings.push(`${feature.id} finalizada com --force-transition sem ADR obrigatório preenchido.`);
|
|
217
|
+
docWarnings.push(`${feature.id} adr governor blocked finalize: ADR obrigatório ausente: ${relProjectPath(paths, requiredAdrPath)}`);
|
|
218
|
+
continue;
|
|
212
219
|
}
|
|
213
220
|
else {
|
|
214
|
-
const adrViolations =
|
|
221
|
+
const adrViolations = evaluateAdrGovernorDocument(adrContent);
|
|
215
222
|
if (adrViolations.length > 0) {
|
|
216
|
-
docWarnings.push(`${feature.id} ADR obrigatório inválido: ${adrViolations.join(' | ')}`);
|
|
217
|
-
|
|
218
|
-
continue;
|
|
219
|
-
}
|
|
220
|
-
docWarnings.push(`${feature.id} finalizada com --force-transition apesar de violações no ADR obrigatório.`);
|
|
223
|
+
docWarnings.push(`${feature.id} adr governor blocked finalize: ADR obrigatório inválido: ${adrViolations.join(' | ')}`);
|
|
224
|
+
continue;
|
|
221
225
|
}
|
|
222
226
|
}
|
|
223
227
|
}
|
|
@@ -261,6 +265,7 @@ export class FinalizeService {
|
|
|
261
265
|
afterTransition: (currentFeature) => {
|
|
262
266
|
currentFeature.current_stage = 'consolidacao';
|
|
263
267
|
currentFeature.done_at = now;
|
|
268
|
+
currentFeature.archived_at = now;
|
|
264
269
|
currentFeature.last_sync_at = now;
|
|
265
270
|
},
|
|
266
271
|
});
|
|
@@ -395,6 +400,7 @@ export class FinalizeService {
|
|
|
395
400
|
}
|
|
396
401
|
await fs.mkdir(paths.archivedDir, { recursive: true });
|
|
397
402
|
await fs.rename(activeDirPath, archivedDirPath);
|
|
403
|
+
await normalizeArchivedFeatureActiveReferences(paths, feature.id);
|
|
398
404
|
const postActiveValidation = await validatePostActiveFeaturePlacement(paths, feature.id);
|
|
399
405
|
if (!postActiveValidation.ok) {
|
|
400
406
|
if (!(await pathExists(activeDirPath)) && (await pathExists(archivedDirPath))) {
|
|
@@ -458,9 +464,9 @@ export class FinalizeService {
|
|
|
458
464
|
});
|
|
459
465
|
}
|
|
460
466
|
}
|
|
461
|
-
async function inferFinalizeTarget(paths, pending, explicitRef, cwd) {
|
|
467
|
+
export async function inferFinalizeTarget(paths, pending, explicitRef, cwd) {
|
|
462
468
|
if (explicitRef)
|
|
463
|
-
return explicitRef;
|
|
469
|
+
return normalizeFeatRef(explicitRef) ?? explicitRef;
|
|
464
470
|
const activeEntries = await fs.readdir(paths.activeDir, { withFileTypes: true }).catch(() => []);
|
|
465
471
|
const activeFeatures = activeEntries
|
|
466
472
|
.filter((entry) => entry.isDirectory() && /^FEAT-\d{4}$/.test(entry.name))
|
|
@@ -483,13 +489,13 @@ async function inferFinalizeTarget(paths, pending, explicitRef, cwd) {
|
|
|
483
489
|
}
|
|
484
490
|
return undefined;
|
|
485
491
|
}
|
|
486
|
-
function isPrivacyComplianceFeature(feature) {
|
|
492
|
+
export function isPrivacyComplianceFeature(feature) {
|
|
487
493
|
if (feature.origin_ref === 'EPIC-0020' || feature.origin_ref === 'EPIC-0021')
|
|
488
494
|
return true;
|
|
489
495
|
const refs = new Set(feature.acceptance_refs || []);
|
|
490
496
|
return refs.has('EPIC-0020') || refs.has('EPIC-0021') || refs.has('DEB-0021') || refs.has('INS-0021');
|
|
491
497
|
}
|
|
492
|
-
async function evaluatePrivacyFinalizeGuardrail(paths, config, feature) {
|
|
498
|
+
export async function evaluatePrivacyFinalizeGuardrail(paths, config, feature) {
|
|
493
499
|
if (!isPrivacyComplianceFeature(feature)) {
|
|
494
500
|
return { ok: true, reasons: [] };
|
|
495
501
|
}
|
|
@@ -576,7 +582,46 @@ async function evaluatePrivacyFinalizeGuardrail(paths, config, feature) {
|
|
|
576
582
|
}
|
|
577
583
|
return { ok: reasons.length === 0, reasons };
|
|
578
584
|
}
|
|
579
|
-
async function
|
|
585
|
+
export async function evaluateDevTrackApiPolicyGates(paths, config, feature) {
|
|
586
|
+
const activePath = path.join(paths.activeDir, feature.id);
|
|
587
|
+
const names = activeDocNamesForLayout(config);
|
|
588
|
+
const qualityPath = path.join(activePath, names.quality);
|
|
589
|
+
const qualityContent = await fs.readFile(qualityPath, 'utf-8').catch(() => '');
|
|
590
|
+
if (!qualityContent.trim()) {
|
|
591
|
+
return { ok: true, reasons: [] };
|
|
592
|
+
}
|
|
593
|
+
let quality;
|
|
594
|
+
try {
|
|
595
|
+
quality = parseWorkspaceYamlDocument('5-quality.yaml', qualityContent);
|
|
596
|
+
}
|
|
597
|
+
catch {
|
|
598
|
+
return { ok: true, reasons: [] };
|
|
599
|
+
}
|
|
600
|
+
const reasons = evaluateDevTrackApiPolicyEvidence(quality);
|
|
601
|
+
return { ok: reasons.length === 0, reasons };
|
|
602
|
+
}
|
|
603
|
+
export function evaluateDevTrackApiPolicyEvidence(quality) {
|
|
604
|
+
const policyRequirements = [
|
|
605
|
+
...(quality.skill_evidence?.policy_requirements ?? []),
|
|
606
|
+
...(quality.policy_injection?.required_policies ?? []),
|
|
607
|
+
];
|
|
608
|
+
if (policyRequirements.length === 0) {
|
|
609
|
+
return [];
|
|
610
|
+
}
|
|
611
|
+
const evidenceEntries = quality.skill_evidence?.evidence ?? [];
|
|
612
|
+
const hasDevTrackApiEvidence = evidenceEntries.some((entry) => entry.skill_id === 'devtrack-api');
|
|
613
|
+
const reasons = new Set();
|
|
614
|
+
for (const policy of policyRequirements) {
|
|
615
|
+
if (policy.skill_id !== 'devtrack-api' || policy.enforcement !== 'blocking') {
|
|
616
|
+
continue;
|
|
617
|
+
}
|
|
618
|
+
if (!hasDevTrackApiEvidence) {
|
|
619
|
+
reasons.add(`devtrack-api policy gate: blocking skill "devtrack-api" lacks evidence in skill_evidence (required rules: ${policy.required_rule_refs.join(', ')})`);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
return Array.from(reasons);
|
|
623
|
+
}
|
|
624
|
+
export async function evaluateWorkspaceQualityFeedback(paths, config, feature) {
|
|
580
625
|
const activePath = path.join(paths.activeDir, feature.id);
|
|
581
626
|
const names = activeDocNamesForLayout(config);
|
|
582
627
|
const artifactName = [names.quality, ...activeDocCandidateNames(config)].find((name) => name === '5-quality.yaml' && existsSync(path.join(activePath, name))) || '';
|
|
@@ -598,9 +643,13 @@ async function evaluateWorkspaceQualityFeedback(paths, config, feature) {
|
|
|
598
643
|
}
|
|
599
644
|
const missingTargets = missingCoverageTargets(document);
|
|
600
645
|
const missingSkills = missingSkillEvidence(document);
|
|
646
|
+
const matrixReasons = evaluateQualityWitnessMatrix(document);
|
|
601
647
|
const reasons = [];
|
|
602
648
|
const maxRounds = document.remediation_policy.max_rounds;
|
|
603
649
|
const rounds = countQualityEvidenceRounds(document);
|
|
650
|
+
const q95Ledger = await computeQ95Ledger(paths, config, feature, document);
|
|
651
|
+
document.q95_ledger = q95Ledger;
|
|
652
|
+
await fs.writeFile(artifactPath, stringifyWorkspaceYamlDocument('5-quality.yaml', document), 'utf-8');
|
|
604
653
|
if (rounds > maxRounds && document.exceptions.length === 0) {
|
|
605
654
|
reasons.push(`quality remediation max_rounds exceeded (${rounds}/${maxRounds}); formal exception required in ${artifact}`);
|
|
606
655
|
}
|
|
@@ -621,9 +670,15 @@ async function evaluateWorkspaceQualityFeedback(paths, config, feature) {
|
|
|
621
670
|
if (missingSkills.length > 0) {
|
|
622
671
|
reasons.push(`required skill evidence missing for ${missingSkills.join(', ')}`);
|
|
623
672
|
}
|
|
673
|
+
if (matrixReasons.length > 0) {
|
|
674
|
+
reasons.push(`quality-witness matrix blocked finalize: ${matrixReasons.join(' | ')}`);
|
|
675
|
+
}
|
|
676
|
+
if (q95Ledger.score < q95Ledger.threshold && document.exceptions.length === 0) {
|
|
677
|
+
reasons.push(`Q95 composite score below threshold (${q95Ledger.score.toFixed(2)} < ${q95Ledger.threshold}); next best action: ${q95Ledger.next_best_action}`);
|
|
678
|
+
}
|
|
624
679
|
return { ok: reasons.length === 0, reasons, artifact };
|
|
625
680
|
}
|
|
626
|
-
async function evaluateWorkspaceTraceabilityFeedback(paths, config, feature) {
|
|
681
|
+
export async function evaluateWorkspaceTraceabilityFeedback(paths, config, feature) {
|
|
627
682
|
const activePath = path.join(paths.activeDir, feature.id);
|
|
628
683
|
const names = activeDocNamesForLayout(config);
|
|
629
684
|
const specPath = path.join(activePath, names.spec);
|
|
@@ -666,14 +721,258 @@ async function evaluateWorkspaceTraceabilityFeedback(paths, config, feature) {
|
|
|
666
721
|
artifact,
|
|
667
722
|
};
|
|
668
723
|
}
|
|
669
|
-
function
|
|
724
|
+
export async function computeQ95Ledger(paths, config, feature, document) {
|
|
725
|
+
const threshold = document.q95_ledger?.threshold ?? 95;
|
|
726
|
+
const weights = {
|
|
727
|
+
coverage: document.q95_ledger?.weights?.coverage ?? 30,
|
|
728
|
+
traceability: document.q95_ledger?.weights?.traceability ?? 20,
|
|
729
|
+
integrity: document.q95_ledger?.weights?.integrity ?? 20,
|
|
730
|
+
naming: document.q95_ledger?.weights?.naming ?? 10,
|
|
731
|
+
token: document.q95_ledger?.weights?.token ?? 20,
|
|
732
|
+
};
|
|
733
|
+
const coverage = computeCoverageAxis(document);
|
|
734
|
+
const traceability = await computeTraceabilityAxis(paths, config, feature, document);
|
|
735
|
+
const integrity = computeIntegrityAxis(document);
|
|
736
|
+
const naming = computeNamingAxis(feature, document);
|
|
737
|
+
const token = computeTokenAxis(document);
|
|
738
|
+
const axes = {
|
|
739
|
+
coverage: weightedAxis(coverage, weights.coverage),
|
|
740
|
+
traceability: weightedAxis(traceability, weights.traceability),
|
|
741
|
+
integrity: weightedAxis(integrity, weights.integrity),
|
|
742
|
+
naming: weightedAxis(naming, weights.naming),
|
|
743
|
+
token: weightedAxis(token, weights.token),
|
|
744
|
+
};
|
|
745
|
+
const score = round2(axes.coverage.weighted_score +
|
|
746
|
+
axes.traceability.weighted_score +
|
|
747
|
+
axes.integrity.weighted_score +
|
|
748
|
+
axes.naming.weighted_score +
|
|
749
|
+
axes.token.weighted_score);
|
|
750
|
+
const status = score >= threshold ? 'pass' : 'fail';
|
|
751
|
+
return {
|
|
752
|
+
threshold,
|
|
753
|
+
score,
|
|
754
|
+
status,
|
|
755
|
+
computed_at: nowIso(),
|
|
756
|
+
weights,
|
|
757
|
+
axes,
|
|
758
|
+
next_best_action: inferQ95NextAction(axes),
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
export function weightedAxis(input, weight) {
|
|
762
|
+
return {
|
|
763
|
+
raw_score: clampPercent(input.raw_score),
|
|
764
|
+
weighted_score: round2((clampPercent(input.raw_score) * weight) / 100),
|
|
765
|
+
rationale: input.rationale,
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
export function computeCoverageAxis(document) {
|
|
769
|
+
const unit = extractCoveragePerformance(document, 'unit');
|
|
770
|
+
const integration = extractCoveragePerformance(document, 'integration');
|
|
771
|
+
const raw = round2((unit + integration) / 2);
|
|
772
|
+
return {
|
|
773
|
+
raw_score: raw,
|
|
774
|
+
rationale: `unit=${unit.toFixed(2)} integration=${integration.toFixed(2)} derived from evidence_log`,
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
export async function computeTraceabilityAxis(paths, config, feature, document) {
|
|
778
|
+
if ((document.traceability?.requirements?.length ?? 0) === 0) {
|
|
779
|
+
return {
|
|
780
|
+
raw_score: 100,
|
|
781
|
+
rationale: 'no requirement matrix declared yet; dedicated traceability guardrail remains authoritative',
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
const activePath = path.join(paths.activeDir, feature.id);
|
|
785
|
+
const names = activeDocNamesForLayout(config);
|
|
786
|
+
const specPath = path.join(activePath, names.spec);
|
|
787
|
+
const changelogPath = path.join(activePath, names.changelog);
|
|
788
|
+
const [specContent, changelogContent] = await Promise.all([
|
|
789
|
+
fs.readFile(specPath, 'utf-8').catch(() => ''),
|
|
790
|
+
fs.readFile(changelogPath, 'utf-8').catch(() => ''),
|
|
791
|
+
]);
|
|
792
|
+
if (!specContent.trim() || !changelogContent.trim()) {
|
|
793
|
+
return {
|
|
794
|
+
raw_score: 70,
|
|
795
|
+
rationale: 'traceability matrix exists but spec/changelog evidence is unavailable for Q95 composite scoring',
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
try {
|
|
799
|
+
const spec = parseWorkspaceYamlDocument('1-spec.yaml', specContent);
|
|
800
|
+
const changelog = parseWorkspaceYamlDocument('4-changelog.yaml', changelogContent);
|
|
801
|
+
const result = evaluateWorkspaceTraceability(paths.projectRoot, spec, changelog, document);
|
|
802
|
+
return result.ok
|
|
803
|
+
? { raw_score: 100, rationale: 'traceability closure validated successfully' }
|
|
804
|
+
: { raw_score: 70, rationale: result.reasons.join(' | ') };
|
|
805
|
+
}
|
|
806
|
+
catch (error) {
|
|
807
|
+
return {
|
|
808
|
+
raw_score: 70,
|
|
809
|
+
rationale: `traceability parse error: ${error.message}`,
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
export function computeIntegrityAxis(document) {
|
|
814
|
+
const requiredSkillIds = Array.from(new Set((document.skill_evidence?.required_skill_ids ?? []).map((value) => value.trim()).filter(Boolean)));
|
|
815
|
+
const providedSkills = new Set((document.skill_evidence?.evidence ?? [])
|
|
816
|
+
.map((entry) => entry.skill_id.trim().toLowerCase())
|
|
817
|
+
.filter(Boolean));
|
|
818
|
+
const coveredSkills = requiredSkillIds.filter((id) => providedSkills.has(id.toLowerCase())).length;
|
|
819
|
+
const skillScore = requiredSkillIds.length === 0 ? 100 : round2((coveredSkills / requiredSkillIds.length) * 100);
|
|
820
|
+
const security = document.security_integrity;
|
|
821
|
+
const securityScore = !security
|
|
822
|
+
? 100
|
|
823
|
+
: round2([
|
|
824
|
+
integritySignalScore(security.endpoint_auth_review),
|
|
825
|
+
integritySignalScore(security.sensitive_data_exposure_review),
|
|
826
|
+
integritySignalScore(security.incident_response_review),
|
|
827
|
+
].reduce((acc, value) => acc + value, 0) / 3);
|
|
828
|
+
const raw = round2(skillScore * 0.7 + securityScore * 0.3);
|
|
829
|
+
return {
|
|
830
|
+
raw_score: raw,
|
|
831
|
+
rationale: `skill_evidence=${skillScore.toFixed(2)} security_integrity=${securityScore.toFixed(2)}`,
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
const LEGACY_NAMING_PATTERNS = [
|
|
835
|
+
/\bcoachsdd\b/u,
|
|
836
|
+
/\bcoach\s+sdd\b/u,
|
|
837
|
+
/\bcoach-sdd\b/u,
|
|
838
|
+
/\bcoach_sdd\b/u,
|
|
839
|
+
/\bsdd\s+coach\b/u,
|
|
840
|
+
/\.codesdd\b/u,
|
|
841
|
+
];
|
|
842
|
+
export function computeNamingAxis(feature, document) {
|
|
843
|
+
let score = 100;
|
|
844
|
+
const rationale = [];
|
|
845
|
+
if (!/^FEAT-\d{4,}$/.test(feature.id)) {
|
|
846
|
+
score -= 40;
|
|
847
|
+
rationale.push('feature id not canonical FEAT-####');
|
|
848
|
+
}
|
|
849
|
+
if (!feature.title || feature.title.trim().length < 10) {
|
|
850
|
+
score -= 30;
|
|
851
|
+
rationale.push('feature title too short');
|
|
852
|
+
}
|
|
853
|
+
const combined = JSON.stringify({ title: feature.title || '', quality: document }).toLowerCase();
|
|
854
|
+
if (LEGACY_NAMING_PATTERNS.some((pattern) => pattern.test(combined))) {
|
|
855
|
+
score -= 30;
|
|
856
|
+
rationale.push('legacy naming detected in active quality context by naming-contract forbidden terms');
|
|
857
|
+
}
|
|
858
|
+
return {
|
|
859
|
+
raw_score: clampPercent(score),
|
|
860
|
+
rationale: rationale.length > 0 ? rationale.join('; ') : 'canonical naming and state metadata checks passed',
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
export function computeTokenAxis(document) {
|
|
864
|
+
const tokenEntries = document.evidence_log.filter((entry) => {
|
|
865
|
+
const label = `${entry.kind} ${entry.result}`.toLowerCase();
|
|
866
|
+
return label.includes('token');
|
|
867
|
+
});
|
|
868
|
+
if (tokenEntries.length === 0) {
|
|
869
|
+
return {
|
|
870
|
+
raw_score: 100,
|
|
871
|
+
rationale: 'token telemetry not provided; neutral score applied until FEAT token harness evidence is recorded',
|
|
872
|
+
};
|
|
873
|
+
}
|
|
874
|
+
const percents = [];
|
|
875
|
+
for (const entry of tokenEntries) {
|
|
876
|
+
const matches = entry.result.match(/(\d+(?:\.\d+)?)\s*%/g) ?? [];
|
|
877
|
+
for (const match of matches) {
|
|
878
|
+
const value = Number(match.replace('%', '').trim());
|
|
879
|
+
if (Number.isFinite(value)) {
|
|
880
|
+
percents.push(clampPercent(value));
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
if (percents.length > 0) {
|
|
885
|
+
const best = Math.max(...percents);
|
|
886
|
+
return {
|
|
887
|
+
raw_score: best,
|
|
888
|
+
rationale: `token efficiency evidence detected (${best.toFixed(2)}%)`,
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
return {
|
|
892
|
+
raw_score: 70,
|
|
893
|
+
rationale: 'token evidence found but no numeric percentage; conservative score applied',
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
export function extractCoveragePerformance(document, kind) {
|
|
897
|
+
const target = kind === 'unit' ? document.coverage_targets.unit : document.coverage_targets.integration;
|
|
898
|
+
const relevant = document.evidence_log.filter((entry) => {
|
|
899
|
+
const evidenceKind = entry.kind.toLowerCase();
|
|
900
|
+
return (evidenceKind.includes(kind) ||
|
|
901
|
+
evidenceKind.includes('coverage') ||
|
|
902
|
+
(kind === 'integration' && evidenceKind.includes('e2e')));
|
|
903
|
+
});
|
|
904
|
+
if (relevant.length === 0)
|
|
905
|
+
return 0;
|
|
906
|
+
const values = relevant.flatMap((entry) => {
|
|
907
|
+
const matches = entry.result.match(/\d+(?:\.\d+)?/g) ?? [];
|
|
908
|
+
return matches.map(Number).filter((value) => Number.isFinite(value) && value >= 0 && value <= 100);
|
|
909
|
+
});
|
|
910
|
+
if (values.length === 0)
|
|
911
|
+
return 0;
|
|
912
|
+
const best = Math.max(...values);
|
|
913
|
+
if (target <= 0)
|
|
914
|
+
return 100;
|
|
915
|
+
return clampPercent((best / target) * 100);
|
|
916
|
+
}
|
|
917
|
+
export function integritySignalScore(value) {
|
|
918
|
+
if (value === 'passed' || value === 'not_applicable')
|
|
919
|
+
return 100;
|
|
920
|
+
if (value === 'pending')
|
|
921
|
+
return 50;
|
|
922
|
+
return 0;
|
|
923
|
+
}
|
|
924
|
+
export function inferQ95NextAction(axes) {
|
|
925
|
+
const ranking = [
|
|
926
|
+
{ axis: 'coverage', weightedGap: 30 - axes.coverage.weighted_score, rawGap: 100 - axes.coverage.raw_score },
|
|
927
|
+
{
|
|
928
|
+
axis: 'traceability',
|
|
929
|
+
weightedGap: 20 - axes.traceability.weighted_score,
|
|
930
|
+
rawGap: 100 - axes.traceability.raw_score,
|
|
931
|
+
},
|
|
932
|
+
{ axis: 'integrity', weightedGap: 20 - axes.integrity.weighted_score, rawGap: 100 - axes.integrity.raw_score },
|
|
933
|
+
{ axis: 'naming', weightedGap: 10 - axes.naming.weighted_score, rawGap: 100 - axes.naming.raw_score },
|
|
934
|
+
{ axis: 'token', weightedGap: 20 - axes.token.weighted_score, rawGap: 100 - axes.token.raw_score },
|
|
935
|
+
];
|
|
936
|
+
ranking.sort((a, b) => b.weightedGap - a.weightedGap || b.rawGap - a.rawGap);
|
|
937
|
+
const primary = ranking[0];
|
|
938
|
+
if (!primary || primary.weightedGap <= 0) {
|
|
939
|
+
return 'Q95 satisfied. Keep evidence_log and traceability synchronized for future rounds.';
|
|
940
|
+
}
|
|
941
|
+
switch (primary.axis) {
|
|
942
|
+
case 'coverage':
|
|
943
|
+
return 'Add or update unit/integration evidence entries with numeric percentages that meet coverage_targets.';
|
|
944
|
+
case 'traceability':
|
|
945
|
+
return 'Complete traceability.spec_anchor and traceability.requirements links across spec, changelog, code, and tests.';
|
|
946
|
+
case 'integrity':
|
|
947
|
+
return 'Record required skill evidence and close pending security_integrity reviews when applicable.';
|
|
948
|
+
case 'naming':
|
|
949
|
+
return 'Fix canonical naming drift in FEAT metadata and avoid legacy governance aliases in quality artifacts.';
|
|
950
|
+
case 'token':
|
|
951
|
+
return 'Record token efficiency telemetry (percentage) in evidence_log to score the token axis explicitly.';
|
|
952
|
+
default:
|
|
953
|
+
return 'Record missing evidence to raise the lowest weighted axis.';
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
export function clampPercent(value) {
|
|
957
|
+
if (!Number.isFinite(value))
|
|
958
|
+
return 0;
|
|
959
|
+
if (value < 0)
|
|
960
|
+
return 0;
|
|
961
|
+
if (value > 100)
|
|
962
|
+
return 100;
|
|
963
|
+
return value;
|
|
964
|
+
}
|
|
965
|
+
export function round2(value) {
|
|
966
|
+
return Math.round(value * 100) / 100;
|
|
967
|
+
}
|
|
968
|
+
export function missingCoverageTargets(document) {
|
|
670
969
|
const targets = [
|
|
671
970
|
{ kind: 'unit', target: document.coverage_targets.unit },
|
|
672
971
|
{ kind: 'integration', target: document.coverage_targets.integration },
|
|
673
972
|
];
|
|
674
973
|
return targets.filter((target) => !coverageTargetMet(document, target.kind, target.target));
|
|
675
974
|
}
|
|
676
|
-
function coverageTargetMet(document, kind, target) {
|
|
975
|
+
export function coverageTargetMet(document, kind, target) {
|
|
677
976
|
return document.evidence_log.some((entry) => {
|
|
678
977
|
const evidenceKind = entry.kind.toLowerCase();
|
|
679
978
|
const result = entry.result.toLowerCase();
|
|
@@ -687,14 +986,14 @@ function coverageTargetMet(document, kind, target) {
|
|
|
687
986
|
return percentages.some((value) => value >= target);
|
|
688
987
|
});
|
|
689
988
|
}
|
|
690
|
-
function countQualityEvidenceRounds(document) {
|
|
989
|
+
export function countQualityEvidenceRounds(document) {
|
|
691
990
|
const roundKinds = ['round', 'unit', 'integration', 'e2e', 'coverage', 'validation', 'test', 'build'];
|
|
692
991
|
return document.evidence_log.filter((entry) => {
|
|
693
992
|
const kind = entry.kind.toLowerCase();
|
|
694
993
|
return roundKinds.some((token) => kind.includes(token));
|
|
695
994
|
}).length;
|
|
696
995
|
}
|
|
697
|
-
function missingSkillEvidence(document) {
|
|
996
|
+
export function missingSkillEvidence(document) {
|
|
698
997
|
const required = Array.from(new Set((document.skill_evidence?.required_skill_ids ?? []).map((value) => value.trim()).filter(Boolean)));
|
|
699
998
|
if (required.length === 0) {
|
|
700
999
|
return [];
|
|
@@ -2,6 +2,12 @@ import { CLI_NAME } from "../../branding.js";
|
|
|
2
2
|
import { allocateEntityId, loadStateSnapshot, nowIso, saveStateTransaction } from "../state.js";
|
|
3
3
|
import { slugify, getRuntime, persistAndRender } from "../legacy-operations.js";
|
|
4
4
|
import { withStateLock } from "../state-lock.js";
|
|
5
|
+
function firstNonEmpty(values) {
|
|
6
|
+
return (values || []).map((value) => value.trim()).find(Boolean) || '';
|
|
7
|
+
}
|
|
8
|
+
function routeLabelFromGap(title, routePath) {
|
|
9
|
+
return title.trim() || routePath;
|
|
10
|
+
}
|
|
5
11
|
export class FrontendGapService {
|
|
6
12
|
stores;
|
|
7
13
|
constructor(stores) {
|
|
@@ -41,17 +47,20 @@ export class FrontendGapService {
|
|
|
41
47
|
for (const routePath of options?.routes || []) {
|
|
42
48
|
const routeId = `route-${slugify(routePath) || 'root'}`;
|
|
43
49
|
const existing = snapshot.frontendMap.routes.find((route) => route.id === routeId);
|
|
50
|
+
const navSurface = firstNonEmpty(options?.menu);
|
|
44
51
|
if (existing) {
|
|
45
52
|
existing.ui_status = existing.ui_status === 'OK' ? 'PARTIAL' : existing.ui_status;
|
|
46
53
|
existing.source_gap_ids = Array.from(new Set([...existing.source_gap_ids, id]));
|
|
54
|
+
existing.label = existing.label || routeLabelFromGap(title, routePath);
|
|
55
|
+
existing.nav_surface = existing.nav_surface || navSurface;
|
|
47
56
|
}
|
|
48
57
|
else {
|
|
49
58
|
snapshot.frontendMap.routes.push({
|
|
50
59
|
id: routeId,
|
|
51
60
|
path: routePath,
|
|
52
61
|
parent_id: '',
|
|
53
|
-
label:
|
|
54
|
-
nav_surface:
|
|
62
|
+
label: routeLabelFromGap(title, routePath),
|
|
63
|
+
nav_surface: navSurface,
|
|
55
64
|
ui_status: 'GAP',
|
|
56
65
|
source_gap_ids: [id],
|
|
57
66
|
implemented_files: [],
|
|
@@ -83,17 +92,20 @@ export class FrontendGapService {
|
|
|
83
92
|
gap.resolved_by_feature = options?.feature || '';
|
|
84
93
|
gap.implemented_files = options?.files || [];
|
|
85
94
|
gap.updated_at = now;
|
|
86
|
-
const routeTargets = new Set([...
|
|
95
|
+
const routeTargets = new Set([...gap.route_targets, ...(options?.routes || [])]);
|
|
96
|
+
const relatedRouteIds = new Set(gap.related_route_ids);
|
|
97
|
+
const navSurface = firstNonEmpty(gap.menu_targets);
|
|
87
98
|
for (const routePath of routeTargets) {
|
|
88
99
|
const routeId = `route-${slugify(routePath) || 'root'}`;
|
|
100
|
+
relatedRouteIds.add(routeId);
|
|
89
101
|
const route = snapshot.frontendMap.routes.find((entry) => entry.id === routeId);
|
|
90
102
|
if (!route) {
|
|
91
103
|
snapshot.frontendMap.routes.push({
|
|
92
104
|
id: routeId,
|
|
93
105
|
path: routePath,
|
|
94
106
|
parent_id: '',
|
|
95
|
-
label:
|
|
96
|
-
nav_surface:
|
|
107
|
+
label: routeLabelFromGap(gap.title, routePath),
|
|
108
|
+
nav_surface: navSurface,
|
|
97
109
|
ui_status: 'OK',
|
|
98
110
|
source_gap_ids: [gap.id],
|
|
99
111
|
implemented_files: options?.files || [],
|
|
@@ -102,9 +114,12 @@ export class FrontendGapService {
|
|
|
102
114
|
continue;
|
|
103
115
|
}
|
|
104
116
|
route.ui_status = 'OK';
|
|
105
|
-
route.
|
|
106
|
-
route.
|
|
117
|
+
route.label = route.label || routeLabelFromGap(gap.title, routePath);
|
|
118
|
+
route.nav_surface = route.nav_surface || navSurface;
|
|
119
|
+
route.implemented_files = Array.from(new Set([...route.implemented_files, ...(options?.files || [])]));
|
|
120
|
+
route.source_gap_ids = Array.from(new Set([...route.source_gap_ids, gap.id]));
|
|
107
121
|
}
|
|
122
|
+
gap.related_route_ids = Array.from(relatedRouteIds);
|
|
108
123
|
await saveStateTransaction(paths, {
|
|
109
124
|
frontendGaps: snapshot.frontendGaps,
|
|
110
125
|
frontendMap: snapshot.frontendMap,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type GovernanceControlPlaneRuntimeMode = 'native' | 'contract';
|
|
2
|
+
export type GovernanceControlPlaneRuntimeDependencyResolver = (packageName: string) => string;
|
|
3
|
+
export interface GovernanceControlPlaneRuntimeAdapterStatus<TRuntime extends string = string> {
|
|
4
|
+
runtime: TRuntime;
|
|
5
|
+
package: string;
|
|
6
|
+
available: boolean;
|
|
7
|
+
mode: GovernanceControlPlaneRuntimeMode;
|
|
8
|
+
reason: string;
|
|
9
|
+
}
|
|
10
|
+
export interface GovernanceControlPlaneRuntimeAdapters {
|
|
11
|
+
ui_write_policy: 'cli-shell-out-only';
|
|
12
|
+
canonical_state: '.sdd/state/*.yaml';
|
|
13
|
+
frontend: GovernanceControlPlaneRuntimeAdapterStatus<'angular-spa'>;
|
|
14
|
+
realtime: GovernanceControlPlaneRuntimeAdapterStatus<'socket.io'>;
|
|
15
|
+
}
|
|
16
|
+
export declare function resolveGovernanceControlPlaneRuntimeAdapters(resolver?: GovernanceControlPlaneRuntimeDependencyResolver): GovernanceControlPlaneRuntimeAdapters;
|
|
17
|
+
//# sourceMappingURL=governance-control-plane-runtime-adapters.d.ts.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
const FRONTEND_RUNTIME_PACKAGE = '@angular/core';
|
|
3
|
+
const REALTIME_RUNTIME_PACKAGE = 'socket.io';
|
|
4
|
+
export function resolveGovernanceControlPlaneRuntimeAdapters(resolver = defaultRuntimeDependencyResolver) {
|
|
5
|
+
return {
|
|
6
|
+
ui_write_policy: 'cli-shell-out-only',
|
|
7
|
+
canonical_state: '.sdd/state/*.yaml',
|
|
8
|
+
frontend: resolveAdapterStatus('angular-spa', FRONTEND_RUNTIME_PACKAGE, resolver),
|
|
9
|
+
realtime: resolveAdapterStatus('socket.io', REALTIME_RUNTIME_PACKAGE, resolver),
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function resolveAdapterStatus(runtime, packageName, resolver) {
|
|
13
|
+
try {
|
|
14
|
+
resolver(packageName);
|
|
15
|
+
return {
|
|
16
|
+
runtime,
|
|
17
|
+
package: packageName,
|
|
18
|
+
available: true,
|
|
19
|
+
mode: 'native',
|
|
20
|
+
reason: `${packageName} resolved; native runtime adapter is available.`,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
const detail = error instanceof Error ? error.message : `Unable to resolve ${packageName}`;
|
|
25
|
+
return {
|
|
26
|
+
runtime,
|
|
27
|
+
package: packageName,
|
|
28
|
+
available: true,
|
|
29
|
+
mode: 'contract',
|
|
30
|
+
reason: `${packageName} unresolved; using internal contract adapter (${detail}).`,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function defaultRuntimeDependencyResolver(packageName) {
|
|
35
|
+
const runtimeRequire = createRequire(import.meta.url);
|
|
36
|
+
return runtimeRequire.resolve(packageName);
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=governance-control-plane-runtime-adapters.js.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { type SddStateSnapshot } from '../state.js';
|
|
2
|
+
export type ControlPlaneMutationAction = 'frontend-impact' | 'finalize';
|
|
3
|
+
export interface GovernanceControlPlaneManifest {
|
|
4
|
+
protocol: 'codesdd-governance-control-plane/v1';
|
|
5
|
+
architecture: {
|
|
6
|
+
frontend: 'angular-spa';
|
|
7
|
+
api: 'rest';
|
|
8
|
+
realtime: 'socket.io';
|
|
9
|
+
ui_write_policy: 'cli-shell-out-only';
|
|
10
|
+
canonical_state: '.sdd/state/*.yaml';
|
|
11
|
+
};
|
|
12
|
+
rest: {
|
|
13
|
+
read_endpoints: Array<{
|
|
14
|
+
method: 'GET';
|
|
15
|
+
path: string;
|
|
16
|
+
description: string;
|
|
17
|
+
}>;
|
|
18
|
+
write_intent_endpoint: {
|
|
19
|
+
method: 'POST';
|
|
20
|
+
path: '/api/intents/cli-shell-out';
|
|
21
|
+
behavior: 'intent-only-by-default';
|
|
22
|
+
allowed_actions: ControlPlaneMutationAction[];
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
realtime: {
|
|
26
|
+
transport: 'socket.io';
|
|
27
|
+
available: boolean;
|
|
28
|
+
channel: 'codesdd.state.changed';
|
|
29
|
+
reason: string;
|
|
30
|
+
};
|
|
31
|
+
blockers: string[];
|
|
32
|
+
}
|
|
33
|
+
export interface GovernanceProjection {
|
|
34
|
+
generated_at: string;
|
|
35
|
+
backlog: SddStateSnapshot['backlog'];
|
|
36
|
+
finalize_queue: SddStateSnapshot['finalizeQueue'];
|
|
37
|
+
frontend_decisions: SddStateSnapshot['frontendDecisions'];
|
|
38
|
+
}
|
|
39
|
+
export interface GovernanceMutationIntent {
|
|
40
|
+
action: ControlPlaneMutationAction;
|
|
41
|
+
command: {
|
|
42
|
+
bin: string;
|
|
43
|
+
args: string[];
|
|
44
|
+
cwd: string;
|
|
45
|
+
};
|
|
46
|
+
gate: {
|
|
47
|
+
executes_immediately: false;
|
|
48
|
+
required_flag: '--allow-mutation-execution';
|
|
49
|
+
ui_can_write_state_directly: false;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export declare class GovernanceControlPlaneService {
|
|
53
|
+
manifest(projectRoot: string): Promise<GovernanceControlPlaneManifest>;
|
|
54
|
+
readProjection(projectRoot: string): Promise<GovernanceProjection>;
|
|
55
|
+
readBacklogFeature(projectRoot: string, featureId: string): Promise<Record<string, unknown>>;
|
|
56
|
+
planMutationIntent(projectRoot: string, action: ControlPlaneMutationAction, params: Record<string, string>): GovernanceMutationIntent;
|
|
57
|
+
executeMutationIntent(intent: GovernanceMutationIntent, options?: {
|
|
58
|
+
allowMutationExecution?: boolean;
|
|
59
|
+
}): Promise<{
|
|
60
|
+
executed: boolean;
|
|
61
|
+
stdout: string;
|
|
62
|
+
stderr: string;
|
|
63
|
+
}>;
|
|
64
|
+
private buildActionArgs;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=governance-control-plane.service.d.ts.map
|