@devtrack-solution/codesdd 1.2.2 → 1.2.4-rc3
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 +170 -31
- package/.sdd/skills/curated/devtrack-api/agents/claude-code.yaml +8 -0
- package/.sdd/skills/curated/devtrack-api/agents/codex.yaml +8 -0
- package/.sdd/skills/curated/devtrack-api/agents/cursor.yaml +8 -0
- package/.sdd/skills/curated/devtrack-api/agents/gemini.yaml +8 -0
- package/.sdd/skills/curated/devtrack-api/agents/kimi.yaml +8 -0
- package/.sdd/skills/curated/devtrack-api/agents/openai.yaml +4 -2
- package/.sdd/skills/curated/devtrack-api/agents/opencode.yaml +10 -0
- package/.sdd/skills/curated/devtrack-api/references/application-presentation.md +2 -2
- 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 +372 -0
- package/.sdd/skills/curated/devtrack-api/references/domain-modeling.md +13 -13
- package/.sdd/skills/curated/devtrack-api/references/field-validation-protocol.md +95 -0
- package/.sdd/skills/curated/devtrack-api/references/foundation-layout.md +294 -0
- package/.sdd/skills/curated/devtrack-api/references/implementation-checklist.md +5 -5
- package/.sdd/skills/curated/devtrack-api/references/imports-lint.md +4 -0
- package/.sdd/skills/curated/devtrack-api/references/portable-agent-contract.md +41 -0
- package/.sdd/skills/curated/devtrack-api/references/testing-validation.md +2 -2
- package/.sdd/skills/curated/devtrack-api/references/typeorm-infrastructure.md +7 -9
- package/LICENSE +1 -1
- package/README.md +399 -53
- package/bin/codesdd.js +3 -2
- package/dist/applications/sdd/index.d.ts +16 -0
- package/dist/applications/sdd/index.js +16 -0
- 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 +320 -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 +489 -28
- package/dist/commands/sdd/plugin.d.ts +3 -0
- package/dist/commands/sdd/plugin.js +158 -0
- package/dist/commands/sdd/shared.d.ts +1 -0
- package/dist/commands/sdd/shared.js +11 -22
- package/dist/commands/sdd/skills.js +7 -0
- package/dist/commands/sdd.js +107 -15
- 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 +19 -1
- package/dist/core/cli-command-quality.d.ts +27 -0
- package/dist/core/cli-command-quality.js +180 -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 +200 -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 +20 -1
- package/dist/core/config-schema.js +70 -2
- package/dist/core/config.d.ts +3 -3
- package/dist/core/config.js +4 -4
- package/dist/core/global-config.d.ts +57 -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/agent-runtime-contract.d.ts +204 -0
- package/dist/core/sdd/agent-runtime-contract.js +200 -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 +44 -0
- package/dist/core/sdd/check.js +62 -24
- 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 +53 -8
- package/dist/core/sdd/coordination/coordination-adapters.js +182 -16
- package/dist/core/sdd/coordination/index.d.ts +1 -0
- package/dist/core/sdd/coordination/index.js +1 -0
- package/dist/core/sdd/coordination/redis-runtime.d.ts +131 -0
- package/dist/core/sdd/coordination/redis-runtime.js +698 -0
- package/dist/core/sdd/deepagent-contracts.d.ts +370 -0
- package/dist/core/sdd/deepagent-contracts.js +235 -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 +3 -3
- package/dist/core/sdd/default-bootstrap-files.js +50 -10
- package/dist/core/sdd/default-skills.d.ts +30 -0
- package/dist/core/sdd/default-skills.js +288 -8
- package/dist/core/sdd/devtrack-api-appliance.d.ts +91 -0
- package/dist/core/sdd/devtrack-api-appliance.js +280 -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 +54 -20
- package/dist/core/sdd/domain/capability-diff.d.ts +63 -0
- package/dist/core/sdd/domain/capability-diff.js +200 -0
- package/dist/core/sdd/domain/change-safety-guardrails.d.ts +74 -0
- package/dist/core/sdd/domain/change-safety-guardrails.js +333 -0
- 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/semantic-intent-classifier.d.ts +29 -0
- package/dist/core/sdd/domain/semantic-intent-classifier.js +117 -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/foundation-artifact-map-validator.d.ts +16 -0
- package/dist/core/sdd/foundation-artifact-map-validator.js +71 -0
- package/dist/core/sdd/foundation-layer-manifest.d.ts +24 -0
- package/dist/core/sdd/foundation-layer-manifest.js +117 -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/intent-guard.d.ts +22 -0
- package/dist/core/sdd/intent-guard.js +67 -0
- package/dist/core/sdd/json-schema.js +108 -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 +507 -44
- package/dist/core/sdd/lenses.d.ts +1 -0
- package/dist/core/sdd/lenses.js +29 -1
- package/dist/core/sdd/migrate-workspace.js +95 -2
- package/dist/core/sdd/migrate.d.ts +1 -1
- package/dist/core/sdd/migrate.js +36 -2
- package/dist/core/sdd/package-security-gates.d.ts +21 -0
- package/dist/core/sdd/package-security-gates.js +119 -0
- package/dist/core/sdd/package-structure-gate.d.ts +83 -0
- package/dist/core/sdd/package-structure-gate.js +357 -0
- package/dist/core/sdd/parallel-feat-automation.d.ts +330 -0
- package/dist/core/sdd/parallel-feat-automation.js +424 -0
- package/dist/core/sdd/plugin-broker.d.ts +777 -0
- package/dist/core/sdd/plugin-broker.js +492 -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 +139 -0
- package/dist/core/sdd/plugin-cli.js +265 -0
- package/dist/core/sdd/plugin-evidence.d.ts +348 -0
- package/dist/core/sdd/plugin-evidence.js +307 -0
- package/dist/core/sdd/plugin-manifest.d.ts +232 -0
- package/dist/core/sdd/plugin-manifest.js +225 -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 +447 -0
- package/dist/core/sdd/plugin-registry.js +138 -0
- package/dist/core/sdd/plugin-sdk-contract.d.ts +363 -0
- package/dist/core/sdd/plugin-sdk-contract.js +268 -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 +620 -0
- package/dist/core/sdd/quality-validation.js +239 -0
- package/dist/core/sdd/release-readiness.d.ts +19 -0
- package/dist/core/sdd/release-readiness.js +472 -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/runtime-boundary-contract.d.ts +45 -0
- package/dist/core/sdd/runtime-boundary-contract.js +90 -0
- package/dist/core/sdd/sanitize.d.ts +30 -1
- package/dist/core/sdd/sanitize.js +23 -23
- package/dist/core/sdd/sdk-agent-plugin-quality-gates.d.ts +150 -0
- package/dist/core/sdd/sdk-agent-plugin-quality-gates.js +258 -0
- package/dist/core/sdd/services/agent-run.service.d.ts +97 -0
- package/dist/core/sdd/services/agent-run.service.js +261 -0
- package/dist/core/sdd/services/breakdown.service.js +2 -1
- package/dist/core/sdd/services/capability-diff.service.d.ts +18 -0
- package/dist/core/sdd/services/capability-diff.service.js +26 -0
- package/dist/core/sdd/services/change-safety-preflight.service.d.ts +17 -0
- package/dist/core/sdd/services/change-safety-preflight.service.js +17 -0
- package/dist/core/sdd/services/context.service.d.ts +43 -340
- package/dist/core/sdd/services/context.service.js +341 -25
- 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 +105 -0
- package/dist/core/sdd/services/finalize.service.js +499 -38
- package/dist/core/sdd/services/frontend-gap.service.js +22 -7
- package/dist/core/sdd/services/frontend-impact.service.d.ts +1 -1
- 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/semantic-intent-classifier.service.d.ts +6 -0
- package/dist/core/sdd/services/semantic-intent-classifier.service.js +7 -0
- 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 +23 -0
- package/dist/core/sdd/state.js +313 -66
- package/dist/core/sdd/store/sdd-stores.js +2 -2
- package/dist/core/sdd/structural-health.d.ts +55 -55
- package/dist/core/sdd/types.d.ts +60 -19
- package/dist/core/sdd/types.js +21 -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/views.js +17 -0
- package/dist/core/sdd/workspace-schemas.d.ts +670 -19
- package/dist/core/sdd/workspace-schemas.js +285 -5
- 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/domains/sdd/index.d.ts +6 -0
- package/dist/domains/sdd/index.js +6 -0
- package/dist/infrastructures/sdd/index.d.ts +7 -0
- package/dist/infrastructures/sdd/index.js +6 -0
- package/dist/presentations/cli/sdd/index.d.ts +3 -0
- package/dist/presentations/cli/sdd/index.js +3 -0
- package/dist/shared/sdd/index.d.ts +3 -0
- package/dist/shared/sdd/index.js +2 -0
- 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 +34 -21
- package/schemas/sdd/1-spec.schema.json +1 -1
- package/schemas/sdd/2-plan.schema.json +280 -3
- 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 +701 -5
- 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/agent-runtime-command-plan.schema.json +212 -0
- package/schemas/sdd/agent-runtime-opencode-run-evidence.schema.json +270 -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 +645 -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 +637 -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 +304 -0
- package/schemas/sdd/parallel-feat-automation-request.schema.json +109 -0
- package/schemas/sdd/parallel-feat-scheduler-request.schema.json +116 -0
- package/schemas/sdd/parallel-feat-scheduler-result.schema.json +404 -0
- package/schemas/sdd/plugin-artifact-manifest.schema.json +259 -0
- package/schemas/sdd/plugin-artifact-map.schema.json +223 -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 +678 -0
- package/schemas/sdd/plugin-language-runtime.schema.json +103 -0
- package/schemas/sdd/plugin-package-governance.schema.json +74 -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 +729 -0
- package/schemas/sdd/plugin-rollback-manifest.schema.json +87 -0
- package/schemas/sdd/plugin-runtime-invocation-plan.schema.json +954 -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 +1337 -0
- package/schemas/sdd/quality-run.schema.json +197 -0
- package/schemas/sdd/quality-scenario.schema.json +252 -0
- package/schemas/sdd/sdk-agent-plugin-quality-gate-input.schema.json +168 -0
- package/schemas/sdd/sdk-agent-plugin-quality-gate-report.schema.json +160 -0
- package/schemas/sdd/workspace-catalog.schema.json +13232 -35
- 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,19 +1,80 @@
|
|
|
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";
|
|
12
|
-
import { getRuntime, persistAndRender, relProjectPath, coreDocRef, activeDocNamesForLayout, activeDocCandidateNames, unresolvedDependencies, updateDependencyMetadata, gitChangedFiles, detectFrontendImpactEvidence, maybeCreateAutomaticFrontendGap, buildFinalizeQueue, evaluateFeatureQuality, buildAdrMarkdown, applyLoggedTransition, gateSatisfied, pathExists, RADAR_TO_DISCOVERY_STATUS } from "../legacy-operations.js";
|
|
13
|
+
import { getRuntime, persistAndRender, relProjectPath, coreDocRef, activeDocNamesForLayout, activeDocCandidateNames, unresolvedDependencies, updateDependencyMetadata, gitChangedFiles, detectFrontendImpactEvidence, maybeCreateAutomaticFrontendGap, buildFinalizeQueue, evaluateFeatureQuality, buildAdrMarkdown, applyLoggedTransition, gateSatisfied, pathExists, computeReadyFeatures, extractFeatureNumber, buildDependentsMap, scoreReadyItem, RADAR_TO_DISCOVERY_STATUS } from "../legacy-operations.js";
|
|
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";
|
|
19
|
+
function recordFinalizeHistory(history, item) {
|
|
20
|
+
const index = history.findIndex((entry) => entry.feature_id === item.feature_id);
|
|
21
|
+
if (index >= 0) {
|
|
22
|
+
history[index] = item;
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
history.push(item);
|
|
26
|
+
history.sort((a, b) => a.feature_id.localeCompare(b.feature_id));
|
|
27
|
+
}
|
|
28
|
+
function moveCompletedFinalizeItemsToHistory(state) {
|
|
29
|
+
const activeItems = [];
|
|
30
|
+
for (const item of state.items) {
|
|
31
|
+
if (item.status === 'DONE') {
|
|
32
|
+
recordFinalizeHistory(state.history, {
|
|
33
|
+
feature_id: item.feature_id,
|
|
34
|
+
status: 'DONE',
|
|
35
|
+
summary: item.summary,
|
|
36
|
+
created_at: item.created_at,
|
|
37
|
+
completed_at: item.completed_at || item.created_at || '',
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
activeItems.push(item);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
state.items = activeItems;
|
|
45
|
+
}
|
|
46
|
+
function buildPostFinalizeReplan(items, finalized, unlocked, generatedAt) {
|
|
47
|
+
const rank = 'impact';
|
|
48
|
+
const computed = computeReadyFeatures(items, { rank });
|
|
49
|
+
const dependentsMap = buildDependentsMap(items);
|
|
50
|
+
const ready = computed.ready
|
|
51
|
+
.map((item) => {
|
|
52
|
+
const scored = scoreReadyItem(item, items, dependentsMap, rank);
|
|
53
|
+
return {
|
|
54
|
+
id: item.id,
|
|
55
|
+
title: item.title,
|
|
56
|
+
recommended_skills: item.recommended_skills.slice(0, 3),
|
|
57
|
+
score: scored.score,
|
|
58
|
+
reasons: scored.reasons,
|
|
59
|
+
};
|
|
60
|
+
})
|
|
61
|
+
.sort((left, right) => {
|
|
62
|
+
if (right.score !== left.score)
|
|
63
|
+
return right.score - left.score;
|
|
64
|
+
return extractFeatureNumber(left.id) - extractFeatureNumber(right.id);
|
|
65
|
+
})
|
|
66
|
+
.slice(0, 10);
|
|
67
|
+
return {
|
|
68
|
+
rank,
|
|
69
|
+
generated_at: generatedAt,
|
|
70
|
+
finalized,
|
|
71
|
+
unlocked,
|
|
72
|
+
ready,
|
|
73
|
+
waves: computed.waves,
|
|
74
|
+
blocked_count: computed.blocked.length,
|
|
75
|
+
conflicts_count: computed.conflicts.length,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
17
78
|
export class FinalizeService {
|
|
18
79
|
stores;
|
|
19
80
|
constructor(stores) {
|
|
@@ -23,6 +84,7 @@ export class FinalizeService {
|
|
|
23
84
|
const { config, paths } = await getRuntime(projectRoot);
|
|
24
85
|
return withStateLock(paths.stateDir, async () => {
|
|
25
86
|
const snapshot = await loadStateSnapshot(paths, config);
|
|
87
|
+
moveCompletedFinalizeItemsToHistory(snapshot.finalizeQueue);
|
|
26
88
|
snapshot.finalizeQueue.items = await buildFinalizeQueue(paths, snapshot.backlog.items, snapshot.finalizeQueue.items);
|
|
27
89
|
const pending = snapshot.finalizeQueue.items.filter((item) => item.status === 'PENDING');
|
|
28
90
|
const inferredRef = options?.allReady
|
|
@@ -45,6 +107,7 @@ export class FinalizeService {
|
|
|
45
107
|
finalized: [],
|
|
46
108
|
unblocked: [],
|
|
47
109
|
pending: pending.length,
|
|
110
|
+
post_finalize_replan: buildPostFinalizeReplan(snapshot.backlog.items, [], [], nowIso()),
|
|
48
111
|
updated_core_docs: [],
|
|
49
112
|
updated_readme: false,
|
|
50
113
|
updated_agent_guide: false,
|
|
@@ -197,6 +260,14 @@ export class FinalizeService {
|
|
|
197
260
|
}
|
|
198
261
|
docWarnings.push(`${feature.id} finalized with --force-transition despite privacy compliance guardrail warnings.`);
|
|
199
262
|
}
|
|
263
|
+
const devtrackApiPolicyGuardrail = await evaluateDevTrackApiPolicyGates(paths, config, feature);
|
|
264
|
+
if (!devtrackApiPolicyGuardrail.ok) {
|
|
265
|
+
docWarnings.push(`${feature.id} devtrack-api policy gate blocked finalize: ${devtrackApiPolicyGuardrail.reasons.join(' | ')}`);
|
|
266
|
+
if (!options?.forceTransition) {
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
docWarnings.push(`${feature.id} finalized with --force-transition despite devtrack-api policy gate warnings.`);
|
|
270
|
+
}
|
|
200
271
|
if (!feature.requires_adr && (await activeWorkspaceDeclaresMandatoryAdrImpact(paths, feature))) {
|
|
201
272
|
feature.requires_adr = true;
|
|
202
273
|
}
|
|
@@ -204,20 +275,14 @@ export class FinalizeService {
|
|
|
204
275
|
const requiredAdrPath = path.join(paths.coreDir, 'adrs', adrFileName(feature.id));
|
|
205
276
|
const adrContent = await fs.readFile(requiredAdrPath, 'utf-8').catch(() => '');
|
|
206
277
|
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.`);
|
|
278
|
+
docWarnings.push(`${feature.id} adr governor blocked finalize: ADR obrigatório ausente: ${relProjectPath(paths, requiredAdrPath)}`);
|
|
279
|
+
continue;
|
|
212
280
|
}
|
|
213
281
|
else {
|
|
214
|
-
const adrViolations =
|
|
282
|
+
const adrViolations = evaluateAdrGovernorDocument(adrContent);
|
|
215
283
|
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.`);
|
|
284
|
+
docWarnings.push(`${feature.id} adr governor blocked finalize: ADR obrigatório inválido: ${adrViolations.join(' | ')}`);
|
|
285
|
+
continue;
|
|
221
286
|
}
|
|
222
287
|
}
|
|
223
288
|
}
|
|
@@ -261,6 +326,7 @@ export class FinalizeService {
|
|
|
261
326
|
afterTransition: (currentFeature) => {
|
|
262
327
|
currentFeature.current_stage = 'consolidacao';
|
|
263
328
|
currentFeature.done_at = now;
|
|
329
|
+
currentFeature.archived_at = now;
|
|
264
330
|
currentFeature.last_sync_at = now;
|
|
265
331
|
},
|
|
266
332
|
});
|
|
@@ -270,19 +336,14 @@ export class FinalizeService {
|
|
|
270
336
|
continue;
|
|
271
337
|
}
|
|
272
338
|
const queue = snapshot.finalizeQueue.items.find((item) => item.feature_id === featureId);
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
summary: `Finalizado manualmente: ${featureId}`,
|
|
282
|
-
created_at: now,
|
|
283
|
-
completed_at: now,
|
|
284
|
-
});
|
|
285
|
-
}
|
|
339
|
+
snapshot.finalizeQueue.items = snapshot.finalizeQueue.items.filter((item) => item.feature_id !== featureId);
|
|
340
|
+
recordFinalizeHistory(snapshot.finalizeQueue.history, {
|
|
341
|
+
feature_id: featureId,
|
|
342
|
+
status: 'DONE',
|
|
343
|
+
summary: queue?.summary || `Finalizado manualmente: ${featureId}`,
|
|
344
|
+
created_at: queue?.created_at || now,
|
|
345
|
+
completed_at: now,
|
|
346
|
+
});
|
|
286
347
|
if ((feature.origin_type === 'radar' || feature.origin_type === 'epic') && feature.origin_ref) {
|
|
287
348
|
const siblings = snapshot.backlog.items.filter((item) => (item.origin_type === 'radar' || item.origin_type === 'epic') && item.origin_ref === feature.origin_ref);
|
|
288
349
|
if (siblings.every((item) => item.status === 'DONE' || item.status === 'ARCHIVED')) {
|
|
@@ -395,6 +456,7 @@ export class FinalizeService {
|
|
|
395
456
|
}
|
|
396
457
|
await fs.mkdir(paths.archivedDir, { recursive: true });
|
|
397
458
|
await fs.rename(activeDirPath, archivedDirPath);
|
|
459
|
+
await normalizeArchivedFeatureActiveReferences(paths, feature.id);
|
|
398
460
|
const postActiveValidation = await validatePostActiveFeaturePlacement(paths, feature.id);
|
|
399
461
|
if (!postActiveValidation.ok) {
|
|
400
462
|
if (!(await pathExists(activeDirPath)) && (await pathExists(archivedDirPath))) {
|
|
@@ -415,6 +477,7 @@ export class FinalizeService {
|
|
|
415
477
|
});
|
|
416
478
|
}
|
|
417
479
|
updateDependencyMetadata(snapshot.backlog.items);
|
|
480
|
+
const postFinalizeReplan = buildPostFinalizeReplan(snapshot.backlog.items, finalized, Array.from(unblocked).sort(), now);
|
|
418
481
|
const stateUpdates = {
|
|
419
482
|
discoveryIndex: snapshot.discoveryIndex,
|
|
420
483
|
backlog: snapshot.backlog,
|
|
@@ -448,6 +511,7 @@ export class FinalizeService {
|
|
|
448
511
|
finalized,
|
|
449
512
|
unblocked: Array.from(unblocked).sort(),
|
|
450
513
|
pending: remaining,
|
|
514
|
+
post_finalize_replan: postFinalizeReplan,
|
|
451
515
|
updated_core_docs: Array.from(updatedCoreDocs).sort(),
|
|
452
516
|
updated_readme: syncResult.updatedReadme,
|
|
453
517
|
updated_agent_guide: syncResult.updatedAgentGuide || syncResult.updatedRootAgents,
|
|
@@ -458,9 +522,9 @@ export class FinalizeService {
|
|
|
458
522
|
});
|
|
459
523
|
}
|
|
460
524
|
}
|
|
461
|
-
async function inferFinalizeTarget(paths, pending, explicitRef, cwd) {
|
|
525
|
+
export async function inferFinalizeTarget(paths, pending, explicitRef, cwd) {
|
|
462
526
|
if (explicitRef)
|
|
463
|
-
return explicitRef;
|
|
527
|
+
return normalizeFeatRef(explicitRef) ?? explicitRef;
|
|
464
528
|
const activeEntries = await fs.readdir(paths.activeDir, { withFileTypes: true }).catch(() => []);
|
|
465
529
|
const activeFeatures = activeEntries
|
|
466
530
|
.filter((entry) => entry.isDirectory() && /^FEAT-\d{4}$/.test(entry.name))
|
|
@@ -483,13 +547,13 @@ async function inferFinalizeTarget(paths, pending, explicitRef, cwd) {
|
|
|
483
547
|
}
|
|
484
548
|
return undefined;
|
|
485
549
|
}
|
|
486
|
-
function isPrivacyComplianceFeature(feature) {
|
|
550
|
+
export function isPrivacyComplianceFeature(feature) {
|
|
487
551
|
if (feature.origin_ref === 'EPIC-0020' || feature.origin_ref === 'EPIC-0021')
|
|
488
552
|
return true;
|
|
489
553
|
const refs = new Set(feature.acceptance_refs || []);
|
|
490
554
|
return refs.has('EPIC-0020') || refs.has('EPIC-0021') || refs.has('DEB-0021') || refs.has('INS-0021');
|
|
491
555
|
}
|
|
492
|
-
async function evaluatePrivacyFinalizeGuardrail(paths, config, feature) {
|
|
556
|
+
export async function evaluatePrivacyFinalizeGuardrail(paths, config, feature) {
|
|
493
557
|
if (!isPrivacyComplianceFeature(feature)) {
|
|
494
558
|
return { ok: true, reasons: [] };
|
|
495
559
|
}
|
|
@@ -576,7 +640,46 @@ async function evaluatePrivacyFinalizeGuardrail(paths, config, feature) {
|
|
|
576
640
|
}
|
|
577
641
|
return { ok: reasons.length === 0, reasons };
|
|
578
642
|
}
|
|
579
|
-
async function
|
|
643
|
+
export async function evaluateDevTrackApiPolicyGates(paths, config, feature) {
|
|
644
|
+
const activePath = path.join(paths.activeDir, feature.id);
|
|
645
|
+
const names = activeDocNamesForLayout(config);
|
|
646
|
+
const qualityPath = path.join(activePath, names.quality);
|
|
647
|
+
const qualityContent = await fs.readFile(qualityPath, 'utf-8').catch(() => '');
|
|
648
|
+
if (!qualityContent.trim()) {
|
|
649
|
+
return { ok: true, reasons: [] };
|
|
650
|
+
}
|
|
651
|
+
let quality;
|
|
652
|
+
try {
|
|
653
|
+
quality = parseWorkspaceYamlDocument('5-quality.yaml', qualityContent);
|
|
654
|
+
}
|
|
655
|
+
catch {
|
|
656
|
+
return { ok: true, reasons: [] };
|
|
657
|
+
}
|
|
658
|
+
const reasons = evaluateDevTrackApiPolicyEvidence(quality);
|
|
659
|
+
return { ok: reasons.length === 0, reasons };
|
|
660
|
+
}
|
|
661
|
+
export function evaluateDevTrackApiPolicyEvidence(quality) {
|
|
662
|
+
const policyRequirements = [
|
|
663
|
+
...(quality.skill_evidence?.policy_requirements ?? []),
|
|
664
|
+
...(quality.policy_injection?.required_policies ?? []),
|
|
665
|
+
];
|
|
666
|
+
if (policyRequirements.length === 0) {
|
|
667
|
+
return [];
|
|
668
|
+
}
|
|
669
|
+
const evidenceEntries = quality.skill_evidence?.evidence ?? [];
|
|
670
|
+
const hasDevTrackApiEvidence = evidenceEntries.some((entry) => entry.skill_id === 'devtrack-api');
|
|
671
|
+
const reasons = new Set();
|
|
672
|
+
for (const policy of policyRequirements) {
|
|
673
|
+
if (policy.skill_id !== 'devtrack-api' || policy.enforcement !== 'blocking') {
|
|
674
|
+
continue;
|
|
675
|
+
}
|
|
676
|
+
if (!hasDevTrackApiEvidence) {
|
|
677
|
+
reasons.add(`devtrack-api policy gate: blocking skill "devtrack-api" lacks evidence in skill_evidence (required rules: ${policy.required_rule_refs.join(', ')})`);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
return Array.from(reasons);
|
|
681
|
+
}
|
|
682
|
+
export async function evaluateWorkspaceQualityFeedback(paths, config, feature) {
|
|
580
683
|
const activePath = path.join(paths.activeDir, feature.id);
|
|
581
684
|
const names = activeDocNamesForLayout(config);
|
|
582
685
|
const artifactName = [names.quality, ...activeDocCandidateNames(config)].find((name) => name === '5-quality.yaml' && existsSync(path.join(activePath, name))) || '';
|
|
@@ -598,9 +701,13 @@ async function evaluateWorkspaceQualityFeedback(paths, config, feature) {
|
|
|
598
701
|
}
|
|
599
702
|
const missingTargets = missingCoverageTargets(document);
|
|
600
703
|
const missingSkills = missingSkillEvidence(document);
|
|
704
|
+
const matrixReasons = evaluateQualityWitnessMatrix(document);
|
|
601
705
|
const reasons = [];
|
|
602
706
|
const maxRounds = document.remediation_policy.max_rounds;
|
|
603
707
|
const rounds = countQualityEvidenceRounds(document);
|
|
708
|
+
const q95Ledger = await computeQ95Ledger(paths, config, feature, document);
|
|
709
|
+
document.q95_ledger = q95Ledger;
|
|
710
|
+
await fs.writeFile(artifactPath, stringifyWorkspaceYamlDocument('5-quality.yaml', document), 'utf-8');
|
|
604
711
|
if (rounds > maxRounds && document.exceptions.length === 0) {
|
|
605
712
|
reasons.push(`quality remediation max_rounds exceeded (${rounds}/${maxRounds}); formal exception required in ${artifact}`);
|
|
606
713
|
}
|
|
@@ -621,9 +728,15 @@ async function evaluateWorkspaceQualityFeedback(paths, config, feature) {
|
|
|
621
728
|
if (missingSkills.length > 0) {
|
|
622
729
|
reasons.push(`required skill evidence missing for ${missingSkills.join(', ')}`);
|
|
623
730
|
}
|
|
731
|
+
if (matrixReasons.length > 0) {
|
|
732
|
+
reasons.push(`quality-witness matrix blocked finalize: ${matrixReasons.join(' | ')}`);
|
|
733
|
+
}
|
|
734
|
+
if (q95Ledger.score < q95Ledger.threshold && document.exceptions.length === 0) {
|
|
735
|
+
reasons.push(`Q95 composite score below threshold (${q95Ledger.score.toFixed(2)} < ${q95Ledger.threshold}); next best action: ${q95Ledger.next_best_action}`);
|
|
736
|
+
}
|
|
624
737
|
return { ok: reasons.length === 0, reasons, artifact };
|
|
625
738
|
}
|
|
626
|
-
async function evaluateWorkspaceTraceabilityFeedback(paths, config, feature) {
|
|
739
|
+
export async function evaluateWorkspaceTraceabilityFeedback(paths, config, feature) {
|
|
627
740
|
const activePath = path.join(paths.activeDir, feature.id);
|
|
628
741
|
const names = activeDocNamesForLayout(config);
|
|
629
742
|
const specPath = path.join(activePath, names.spec);
|
|
@@ -666,14 +779,362 @@ async function evaluateWorkspaceTraceabilityFeedback(paths, config, feature) {
|
|
|
666
779
|
artifact,
|
|
667
780
|
};
|
|
668
781
|
}
|
|
669
|
-
function
|
|
782
|
+
export async function computeQ95Ledger(paths, config, feature, document) {
|
|
783
|
+
const threshold = document.q95_ledger?.threshold ?? 95;
|
|
784
|
+
const weights = {
|
|
785
|
+
coverage: document.q95_ledger?.weights?.coverage ?? 30,
|
|
786
|
+
traceability: document.q95_ledger?.weights?.traceability ?? 20,
|
|
787
|
+
integrity: document.q95_ledger?.weights?.integrity ?? 20,
|
|
788
|
+
naming: document.q95_ledger?.weights?.naming ?? 10,
|
|
789
|
+
token: document.q95_ledger?.weights?.token ?? 20,
|
|
790
|
+
};
|
|
791
|
+
const coverage = computeCoverageAxis(document);
|
|
792
|
+
const traceability = await computeTraceabilityAxis(paths, config, feature, document);
|
|
793
|
+
const integrity = computeIntegrityAxis(document);
|
|
794
|
+
const naming = computeNamingAxis(feature, document);
|
|
795
|
+
const token = computeTokenAxis(document);
|
|
796
|
+
const axes = {
|
|
797
|
+
coverage: weightedAxis(coverage, weights.coverage),
|
|
798
|
+
traceability: weightedAxis(traceability, weights.traceability),
|
|
799
|
+
integrity: weightedAxis(integrity, weights.integrity),
|
|
800
|
+
naming: weightedAxis(naming, weights.naming),
|
|
801
|
+
token: weightedAxis(token, weights.token),
|
|
802
|
+
};
|
|
803
|
+
const score = round2(axes.coverage.weighted_score +
|
|
804
|
+
axes.traceability.weighted_score +
|
|
805
|
+
axes.integrity.weighted_score +
|
|
806
|
+
axes.naming.weighted_score +
|
|
807
|
+
axes.token.weighted_score);
|
|
808
|
+
const status = score >= threshold ? 'pass' : 'fail';
|
|
809
|
+
return {
|
|
810
|
+
threshold,
|
|
811
|
+
score,
|
|
812
|
+
status,
|
|
813
|
+
computed_at: nowIso(),
|
|
814
|
+
weights,
|
|
815
|
+
axes,
|
|
816
|
+
next_best_action: inferQ95NextAction(axes),
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
export function weightedAxis(input, weight) {
|
|
820
|
+
return {
|
|
821
|
+
raw_score: clampPercent(input.raw_score),
|
|
822
|
+
weighted_score: round2((clampPercent(input.raw_score) * weight) / 100),
|
|
823
|
+
rationale: input.rationale,
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
export function computeCoverageAxis(document) {
|
|
827
|
+
const unit = extractCoveragePerformance(document, 'unit');
|
|
828
|
+
const integration = extractCoveragePerformance(document, 'integration');
|
|
829
|
+
const raw = round2((unit + integration) / 2);
|
|
830
|
+
return {
|
|
831
|
+
raw_score: raw,
|
|
832
|
+
rationale: `unit=${unit.toFixed(2)} integration=${integration.toFixed(2)} derived from evidence_log`,
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
export async function computeTraceabilityAxis(paths, config, feature, document) {
|
|
836
|
+
if ((document.traceability?.requirements?.length ?? 0) === 0) {
|
|
837
|
+
return {
|
|
838
|
+
raw_score: 100,
|
|
839
|
+
rationale: 'no requirement matrix declared yet; dedicated traceability guardrail remains authoritative',
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
const activePath = path.join(paths.activeDir, feature.id);
|
|
843
|
+
const names = activeDocNamesForLayout(config);
|
|
844
|
+
const specPath = path.join(activePath, names.spec);
|
|
845
|
+
const changelogPath = path.join(activePath, names.changelog);
|
|
846
|
+
const [specContent, changelogContent] = await Promise.all([
|
|
847
|
+
fs.readFile(specPath, 'utf-8').catch(() => ''),
|
|
848
|
+
fs.readFile(changelogPath, 'utf-8').catch(() => ''),
|
|
849
|
+
]);
|
|
850
|
+
if (!specContent.trim() || !changelogContent.trim()) {
|
|
851
|
+
return {
|
|
852
|
+
raw_score: 70,
|
|
853
|
+
rationale: 'traceability matrix exists but spec/changelog evidence is unavailable for Q95 composite scoring',
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
try {
|
|
857
|
+
const spec = parseWorkspaceYamlDocument('1-spec.yaml', specContent);
|
|
858
|
+
const changelog = parseWorkspaceYamlDocument('4-changelog.yaml', changelogContent);
|
|
859
|
+
const result = evaluateWorkspaceTraceability(paths.projectRoot, spec, changelog, document);
|
|
860
|
+
return result.ok
|
|
861
|
+
? { raw_score: 100, rationale: 'traceability closure validated successfully' }
|
|
862
|
+
: { raw_score: 70, rationale: result.reasons.join(' | ') };
|
|
863
|
+
}
|
|
864
|
+
catch (error) {
|
|
865
|
+
return {
|
|
866
|
+
raw_score: 70,
|
|
867
|
+
rationale: `traceability parse error: ${error.message}`,
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
export function computeIntegrityAxis(document) {
|
|
872
|
+
const requiredSkillIds = Array.from(new Set((document.skill_evidence?.required_skill_ids ?? []).map((value) => value.trim()).filter(Boolean)));
|
|
873
|
+
const providedSkills = new Set((document.skill_evidence?.evidence ?? [])
|
|
874
|
+
.map((entry) => entry.skill_id.trim().toLowerCase())
|
|
875
|
+
.filter(Boolean));
|
|
876
|
+
const coveredSkills = requiredSkillIds.filter((id) => providedSkills.has(id.toLowerCase())).length;
|
|
877
|
+
const skillScore = requiredSkillIds.length === 0 ? 100 : round2((coveredSkills / requiredSkillIds.length) * 100);
|
|
878
|
+
const security = document.security_integrity;
|
|
879
|
+
const securityScore = !security
|
|
880
|
+
? 100
|
|
881
|
+
: round2([
|
|
882
|
+
integritySignalScore(security.endpoint_auth_review),
|
|
883
|
+
integritySignalScore(security.sensitive_data_exposure_review),
|
|
884
|
+
integritySignalScore(security.incident_response_review),
|
|
885
|
+
].reduce((acc, value) => acc + value, 0) / 3);
|
|
886
|
+
const runtimeQuality = computeRuntimeQualityAxis(document);
|
|
887
|
+
const raw = round2(skillScore * 0.55 + securityScore * 0.25 + runtimeQuality.raw_score * 0.2);
|
|
888
|
+
return {
|
|
889
|
+
raw_score: raw,
|
|
890
|
+
rationale: `skill_evidence=${skillScore.toFixed(2)} security_integrity=${securityScore.toFixed(2)} runtime_quality=${runtimeQuality.raw_score.toFixed(2)} (${runtimeQuality.rationale})`,
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
export function computeRuntimeQualityAxis(document) {
|
|
894
|
+
const gates = document.runtime_quality_gates;
|
|
895
|
+
const performance = gates?.performance ?? [];
|
|
896
|
+
const flakiness = gates?.flakiness ?? [];
|
|
897
|
+
const entries = [
|
|
898
|
+
...performance.map((entry) => ({ kind: 'performance', gate: entry.gate, evidence: entry.evidence_ref, score: runtimePerformanceScore(entry) })),
|
|
899
|
+
...flakiness.map((entry) => ({ kind: 'flakiness', gate: entry.gate, evidence: entry.evidence_ref, score: runtimeFlakinessScore(entry) })),
|
|
900
|
+
];
|
|
901
|
+
if (entries.length === 0) {
|
|
902
|
+
return {
|
|
903
|
+
raw_score: 100,
|
|
904
|
+
rationale: 'runtime quality telemetry not provided; neutral score applied',
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
const scores = entries.map((entry) => {
|
|
908
|
+
if (entry.gate === 'fail')
|
|
909
|
+
return Math.min(entry.score, 70);
|
|
910
|
+
if (entry.gate === 'warn')
|
|
911
|
+
return Math.min(entry.score, 85);
|
|
912
|
+
return entry.score;
|
|
913
|
+
});
|
|
914
|
+
const worst = Math.min(...scores);
|
|
915
|
+
const failed = entries.filter((entry) => entry.gate === 'fail').map((entry) => `${entry.kind}:${entry.evidence ?? 'runtime evidence'}`);
|
|
916
|
+
const warned = entries.filter((entry) => entry.gate === 'warn').map((entry) => `${entry.kind}:${entry.evidence ?? 'runtime evidence'}`);
|
|
917
|
+
const notes = [...failed.map((value) => `${value} failed`), ...warned.map((value) => `${value} warned`)];
|
|
918
|
+
return {
|
|
919
|
+
raw_score: worst,
|
|
920
|
+
rationale: `runtime quality gate scored ${worst.toFixed(2)}${notes.length > 0 ? `; ${notes.join('; ')}` : ''}`,
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
function runtimePerformanceScore(entry) {
|
|
924
|
+
if (entry.actual !== undefined && entry.threshold !== undefined && entry.threshold > 0) {
|
|
925
|
+
return clampPercent((entry.threshold / Math.max(entry.actual, 1)) * 100);
|
|
926
|
+
}
|
|
927
|
+
return entry.gate === 'pass' ? 100 : entry.gate === 'warn' ? 85 : 70;
|
|
928
|
+
}
|
|
929
|
+
function runtimeFlakinessScore(entry) {
|
|
930
|
+
let failureRate = entry.failure_rate_percent;
|
|
931
|
+
if (failureRate === undefined && entry.attempts !== undefined && entry.failures !== undefined && entry.attempts > 0) {
|
|
932
|
+
failureRate = clampPercent((entry.failures / entry.attempts) * 100);
|
|
933
|
+
}
|
|
934
|
+
if (failureRate === undefined) {
|
|
935
|
+
return entry.gate === 'pass' ? 100 : entry.gate === 'warn' ? 85 : 70;
|
|
936
|
+
}
|
|
937
|
+
return clampPercent(100 - failureRate);
|
|
938
|
+
}
|
|
939
|
+
const LEGACY_NAMING_PATTERNS = [
|
|
940
|
+
/\bcoachsdd\b/u,
|
|
941
|
+
/\bcoach\s+sdd\b/u,
|
|
942
|
+
/\bcoach-sdd\b/u,
|
|
943
|
+
/\bcoach_sdd\b/u,
|
|
944
|
+
/\bsdd\s+coach\b/u,
|
|
945
|
+
/\.codesdd\b/u,
|
|
946
|
+
];
|
|
947
|
+
export function computeNamingAxis(feature, document) {
|
|
948
|
+
let score = 100;
|
|
949
|
+
const rationale = [];
|
|
950
|
+
if (!/^FEAT-\d{4,}$/.test(feature.id)) {
|
|
951
|
+
score -= 40;
|
|
952
|
+
rationale.push('feature id not canonical FEAT-####');
|
|
953
|
+
}
|
|
954
|
+
if (!feature.title || feature.title.trim().length < 10) {
|
|
955
|
+
score -= 30;
|
|
956
|
+
rationale.push('feature title too short');
|
|
957
|
+
}
|
|
958
|
+
const combined = JSON.stringify({ title: feature.title || '', quality: document }).toLowerCase();
|
|
959
|
+
if (LEGACY_NAMING_PATTERNS.some((pattern) => pattern.test(combined))) {
|
|
960
|
+
score -= 30;
|
|
961
|
+
rationale.push('legacy naming detected in active quality context by naming-contract forbidden terms');
|
|
962
|
+
}
|
|
963
|
+
return {
|
|
964
|
+
raw_score: clampPercent(score),
|
|
965
|
+
rationale: rationale.length > 0 ? rationale.join('; ') : 'canonical naming and state metadata checks passed',
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
export function computeTokenAxis(document) {
|
|
969
|
+
const structuredTelemetry = document.token_budget_gates?.telemetry ?? [];
|
|
970
|
+
if (structuredTelemetry.length > 0) {
|
|
971
|
+
return computeStructuredTokenAxis(document);
|
|
972
|
+
}
|
|
973
|
+
const tokenEntries = document.evidence_log.filter((entry) => {
|
|
974
|
+
const label = `${entry.kind} ${entry.result}`.toLowerCase();
|
|
975
|
+
return label.includes('token');
|
|
976
|
+
});
|
|
977
|
+
if (tokenEntries.length === 0) {
|
|
978
|
+
return {
|
|
979
|
+
raw_score: 100,
|
|
980
|
+
rationale: 'token telemetry not provided; neutral score applied until FEAT token harness evidence is recorded',
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
const percents = [];
|
|
984
|
+
for (const entry of tokenEntries) {
|
|
985
|
+
const matches = entry.result.match(/(\d+(?:\.\d+)?)\s*%/g) ?? [];
|
|
986
|
+
for (const match of matches) {
|
|
987
|
+
const value = Number(match.replace('%', '').trim());
|
|
988
|
+
if (Number.isFinite(value)) {
|
|
989
|
+
percents.push(clampPercent(value));
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
if (percents.length > 0) {
|
|
994
|
+
const best = Math.max(...percents);
|
|
995
|
+
return {
|
|
996
|
+
raw_score: best,
|
|
997
|
+
rationale: `token efficiency evidence detected (${best.toFixed(2)}%)`,
|
|
998
|
+
};
|
|
999
|
+
}
|
|
1000
|
+
return {
|
|
1001
|
+
raw_score: 70,
|
|
1002
|
+
rationale: 'token evidence found but no numeric percentage; conservative score applied',
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
export function computeStructuredTokenAxis(document) {
|
|
1006
|
+
const gates = document.token_budget_gates;
|
|
1007
|
+
const telemetry = gates?.telemetry ?? [];
|
|
1008
|
+
const requireNumeric = gates?.require_numeric_efficiency ?? true;
|
|
1009
|
+
const failBelow = gates?.fail_below_percent ?? 95;
|
|
1010
|
+
const scores = [];
|
|
1011
|
+
const reasons = [];
|
|
1012
|
+
let hardFailure = false;
|
|
1013
|
+
for (const entry of telemetry) {
|
|
1014
|
+
let score = entry.efficiency_percent;
|
|
1015
|
+
if (score === undefined && entry.budget_chars !== undefined && entry.actual_chars !== undefined) {
|
|
1016
|
+
score = entry.actual_chars === 0 ? 100 : clampPercent((entry.budget_chars / entry.actual_chars) * 100);
|
|
1017
|
+
}
|
|
1018
|
+
if (score === undefined && entry.actual_chars !== undefined && gates?.max_context_chars !== undefined) {
|
|
1019
|
+
score = entry.actual_chars === 0 ? 100 : clampPercent((gates.max_context_chars / entry.actual_chars) * 100);
|
|
1020
|
+
}
|
|
1021
|
+
if (score === undefined) {
|
|
1022
|
+
if (requireNumeric) {
|
|
1023
|
+
scores.push(70);
|
|
1024
|
+
reasons.push(`${entry.evidence_ref ?? 'token telemetry'} lacks numeric efficiency`);
|
|
1025
|
+
}
|
|
1026
|
+
continue;
|
|
1027
|
+
}
|
|
1028
|
+
const normalized = clampPercent(score);
|
|
1029
|
+
scores.push(normalized);
|
|
1030
|
+
if (entry.gate === 'fail' || normalized < failBelow) {
|
|
1031
|
+
hardFailure = true;
|
|
1032
|
+
reasons.push(`${entry.evidence_ref ?? 'token telemetry'} below ${failBelow}%`);
|
|
1033
|
+
}
|
|
1034
|
+
else if (entry.gate === 'warn') {
|
|
1035
|
+
reasons.push(`${entry.evidence_ref ?? 'token telemetry'} warned at ${normalized.toFixed(2)}%`);
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
if (scores.length === 0) {
|
|
1039
|
+
return {
|
|
1040
|
+
raw_score: requireNumeric ? 70 : 100,
|
|
1041
|
+
rationale: requireNumeric
|
|
1042
|
+
? 'token budget gate requires numeric efficiency but no numeric telemetry was provided'
|
|
1043
|
+
: 'token budget telemetry provided without numeric scoring requirement',
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
const worst = Math.min(...scores);
|
|
1047
|
+
if (hardFailure) {
|
|
1048
|
+
return {
|
|
1049
|
+
raw_score: Math.min(worst, 70),
|
|
1050
|
+
rationale: `token budget gate failed: ${reasons.join('; ')}`,
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1053
|
+
return {
|
|
1054
|
+
raw_score: worst,
|
|
1055
|
+
rationale: `token budget gate satisfied (${worst.toFixed(2)}% worst-case efficiency)${reasons.length > 0 ? `; ${reasons.join('; ')}` : ''}`,
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
export function extractCoveragePerformance(document, kind) {
|
|
1059
|
+
const target = kind === 'unit' ? document.coverage_targets.unit : document.coverage_targets.integration;
|
|
1060
|
+
const relevant = document.evidence_log.filter((entry) => {
|
|
1061
|
+
const evidenceKind = entry.kind.toLowerCase();
|
|
1062
|
+
return (evidenceKind.includes(kind) ||
|
|
1063
|
+
evidenceKind.includes('coverage') ||
|
|
1064
|
+
(kind === 'integration' && evidenceKind.includes('e2e')));
|
|
1065
|
+
});
|
|
1066
|
+
if (relevant.length === 0)
|
|
1067
|
+
return 0;
|
|
1068
|
+
const values = relevant.flatMap((entry) => {
|
|
1069
|
+
const matches = entry.result.match(/\d+(?:\.\d+)?/g) ?? [];
|
|
1070
|
+
return matches.map(Number).filter((value) => Number.isFinite(value) && value >= 0 && value <= 100);
|
|
1071
|
+
});
|
|
1072
|
+
if (values.length === 0)
|
|
1073
|
+
return 0;
|
|
1074
|
+
const best = Math.max(...values);
|
|
1075
|
+
if (target <= 0)
|
|
1076
|
+
return 100;
|
|
1077
|
+
return clampPercent((best / target) * 100);
|
|
1078
|
+
}
|
|
1079
|
+
export function integritySignalScore(value) {
|
|
1080
|
+
if (value === 'passed' || value === 'not_applicable')
|
|
1081
|
+
return 100;
|
|
1082
|
+
if (value === 'pending')
|
|
1083
|
+
return 50;
|
|
1084
|
+
return 0;
|
|
1085
|
+
}
|
|
1086
|
+
export function inferQ95NextAction(axes) {
|
|
1087
|
+
const ranking = [
|
|
1088
|
+
{ axis: 'coverage', weightedGap: 30 - axes.coverage.weighted_score, rawGap: 100 - axes.coverage.raw_score },
|
|
1089
|
+
{
|
|
1090
|
+
axis: 'traceability',
|
|
1091
|
+
weightedGap: 20 - axes.traceability.weighted_score,
|
|
1092
|
+
rawGap: 100 - axes.traceability.raw_score,
|
|
1093
|
+
},
|
|
1094
|
+
{ axis: 'integrity', weightedGap: 20 - axes.integrity.weighted_score, rawGap: 100 - axes.integrity.raw_score },
|
|
1095
|
+
{ axis: 'naming', weightedGap: 10 - axes.naming.weighted_score, rawGap: 100 - axes.naming.raw_score },
|
|
1096
|
+
{ axis: 'token', weightedGap: 20 - axes.token.weighted_score, rawGap: 100 - axes.token.raw_score },
|
|
1097
|
+
];
|
|
1098
|
+
ranking.sort((a, b) => b.weightedGap - a.weightedGap || b.rawGap - a.rawGap);
|
|
1099
|
+
const primary = ranking[0];
|
|
1100
|
+
if (!primary || primary.weightedGap <= 0) {
|
|
1101
|
+
return 'Q95 satisfied. Keep evidence_log and traceability synchronized for future rounds.';
|
|
1102
|
+
}
|
|
1103
|
+
switch (primary.axis) {
|
|
1104
|
+
case 'coverage':
|
|
1105
|
+
return 'Add or update unit/integration evidence entries with numeric percentages that meet coverage_targets.';
|
|
1106
|
+
case 'traceability':
|
|
1107
|
+
return 'Complete traceability.spec_anchor and traceability.requirements links across spec, changelog, code, and tests.';
|
|
1108
|
+
case 'integrity':
|
|
1109
|
+
return 'Record required skill evidence and close pending security_integrity reviews when applicable.';
|
|
1110
|
+
case 'naming':
|
|
1111
|
+
return 'Fix canonical naming drift in FEAT metadata and avoid legacy governance aliases in quality artifacts.';
|
|
1112
|
+
case 'token':
|
|
1113
|
+
return 'Record token efficiency telemetry (percentage) in evidence_log to score the token axis explicitly.';
|
|
1114
|
+
default:
|
|
1115
|
+
return 'Record missing evidence to raise the lowest weighted axis.';
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
export function clampPercent(value) {
|
|
1119
|
+
if (!Number.isFinite(value))
|
|
1120
|
+
return 0;
|
|
1121
|
+
if (value < 0)
|
|
1122
|
+
return 0;
|
|
1123
|
+
if (value > 100)
|
|
1124
|
+
return 100;
|
|
1125
|
+
return value;
|
|
1126
|
+
}
|
|
1127
|
+
export function round2(value) {
|
|
1128
|
+
return Math.round(value * 100) / 100;
|
|
1129
|
+
}
|
|
1130
|
+
export function missingCoverageTargets(document) {
|
|
670
1131
|
const targets = [
|
|
671
1132
|
{ kind: 'unit', target: document.coverage_targets.unit },
|
|
672
1133
|
{ kind: 'integration', target: document.coverage_targets.integration },
|
|
673
1134
|
];
|
|
674
1135
|
return targets.filter((target) => !coverageTargetMet(document, target.kind, target.target));
|
|
675
1136
|
}
|
|
676
|
-
function coverageTargetMet(document, kind, target) {
|
|
1137
|
+
export function coverageTargetMet(document, kind, target) {
|
|
677
1138
|
return document.evidence_log.some((entry) => {
|
|
678
1139
|
const evidenceKind = entry.kind.toLowerCase();
|
|
679
1140
|
const result = entry.result.toLowerCase();
|
|
@@ -687,14 +1148,14 @@ function coverageTargetMet(document, kind, target) {
|
|
|
687
1148
|
return percentages.some((value) => value >= target);
|
|
688
1149
|
});
|
|
689
1150
|
}
|
|
690
|
-
function countQualityEvidenceRounds(document) {
|
|
1151
|
+
export function countQualityEvidenceRounds(document) {
|
|
691
1152
|
const roundKinds = ['round', 'unit', 'integration', 'e2e', 'coverage', 'validation', 'test', 'build'];
|
|
692
1153
|
return document.evidence_log.filter((entry) => {
|
|
693
1154
|
const kind = entry.kind.toLowerCase();
|
|
694
1155
|
return roundKinds.some((token) => kind.includes(token));
|
|
695
1156
|
}).length;
|
|
696
1157
|
}
|
|
697
|
-
function missingSkillEvidence(document) {
|
|
1158
|
+
export function missingSkillEvidence(document) {
|
|
698
1159
|
const required = Array.from(new Set((document.skill_evidence?.required_skill_ids ?? []).map((value) => value.trim()).filter(Boolean)));
|
|
699
1160
|
if (required.length === 0) {
|
|
700
1161
|
return [];
|