@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,10 +1,48 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { existsSync, promises as fs } from "node:fs";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
3
4
|
import { parse } from "yaml";
|
|
5
|
+
import { createProjectFingerprint, ensureGlobalCacheLayout } from "../../global-config.js";
|
|
4
6
|
import { CLI_NAME } from "../../branding.js";
|
|
5
|
-
import { loadStateSnapshot } from "../state.js";
|
|
7
|
+
import { loadStateSnapshot, nowIso } from "../state.js";
|
|
6
8
|
import { bundlesForSkills, getRuntime, relProjectPath, coreDocRef, planningDocRef, featureActiveDir, activeDocNamesForLayout, unresolvedDependencies, lockConflictWithActive, featureReadiness, gateSatisfied, detectContextType, pathExists, listAdrRefs } from "../legacy-operations.js";
|
|
9
|
+
import { normalizeSddEntityRef } from "../entity-reference.js";
|
|
7
10
|
import { workspacePlanSchema, workspaceSpecSchema } from "../workspace-schemas.js";
|
|
11
|
+
export const CONTEXT_PACK_BUDGET_MODES = ['compact', 'standard', 'full'];
|
|
12
|
+
const UNLIMITED = Number.POSITIVE_INFINITY;
|
|
13
|
+
const CONTEXT_PACK_CACHE_SCHEMA_VERSION = 2;
|
|
14
|
+
const CONTEXT_PACK_BUDGET_PROFILES = {
|
|
15
|
+
compact: {
|
|
16
|
+
maxReadOrder: 8,
|
|
17
|
+
maxCoreDocs: 6,
|
|
18
|
+
maxRelevantContracts: 40,
|
|
19
|
+
maxRelevantServices: 5,
|
|
20
|
+
maxServiceContracts: 12,
|
|
21
|
+
maxPredecessorOutputs: 5,
|
|
22
|
+
maxRelatedFeatures: 50,
|
|
23
|
+
maxRelatedDebates: 20,
|
|
24
|
+
},
|
|
25
|
+
standard: {
|
|
26
|
+
maxReadOrder: UNLIMITED,
|
|
27
|
+
maxCoreDocs: UNLIMITED,
|
|
28
|
+
maxRelevantContracts: UNLIMITED,
|
|
29
|
+
maxRelevantServices: UNLIMITED,
|
|
30
|
+
maxServiceContracts: UNLIMITED,
|
|
31
|
+
maxPredecessorOutputs: UNLIMITED,
|
|
32
|
+
maxRelatedFeatures: UNLIMITED,
|
|
33
|
+
maxRelatedDebates: UNLIMITED,
|
|
34
|
+
},
|
|
35
|
+
full: {
|
|
36
|
+
maxReadOrder: UNLIMITED,
|
|
37
|
+
maxCoreDocs: UNLIMITED,
|
|
38
|
+
maxRelevantContracts: UNLIMITED,
|
|
39
|
+
maxRelevantServices: UNLIMITED,
|
|
40
|
+
maxServiceContracts: UNLIMITED,
|
|
41
|
+
maxPredecessorOutputs: UNLIMITED,
|
|
42
|
+
maxRelatedFeatures: UNLIMITED,
|
|
43
|
+
maxRelatedDebates: UNLIMITED,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
8
46
|
async function readYamlWorkspaceDoc(filePath, schema) {
|
|
9
47
|
const content = await fs.readFile(filePath, 'utf-8').catch(() => '');
|
|
10
48
|
if (!content.trim())
|
|
@@ -59,13 +97,25 @@ export class ContextService {
|
|
|
59
97
|
constructor(stores) {
|
|
60
98
|
this.stores = stores;
|
|
61
99
|
}
|
|
62
|
-
async execute(projectRoot, ref) {
|
|
100
|
+
async execute(projectRoot, ref, options = {}) {
|
|
101
|
+
const budgetMode = options.budgetMode ?? 'standard';
|
|
63
102
|
const { config, paths } = await getRuntime(projectRoot);
|
|
64
103
|
const snapshot = await loadStateSnapshot(paths, config);
|
|
65
|
-
const
|
|
104
|
+
const normalizedRef = normalizeSddEntityRef(ref);
|
|
105
|
+
const type = detectContextType(normalizedRef);
|
|
66
106
|
if (!type) {
|
|
67
107
|
throw new Error(`Referencia ${ref} invalida. Use FEAT/EPIC/RAD/FGAP/TD.`);
|
|
68
108
|
}
|
|
109
|
+
const cacheRef = await buildContextPackCacheRef(paths, normalizedRef, type, budgetMode);
|
|
110
|
+
const cached = await readContextPackCache(cacheRef);
|
|
111
|
+
if (cached) {
|
|
112
|
+
return attachContextPackCacheMetadata(cached, cacheRef, true);
|
|
113
|
+
}
|
|
114
|
+
const finishContextPack = async (context) => {
|
|
115
|
+
const budgeted = applyContextPackBudget(context, budgetMode);
|
|
116
|
+
await writeContextPackCache(cacheRef, budgeted);
|
|
117
|
+
return attachContextPackCacheMetadata(budgeted, cacheRef, false);
|
|
118
|
+
};
|
|
69
119
|
const coreDocs = [
|
|
70
120
|
coreDocRef(paths, 'index.md'),
|
|
71
121
|
coreDocRef(paths, 'arquitetura.md'),
|
|
@@ -80,9 +130,9 @@ export class ContextService {
|
|
|
80
130
|
coreDocs.push(planningDocRef(paths, 'frontend-auditoria.md'));
|
|
81
131
|
}
|
|
82
132
|
if (type === 'FEAT') {
|
|
83
|
-
const item = snapshot.backlog.items.find((entry) => entry.id ===
|
|
133
|
+
const item = snapshot.backlog.items.find((entry) => entry.id === normalizedRef);
|
|
84
134
|
if (!item)
|
|
85
|
-
throw new Error(`Feature ${
|
|
135
|
+
throw new Error(`Feature ${normalizedRef} nao encontrada.`);
|
|
86
136
|
const unresolved = unresolvedDependencies(item, snapshot.backlog.items);
|
|
87
137
|
const lockConflicts = lockConflictWithActive(item, snapshot.backlog.items);
|
|
88
138
|
const predecessorOutputs = item.blocked_by
|
|
@@ -145,9 +195,9 @@ export class ContextService {
|
|
|
145
195
|
? relProjectPath(paths, path.join(paths.projectRoot, workspace.workspace_path, docNames.quality))
|
|
146
196
|
: '';
|
|
147
197
|
const requiredSkillEvidence = item.recommended_skills.filter(Boolean);
|
|
148
|
-
return {
|
|
198
|
+
return finishContextPack({
|
|
149
199
|
sdd_pack_version: 1,
|
|
150
|
-
target_id:
|
|
200
|
+
target_id: normalizedRef,
|
|
151
201
|
target_type: type,
|
|
152
202
|
summary: `${item.title} [${item.status}]`,
|
|
153
203
|
objective: workspace.spec?.objective || item.summary || '',
|
|
@@ -166,7 +216,7 @@ export class ContextService {
|
|
|
166
216
|
origin: { type: item.origin_type, ref: item.origin_ref || '' },
|
|
167
217
|
related_discovery: item.origin_ref
|
|
168
218
|
? snapshot.discoveryIndex.records
|
|
169
|
-
.filter((record) => record.id === item.origin_ref || record.related_ids.includes(
|
|
219
|
+
.filter((record) => record.id === item.origin_ref || record.related_ids.includes(normalizedRef))
|
|
170
220
|
.map((record) => record.id)
|
|
171
221
|
: [],
|
|
172
222
|
related_gaps: item.frontend_gap_refs,
|
|
@@ -210,22 +260,22 @@ export class ContextService {
|
|
|
210
260
|
lock_conflicts_with: lockConflicts,
|
|
211
261
|
readiness: featureReadiness(item, snapshot.backlog.items),
|
|
212
262
|
core_docs: coreDocs,
|
|
213
|
-
};
|
|
263
|
+
});
|
|
214
264
|
}
|
|
215
265
|
if (type === 'RAD' || type === 'EPIC') {
|
|
216
|
-
const radar = snapshot.discoveryIndex.records.find((record) => record.id ===
|
|
266
|
+
const radar = snapshot.discoveryIndex.records.find((record) => record.id === normalizedRef && (record.type === 'RAD' || record.type === 'EPIC'));
|
|
217
267
|
if (!radar)
|
|
218
|
-
throw new Error(`Epic/Radar ${
|
|
219
|
-
const relatedFeatures = snapshot.backlog.items.filter((item) => (item.origin_type === 'radar' || item.origin_type === 'epic') && item.origin_ref ===
|
|
220
|
-
return {
|
|
268
|
+
throw new Error(`Epic/Radar ${normalizedRef} nao encontrado.`);
|
|
269
|
+
const relatedFeatures = snapshot.backlog.items.filter((item) => (item.origin_type === 'radar' || item.origin_type === 'epic') && item.origin_ref === normalizedRef);
|
|
270
|
+
return finishContextPack({
|
|
221
271
|
sdd_pack_version: 1,
|
|
222
|
-
target_id:
|
|
272
|
+
target_id: normalizedRef,
|
|
223
273
|
target_type: type,
|
|
224
274
|
summary: `${radar.title} [${radar.status}]`,
|
|
225
275
|
quality_contract: radar.quality_contract || null,
|
|
226
276
|
related_features: relatedFeatures.map((item) => item.id),
|
|
227
277
|
related_debates: snapshot.discoveryIndex.records
|
|
228
|
-
.filter((record) => record.type === 'DEB' && record.related_ids.includes(
|
|
278
|
+
.filter((record) => record.type === 'DEB' && record.related_ids.includes(normalizedRef))
|
|
229
279
|
.map((record) => record.id),
|
|
230
280
|
read_order: [
|
|
231
281
|
'README.md',
|
|
@@ -234,15 +284,15 @@ export class ContextService {
|
|
|
234
284
|
planningDocRef(paths, 'backlog-graph.md'),
|
|
235
285
|
],
|
|
236
286
|
core_docs: coreDocs,
|
|
237
|
-
};
|
|
287
|
+
});
|
|
238
288
|
}
|
|
239
289
|
if (type === 'FGAP') {
|
|
240
|
-
const gap = snapshot.frontendGaps?.items.find((item) => item.id ===
|
|
290
|
+
const gap = snapshot.frontendGaps?.items.find((item) => item.id === normalizedRef);
|
|
241
291
|
if (!gap)
|
|
242
|
-
throw new Error(`Gap de frontend ${
|
|
243
|
-
return {
|
|
292
|
+
throw new Error(`Gap de frontend ${normalizedRef} nao encontrado.`);
|
|
293
|
+
return finishContextPack({
|
|
244
294
|
sdd_pack_version: 1,
|
|
245
|
-
target_id:
|
|
295
|
+
target_id: normalizedRef,
|
|
246
296
|
target_type: type,
|
|
247
297
|
summary: `${gap.title} [${gap.status}]`,
|
|
248
298
|
source_feature: gap.origin_feature || '',
|
|
@@ -255,14 +305,14 @@ export class ContextService {
|
|
|
255
305
|
planningDocRef(paths, 'frontend-gaps.md'),
|
|
256
306
|
],
|
|
257
307
|
core_docs: coreDocs,
|
|
258
|
-
};
|
|
308
|
+
});
|
|
259
309
|
}
|
|
260
|
-
const debt = snapshot.techDebt.items.find((item) => item.id ===
|
|
310
|
+
const debt = snapshot.techDebt.items.find((item) => item.id === normalizedRef);
|
|
261
311
|
if (!debt)
|
|
262
|
-
throw new Error(`Divida tecnica ${
|
|
263
|
-
return {
|
|
312
|
+
throw new Error(`Divida tecnica ${normalizedRef} nao encontrada.`);
|
|
313
|
+
return finishContextPack({
|
|
264
314
|
sdd_pack_version: 1,
|
|
265
|
-
target_id:
|
|
315
|
+
target_id: normalizedRef,
|
|
266
316
|
target_type: type,
|
|
267
317
|
summary: `${debt.title} [${debt.status}]`,
|
|
268
318
|
related_refs: debt.related_refs,
|
|
@@ -272,7 +322,273 @@ export class ContextService {
|
|
|
272
322
|
planningDocRef(paths, 'tech-debt.md'),
|
|
273
323
|
],
|
|
274
324
|
core_docs: coreDocs,
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
async function buildContextPackCacheRef(paths, ref, type, mode) {
|
|
329
|
+
const projectFingerprint = createProjectFingerprint(paths.projectRoot);
|
|
330
|
+
const sourceFingerprint = await buildContextSourceFingerprint(paths, ref);
|
|
331
|
+
const key = createHash('sha256')
|
|
332
|
+
.update(JSON.stringify([CONTEXT_PACK_CACHE_SCHEMA_VERSION, projectFingerprint, ref, type, mode, sourceFingerprint]))
|
|
333
|
+
.digest('hex')
|
|
334
|
+
.slice(0, 32);
|
|
335
|
+
const cacheRoot = path.join(ensureGlobalCacheLayout().projects, projectFingerprint, 'context-summary');
|
|
336
|
+
const cacheFilePath = path.join(cacheRoot, `${key}.json`);
|
|
337
|
+
return {
|
|
338
|
+
schema_version: CONTEXT_PACK_CACHE_SCHEMA_VERSION,
|
|
339
|
+
enabled: true,
|
|
340
|
+
key,
|
|
341
|
+
project_fingerprint: projectFingerprint,
|
|
342
|
+
source_fingerprint: sourceFingerprint,
|
|
343
|
+
cache_path: displayCachePath(cacheFilePath),
|
|
344
|
+
cache_file_path: cacheFilePath,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
async function buildContextSourceFingerprint(paths, ref) {
|
|
348
|
+
const hash = createHash('sha256');
|
|
349
|
+
const stateFiles = Object.values(paths.stateFiles);
|
|
350
|
+
for (const filePath of stateFiles.sort()) {
|
|
351
|
+
await hashFileIfPresent(hash, filePath);
|
|
352
|
+
}
|
|
353
|
+
const workspaceFiles = await listContextWorkspaceFiles(paths, ref);
|
|
354
|
+
for (const filePath of workspaceFiles.sort()) {
|
|
355
|
+
await hashFileIfPresent(hash, filePath);
|
|
356
|
+
}
|
|
357
|
+
return hash.digest('hex').slice(0, 32);
|
|
358
|
+
}
|
|
359
|
+
async function listContextWorkspaceFiles(paths, ref) {
|
|
360
|
+
const workspaceRoots = [paths.activeDir, paths.plannedDir, paths.archivedDir].map((root) => path.join(root, ref));
|
|
361
|
+
const files = [];
|
|
362
|
+
for (const workspaceRoot of workspaceRoots) {
|
|
363
|
+
const names = await fs.readdir(workspaceRoot).catch(() => []);
|
|
364
|
+
for (const name of names) {
|
|
365
|
+
if (name.endsWith('.yaml') || name.endsWith('.yml') || name.endsWith('.md')) {
|
|
366
|
+
files.push(path.join(workspaceRoot, name));
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return files;
|
|
371
|
+
}
|
|
372
|
+
async function hashFileIfPresent(hash, filePath) {
|
|
373
|
+
const content = await fs.readFile(filePath).catch(() => null);
|
|
374
|
+
if (!content)
|
|
375
|
+
return;
|
|
376
|
+
hash.update(filePath);
|
|
377
|
+
hash.update('\0');
|
|
378
|
+
hash.update(content);
|
|
379
|
+
hash.update('\0');
|
|
380
|
+
}
|
|
381
|
+
async function readContextPackCache(cacheRef) {
|
|
382
|
+
const content = await fs.readFile(cacheRef.cache_file_path, 'utf-8').catch(() => '');
|
|
383
|
+
if (!content.trim())
|
|
384
|
+
return null;
|
|
385
|
+
try {
|
|
386
|
+
const entry = JSON.parse(content);
|
|
387
|
+
if (entry.schema_version !== CONTEXT_PACK_CACHE_SCHEMA_VERSION ||
|
|
388
|
+
entry.key !== cacheRef.key ||
|
|
389
|
+
entry.project_fingerprint !== cacheRef.project_fingerprint ||
|
|
390
|
+
entry.source_fingerprint !== cacheRef.source_fingerprint ||
|
|
391
|
+
!isRecord(entry.context)) {
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
return entry.context;
|
|
395
|
+
}
|
|
396
|
+
catch {
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
async function writeContextPackCache(cacheRef, context) {
|
|
401
|
+
const entry = {
|
|
402
|
+
schema_version: CONTEXT_PACK_CACHE_SCHEMA_VERSION,
|
|
403
|
+
key: cacheRef.key,
|
|
404
|
+
project_fingerprint: cacheRef.project_fingerprint,
|
|
405
|
+
source_fingerprint: cacheRef.source_fingerprint,
|
|
406
|
+
created_at: nowIso(),
|
|
407
|
+
context,
|
|
408
|
+
};
|
|
409
|
+
await fs.mkdir(path.dirname(cacheRef.cache_file_path), { recursive: true });
|
|
410
|
+
await fs.writeFile(cacheRef.cache_file_path, JSON.stringify(entry), 'utf-8').catch(() => undefined);
|
|
411
|
+
}
|
|
412
|
+
function attachContextPackCacheMetadata(context, cacheRef, hit) {
|
|
413
|
+
const { cache_file_path: _cacheFilePath, ...publicCacheRef } = cacheRef;
|
|
414
|
+
const result = {
|
|
415
|
+
...context,
|
|
416
|
+
context_cache: {
|
|
417
|
+
...publicCacheRef,
|
|
418
|
+
hit,
|
|
419
|
+
refreshed_at: nowIso(),
|
|
420
|
+
},
|
|
421
|
+
};
|
|
422
|
+
result.context_budget = {
|
|
423
|
+
...result.context_budget,
|
|
424
|
+
estimated_chars_after_budget: estimatePayloadChars(result),
|
|
425
|
+
};
|
|
426
|
+
return result;
|
|
427
|
+
}
|
|
428
|
+
function displayCachePath(filePath) {
|
|
429
|
+
const homeDir = process.env.HOME ? path.resolve(process.env.HOME) : '';
|
|
430
|
+
const resolved = path.resolve(filePath);
|
|
431
|
+
if (!homeDir || !resolved.startsWith(`${homeDir}${path.sep}`)) {
|
|
432
|
+
return resolved;
|
|
433
|
+
}
|
|
434
|
+
return path.join('~', path.relative(homeDir, resolved));
|
|
435
|
+
}
|
|
436
|
+
function applyContextPackBudget(context, mode) {
|
|
437
|
+
const profile = CONTEXT_PACK_BUDGET_PROFILES[mode];
|
|
438
|
+
const estimatedBefore = estimatePayloadChars(context);
|
|
439
|
+
const dedupedFields = [];
|
|
440
|
+
const truncatedFields = [];
|
|
441
|
+
const budgeted = { ...context };
|
|
442
|
+
const targetRef = typeof context.target_id === 'string' ? context.target_id : '<REF>';
|
|
443
|
+
const omittedFields = [];
|
|
444
|
+
budgeted.read_order = dedupeArrayField(budgeted.read_order, 'read_order', dedupedFields);
|
|
445
|
+
budgeted.core_docs = dedupeArrayField(budgeted.core_docs, 'core_docs', dedupedFields);
|
|
446
|
+
budgeted.relevant_contracts = dedupeArrayField(budgeted.relevant_contracts, 'relevant_contracts', dedupedFields);
|
|
447
|
+
budgeted.predecessor_outputs = dedupeArrayField(budgeted.predecessor_outputs, 'predecessor_outputs', dedupedFields);
|
|
448
|
+
budgeted.related_features = dedupeArrayField(budgeted.related_features, 'related_features', dedupedFields);
|
|
449
|
+
budgeted.related_debates = dedupeArrayField(budgeted.related_debates, 'related_debates', dedupedFields);
|
|
450
|
+
budgeted.relevant_services = dedupeRelevantServices(budgeted.relevant_services, dedupedFields);
|
|
451
|
+
budgeted.read_order = trimArrayField(budgeted.read_order, profile.maxReadOrder, 'read_order', truncatedFields, omittedFields);
|
|
452
|
+
budgeted.core_docs = trimArrayField(budgeted.core_docs, profile.maxCoreDocs, 'core_docs', truncatedFields, omittedFields);
|
|
453
|
+
budgeted.relevant_contracts = trimArrayField(budgeted.relevant_contracts, profile.maxRelevantContracts, 'relevant_contracts', truncatedFields, omittedFields);
|
|
454
|
+
budgeted.relevant_services = trimRelevantServices(trimArrayField(budgeted.relevant_services, profile.maxRelevantServices, 'relevant_services', truncatedFields, omittedFields), profile.maxServiceContracts, truncatedFields, omittedFields);
|
|
455
|
+
budgeted.predecessor_outputs = trimArrayField(budgeted.predecessor_outputs, profile.maxPredecessorOutputs, 'predecessor_outputs', truncatedFields, omittedFields);
|
|
456
|
+
budgeted.related_features = trimArrayField(budgeted.related_features, profile.maxRelatedFeatures, 'related_features', truncatedFields, omittedFields);
|
|
457
|
+
budgeted.related_debates = trimArrayField(budgeted.related_debates, profile.maxRelatedDebates, 'related_debates', truncatedFields, omittedFields);
|
|
458
|
+
const result = {
|
|
459
|
+
...budgeted,
|
|
460
|
+
context_budget: {
|
|
461
|
+
mode,
|
|
462
|
+
estimated_chars_before_budget: estimatedBefore,
|
|
463
|
+
estimated_chars_after_budget: 0,
|
|
464
|
+
deduped_fields: dedupedFields,
|
|
465
|
+
truncated_fields: Array.from(new Set(truncatedFields)),
|
|
466
|
+
},
|
|
467
|
+
};
|
|
468
|
+
if (omittedFields.length > 0) {
|
|
469
|
+
result.progressive_disclosure = {
|
|
470
|
+
mode,
|
|
471
|
+
reveal_command: `${CLI_NAME} sdd context ${targetRef} --full --json`,
|
|
472
|
+
omitted_fields: mergeDisclosureHints(omittedFields),
|
|
275
473
|
};
|
|
276
474
|
}
|
|
475
|
+
result.context_budget.estimated_chars_after_budget = estimatePayloadChars(result);
|
|
476
|
+
return result;
|
|
477
|
+
}
|
|
478
|
+
function dedupeArrayField(value, fieldName, dedupedFields) {
|
|
479
|
+
if (!Array.isArray(value) || value.length <= 1) {
|
|
480
|
+
return value;
|
|
481
|
+
}
|
|
482
|
+
const seen = new Set();
|
|
483
|
+
const deduped = [];
|
|
484
|
+
for (const entry of value) {
|
|
485
|
+
const key = stableContextKey(entry);
|
|
486
|
+
if (seen.has(key))
|
|
487
|
+
continue;
|
|
488
|
+
seen.add(key);
|
|
489
|
+
deduped.push(entry);
|
|
490
|
+
}
|
|
491
|
+
if (deduped.length < value.length) {
|
|
492
|
+
dedupedFields.push({
|
|
493
|
+
field: fieldName,
|
|
494
|
+
before: value.length,
|
|
495
|
+
after: deduped.length,
|
|
496
|
+
removed: value.length - deduped.length,
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
return deduped;
|
|
500
|
+
}
|
|
501
|
+
function dedupeRelevantServices(value, dedupedFields) {
|
|
502
|
+
const services = dedupeArrayField(value, 'relevant_services', dedupedFields);
|
|
503
|
+
if (!Array.isArray(services)) {
|
|
504
|
+
return services;
|
|
505
|
+
}
|
|
506
|
+
return services.map((service, index) => {
|
|
507
|
+
if (!isRecord(service) || !Array.isArray(service.contracts)) {
|
|
508
|
+
return service;
|
|
509
|
+
}
|
|
510
|
+
const field = `relevant_services[${index}].contracts`;
|
|
511
|
+
return {
|
|
512
|
+
...service,
|
|
513
|
+
contracts: dedupeArrayField(service.contracts, field, dedupedFields),
|
|
514
|
+
};
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
function trimArrayField(value, maxItems, fieldName, truncatedFields, omittedFields) {
|
|
518
|
+
if (!Array.isArray(value) || !Number.isFinite(maxItems) || value.length <= maxItems) {
|
|
519
|
+
return value;
|
|
520
|
+
}
|
|
521
|
+
truncatedFields.push(fieldName);
|
|
522
|
+
omittedFields.push({
|
|
523
|
+
field: fieldName,
|
|
524
|
+
omitted_count: value.length - maxItems,
|
|
525
|
+
reason: 'budget',
|
|
526
|
+
});
|
|
527
|
+
return value.slice(0, maxItems);
|
|
528
|
+
}
|
|
529
|
+
function trimRelevantServices(value, maxServiceContracts, truncatedFields, omittedFields) {
|
|
530
|
+
if (!Array.isArray(value) || !Number.isFinite(maxServiceContracts)) {
|
|
531
|
+
return value;
|
|
532
|
+
}
|
|
533
|
+
let truncated = false;
|
|
534
|
+
const services = value.map((service) => {
|
|
535
|
+
if (!isRecord(service) || !Array.isArray(service.contracts)) {
|
|
536
|
+
return service;
|
|
537
|
+
}
|
|
538
|
+
if (service.contracts.length <= maxServiceContracts) {
|
|
539
|
+
return service;
|
|
540
|
+
}
|
|
541
|
+
truncated = true;
|
|
542
|
+
omittedFields.push({
|
|
543
|
+
field: 'relevant_services.contracts',
|
|
544
|
+
omitted_count: service.contracts.length - maxServiceContracts,
|
|
545
|
+
reason: 'budget',
|
|
546
|
+
});
|
|
547
|
+
return {
|
|
548
|
+
...service,
|
|
549
|
+
contracts: service.contracts.slice(0, maxServiceContracts),
|
|
550
|
+
};
|
|
551
|
+
});
|
|
552
|
+
if (truncated) {
|
|
553
|
+
truncatedFields.push('relevant_services.contracts');
|
|
554
|
+
}
|
|
555
|
+
return services;
|
|
556
|
+
}
|
|
557
|
+
function mergeDisclosureHints(hints) {
|
|
558
|
+
const byField = new Map();
|
|
559
|
+
for (const hint of hints) {
|
|
560
|
+
const existing = byField.get(hint.field);
|
|
561
|
+
if (!existing) {
|
|
562
|
+
byField.set(hint.field, { ...hint });
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
existing.omitted_count += hint.omitted_count;
|
|
566
|
+
}
|
|
567
|
+
return Array.from(byField.values());
|
|
568
|
+
}
|
|
569
|
+
function stableContextKey(value) {
|
|
570
|
+
if (typeof value === 'string')
|
|
571
|
+
return value;
|
|
572
|
+
if (typeof value === 'number' || typeof value === 'boolean' || value === null) {
|
|
573
|
+
return JSON.stringify(value);
|
|
574
|
+
}
|
|
575
|
+
return JSON.stringify(sortContextValue(value));
|
|
576
|
+
}
|
|
577
|
+
function sortContextValue(value) {
|
|
578
|
+
if (Array.isArray(value)) {
|
|
579
|
+
return value.map(sortContextValue);
|
|
580
|
+
}
|
|
581
|
+
if (!isRecord(value)) {
|
|
582
|
+
return value;
|
|
583
|
+
}
|
|
584
|
+
return Object.fromEntries(Object.keys(value)
|
|
585
|
+
.sort()
|
|
586
|
+
.map((key) => [key, sortContextValue(value[key])]));
|
|
587
|
+
}
|
|
588
|
+
function estimatePayloadChars(value) {
|
|
589
|
+
return JSON.stringify(value).length;
|
|
590
|
+
}
|
|
591
|
+
function isRecord(value) {
|
|
592
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
277
593
|
}
|
|
278
594
|
//# sourceMappingURL=context.service.js.map
|
|
@@ -2,7 +2,7 @@ import path from "node:path";
|
|
|
2
2
|
import { promises as fs } from "node:fs";
|
|
3
3
|
import { TransitionEngine } from "../domain/transition-engine.js";
|
|
4
4
|
import { allocateEntityId, loadStateSnapshot, nowIso, saveStateTransaction } from "../state.js";
|
|
5
|
-
import { slugify, computeCanonicalTitle, findDiscoveryRecord, markdownDebateTemplate, buildDefaultQualityContract, syncCounterFromId, getRuntime, persistAndRender, applyLoggedTransition } from "../legacy-operations.js";
|
|
5
|
+
import { slugify, computeCanonicalTitle, findDiscoveryRecord, markdownDebateTemplate, buildDefaultQualityContract, findDebateFile, syncCounterFromId, getRuntime, persistAndRender, applyLoggedTransition } from "../legacy-operations.js";
|
|
6
6
|
import { withStateLock } from "../state-lock.js";
|
|
7
7
|
export class DebateService {
|
|
8
8
|
stores;
|
|
@@ -19,10 +19,23 @@ export class DebateService {
|
|
|
19
19
|
}
|
|
20
20
|
const existingDebate = snapshot.discoveryIndex.records.find((record) => record.type === 'DEB' && record.related_ids.includes(insight.id));
|
|
21
21
|
if (existingDebate) {
|
|
22
|
+
const existingFile = await findDebateFile(paths, existingDebate.id);
|
|
23
|
+
if (existingFile) {
|
|
24
|
+
return {
|
|
25
|
+
id: existingDebate.id,
|
|
26
|
+
title: existingDebate.title,
|
|
27
|
+
filePath: existingFile,
|
|
28
|
+
reused: true,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const repairedFilePath = path.join(paths.discoveryDebatesDir, `${existingDebate.id}-${slugify(existingDebate.title)}.md`);
|
|
32
|
+
await fs.mkdir(paths.discoveryDebatesDir, { recursive: true });
|
|
33
|
+
await fs.writeFile(repairedFilePath, markdownDebateTemplate(insight, existingDebate.id), 'utf-8');
|
|
34
|
+
await persistAndRender(paths, config, options?.render);
|
|
22
35
|
return {
|
|
23
36
|
id: existingDebate.id,
|
|
24
37
|
title: existingDebate.title,
|
|
25
|
-
filePath:
|
|
38
|
+
filePath: repairedFilePath,
|
|
26
39
|
reused: true,
|
|
27
40
|
};
|
|
28
41
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { type SddPaths, type SddRuntimeConfig } from '../state.js';
|
|
2
|
+
import type { BacklogItem } from '../types.js';
|
|
3
|
+
import { type WorkspaceQualityDocument, type WorkspaceTasksDocument } from '../workspace-schemas.js';
|
|
1
4
|
export interface FeatureLintOptions {
|
|
2
5
|
strict?: boolean;
|
|
3
6
|
}
|
|
@@ -22,6 +25,25 @@ export interface FeatureLintReport {
|
|
|
22
25
|
};
|
|
23
26
|
findings: FeatureLintFinding[];
|
|
24
27
|
}
|
|
28
|
+
export declare function isFoundationPrescriptiveFeature(feature: Pick<BacklogItem, 'title' | 'summary'>): boolean;
|
|
29
|
+
export declare function loadTasksDocument(paths: SddPaths, config: SddRuntimeConfig, featureId: string): Promise<{
|
|
30
|
+
workspacePath: string;
|
|
31
|
+
tasks?: WorkspaceTasksDocument;
|
|
32
|
+
error?: string;
|
|
33
|
+
}>;
|
|
34
|
+
export declare function loadQualityDocument(paths: SddPaths, config: SddRuntimeConfig, featureId: string): Promise<{
|
|
35
|
+
workspacePath: string;
|
|
36
|
+
quality?: WorkspaceQualityDocument;
|
|
37
|
+
error?: string;
|
|
38
|
+
}>;
|
|
39
|
+
export declare function hasTemplatePlaceholder(content: string): boolean;
|
|
40
|
+
export declare function collectTaskCommands(tasks: WorkspaceTasksDocument | undefined): string[];
|
|
41
|
+
export declare function addFinding(findings: FeatureLintFinding[], severity: FeatureLintFinding['severity'], code: string, message: string, evidence: Record<string, unknown>): void;
|
|
42
|
+
export declare function lintFeatureShape(feature: BacklogItem, taskCount: number, workspaceError: string | undefined, qualityDocument: WorkspaceQualityDocument | undefined, qualityError: string | undefined, commands: string[], adr: {
|
|
43
|
+
exists: boolean;
|
|
44
|
+
isTemplate: boolean;
|
|
45
|
+
path: string;
|
|
46
|
+
}): FeatureLintFinding[];
|
|
25
47
|
export declare class FeatureLintService {
|
|
26
48
|
execute(projectRoot: string, featureId: string, options?: FeatureLintOptions): Promise<FeatureLintReport>;
|
|
27
49
|
}
|
|
@@ -2,8 +2,20 @@ import path from 'node:path';
|
|
|
2
2
|
import { existsSync, promises as fs } from 'node:fs';
|
|
3
3
|
import { loadProjectSddConfig, loadStateSnapshot, resolveSddPaths, } from '../state.js';
|
|
4
4
|
import { activeDocNamesForLayout, ensureMemoryInitialized, relProjectPath } from '../legacy-operations.js';
|
|
5
|
-
import { parseWorkspaceYamlDocument } from '../workspace-schemas.js';
|
|
6
|
-
|
|
5
|
+
import { parseWorkspaceYamlDocument, } from '../workspace-schemas.js';
|
|
6
|
+
const FOUNDATION_PROFILE_TOKENS = ['foundation api', 'devtrack foundation api'];
|
|
7
|
+
const FOUNDATION_ENFORCEMENT_TOKENS = ['prescriptive', 'enforcement', 'lint', 'scaffold'];
|
|
8
|
+
const ADR_TEMPLATE_PLACEHOLDERS = [
|
|
9
|
+
'Describe the context that motivated this architecture decision',
|
|
10
|
+
'Document the decision, the change scope, and the alternatives rejected.',
|
|
11
|
+
'List positive impacts, residual risks, and mitigation actions.',
|
|
12
|
+
];
|
|
13
|
+
export function isFoundationPrescriptiveFeature(feature) {
|
|
14
|
+
const source = `${feature.title} ${feature.summary || ''}`.toLowerCase();
|
|
15
|
+
return (FOUNDATION_PROFILE_TOKENS.some((token) => source.includes(token)) &&
|
|
16
|
+
FOUNDATION_ENFORCEMENT_TOKENS.some((token) => source.includes(token)));
|
|
17
|
+
}
|
|
18
|
+
export async function loadTasksDocument(paths, config, featureId) {
|
|
7
19
|
const names = activeDocNamesForLayout(config);
|
|
8
20
|
const roots = [paths.activeDir, paths.plannedDir, paths.archivedDir];
|
|
9
21
|
for (const root of roots) {
|
|
@@ -30,10 +42,47 @@ async function loadTasksDocument(paths, config, featureId) {
|
|
|
30
42
|
}
|
|
31
43
|
return { workspacePath: '', error: 'Feature workspace not found.' };
|
|
32
44
|
}
|
|
33
|
-
function
|
|
45
|
+
export async function loadQualityDocument(paths, config, featureId) {
|
|
46
|
+
const names = activeDocNamesForLayout(config);
|
|
47
|
+
const roots = [paths.activeDir, paths.plannedDir, paths.archivedDir];
|
|
48
|
+
for (const root of roots) {
|
|
49
|
+
const workspacePath = path.join(root, featureId);
|
|
50
|
+
if (!existsSync(workspacePath))
|
|
51
|
+
continue;
|
|
52
|
+
const qualityPath = path.join(workspacePath, names.quality);
|
|
53
|
+
if (!existsSync(qualityPath)) {
|
|
54
|
+
return { workspacePath: relProjectPath(paths, workspacePath), error: `${names.quality} not found.` };
|
|
55
|
+
}
|
|
56
|
+
const content = await fs.readFile(qualityPath, 'utf-8');
|
|
57
|
+
try {
|
|
58
|
+
return {
|
|
59
|
+
workspacePath: relProjectPath(paths, workspacePath),
|
|
60
|
+
quality: parseWorkspaceYamlDocument('5-quality.yaml', content),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
return {
|
|
65
|
+
workspacePath: relProjectPath(paths, workspacePath),
|
|
66
|
+
error: error instanceof Error ? error.message : String(error),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return { workspacePath: '', error: 'Feature workspace not found.' };
|
|
71
|
+
}
|
|
72
|
+
export function hasTemplatePlaceholder(content) {
|
|
73
|
+
return ADR_TEMPLATE_PLACEHOLDERS.some((placeholder) => content.includes(placeholder));
|
|
74
|
+
}
|
|
75
|
+
export function collectTaskCommands(tasks) {
|
|
76
|
+
if (!tasks)
|
|
77
|
+
return [];
|
|
78
|
+
return tasks.tasks
|
|
79
|
+
.flatMap((task) => task.test_scripts.map((script) => script.command.toLowerCase().trim()))
|
|
80
|
+
.filter(Boolean);
|
|
81
|
+
}
|
|
82
|
+
export function addFinding(findings, severity, code, message, evidence) {
|
|
34
83
|
findings.push({ severity, code, message, evidence });
|
|
35
84
|
}
|
|
36
|
-
function lintFeatureShape(feature, taskCount, workspaceError) {
|
|
85
|
+
export function lintFeatureShape(feature, taskCount, workspaceError, qualityDocument, qualityError, commands, adr) {
|
|
37
86
|
const findings = [];
|
|
38
87
|
if (workspaceError) {
|
|
39
88
|
addFinding(findings, 'error', 'workspace_tasks_unreadable', 'Feature tasks could not be read from the workspace.', {
|
|
@@ -87,6 +136,49 @@ function lintFeatureShape(feature, taskCount, workspaceError) {
|
|
|
87
136
|
touches: feature.touches,
|
|
88
137
|
});
|
|
89
138
|
}
|
|
139
|
+
if (isFoundationPrescriptiveFeature(feature)) {
|
|
140
|
+
if ((feature.quality_contract?.enforcement || 'blocking') !== 'blocking') {
|
|
141
|
+
addFinding(findings, 'error', 'foundation_enforcement_not_blocking', 'Foundation API prescriptive features must use blocking quality enforcement.', {
|
|
142
|
+
feature_id: feature.id,
|
|
143
|
+
enforcement: feature.quality_contract?.enforcement || 'undefined',
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
if (qualityError) {
|
|
147
|
+
addFinding(findings, 'error', 'foundation_quality_unreadable', 'Foundation API prescriptive features require a readable 5-quality.yaml workspace document.', {
|
|
148
|
+
feature_id: feature.id,
|
|
149
|
+
error: qualityError,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
if (!commands.some((command) => command.includes('sdd lint feature'))) {
|
|
153
|
+
addFinding(findings, 'error', 'foundation_lint_blocker_missing', 'Foundation API prescriptive features must include a feature-lint blocker command in tasks.', {
|
|
154
|
+
feature_id: feature.id,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
if (!commands.some((command) => command.includes('sdd plugin devtrack-api scaffold-dry-run'))) {
|
|
158
|
+
addFinding(findings, 'error', 'foundation_scaffold_blocker_missing', 'Foundation API prescriptive features must include a devtrack-api scaffold dry-run blocker command in tasks.', {
|
|
159
|
+
feature_id: feature.id,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
const exceptionCount = qualityDocument?.exceptions.length || feature.quality_contract?.exceptions.length || 0;
|
|
163
|
+
if (exceptionCount > 0 && !feature.requires_adr) {
|
|
164
|
+
addFinding(findings, 'error', 'foundation_exception_requires_adr_flag', 'Foundation API exceptions require requires_adr=true in backlog state.', {
|
|
165
|
+
feature_id: feature.id,
|
|
166
|
+
exceptions: exceptionCount,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
if (exceptionCount > 0 && !adr.exists) {
|
|
170
|
+
addFinding(findings, 'error', 'foundation_exception_missing_adr', 'Foundation API exceptions require ADR documentation before execution can proceed.', {
|
|
171
|
+
feature_id: feature.id,
|
|
172
|
+
expected_adr: adr.path,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
if (exceptionCount > 0 && adr.isTemplate) {
|
|
176
|
+
addFinding(findings, 'error', 'foundation_exception_adr_template', 'Foundation API exception ADR must be completed with concrete context, decision, and consequences.', {
|
|
177
|
+
feature_id: feature.id,
|
|
178
|
+
adr_path: adr.path,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
90
182
|
return findings;
|
|
91
183
|
}
|
|
92
184
|
export class FeatureLintService {
|
|
@@ -103,8 +195,16 @@ export class FeatureLintService {
|
|
|
103
195
|
throw new Error(`Feature ${featureId} not found in backlog.`);
|
|
104
196
|
}
|
|
105
197
|
const workspace = await loadTasksDocument(paths, config, featureId);
|
|
198
|
+
const qualityWorkspace = await loadQualityDocument(paths, config, featureId);
|
|
106
199
|
const taskCount = workspace.tasks?.tasks.length ?? 0;
|
|
107
|
-
const
|
|
200
|
+
const commandList = collectTaskCommands(workspace.tasks);
|
|
201
|
+
const adrPath = path.join(paths.coreDir, 'adrs', `ADR-${feature.id}.md`);
|
|
202
|
+
const adrContent = await fs.readFile(adrPath, 'utf-8').catch(() => '');
|
|
203
|
+
const findings = lintFeatureShape(feature, taskCount, workspace.error, qualityWorkspace.quality, qualityWorkspace.error, commandList, {
|
|
204
|
+
exists: !!adrContent.trim(),
|
|
205
|
+
isTemplate: hasTemplatePlaceholder(adrContent),
|
|
206
|
+
path: relProjectPath(paths, adrPath),
|
|
207
|
+
});
|
|
108
208
|
const hasErrors = findings.some((finding) => finding.severity === 'error');
|
|
109
209
|
const hasWarnings = findings.some((finding) => finding.severity === 'warning');
|
|
110
210
|
const ok = !hasErrors && !(options.strict && hasWarnings);
|