project-tiny-context-harness 0.2.85 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +164 -427
- package/assets/README.md +284 -455
- package/assets/README.zh-CN.md +200 -123
- package/assets/agents/AGENTS_CORE.md +54 -67
- package/assets/context_templates/context.toml +3 -0
- package/assets/context_templates/global.md +6 -6
- package/assets/github/harness.yml +2 -2
- package/assets/skills/context_development_engineer/SKILL.md +91 -107
- package/assets/skills/context_product_plan/SKILL.md +18 -28
- package/assets/skills/context_surface_contract/SKILL.md +35 -38
- package/assets/skills/context_uiux_design/SKILL.md +31 -30
- package/assets/skills/long-task-workflow/SKILL.md +76 -0
- package/assets/skills/long-task-workflow/agents/openai.yaml +4 -0
- package/assets/skills/long-task-workflow/references/authority-lifecycle.md +41 -0
- package/assets/skills/long-task-workflow/references/contract-authoring.md +63 -0
- package/assets/skills/long-task-workflow/references/evidence-design.md +45 -0
- package/assets/skills/normal-long-task/SKILL.md +12 -489
- package/assets/skills/source-plan-authoring/SKILL.md +467 -0
- package/assets/tools/validate_context.py +293 -127
- package/dist/cli.js +3 -1
- package/dist/commands/check-modularity.js +33 -10
- package/dist/commands/composite-campaign.d.ts +1 -0
- package/dist/commands/composite-campaign.js +8 -0
- package/dist/commands/composite-long-task.d.ts +1 -6
- package/dist/commands/composite-long-task.js +7 -163
- package/dist/commands/delivery-set.d.ts +1 -0
- package/dist/commands/delivery-set.js +8 -0
- package/dist/commands/disable.d.ts +1 -0
- package/dist/commands/disable.js +15 -0
- package/dist/commands/enable.d.ts +1 -0
- package/dist/commands/enable.js +14 -0
- package/dist/commands/export-context-args.js +13 -4
- package/dist/commands/export-context.js +18 -7
- package/dist/commands/index.js +42 -30
- package/dist/commands/init.js +12 -6
- package/dist/commands/long-task-authoring.d.ts +3 -0
- package/dist/commands/long-task-authoring.js +92 -0
- package/dist/commands/long-task-explain.d.ts +1 -0
- package/dist/commands/long-task-explain.js +52 -0
- package/dist/commands/long-task.d.ts +1 -0
- package/dist/commands/long-task.js +226 -0
- package/dist/commands/package-source.js +2 -2
- package/dist/commands/upgrade.js +10 -10
- package/dist/index.d.ts +11 -1
- package/dist/index.js +8 -0
- package/dist/lib/config.js +34 -8
- package/dist/lib/context-default-footprint.d.ts +16 -0
- package/dist/lib/context-default-footprint.js +107 -0
- package/dist/lib/context-export.js +105 -41
- package/dist/lib/context-graph-snapshot.d.ts +18 -0
- package/dist/lib/context-graph-snapshot.js +171 -0
- package/dist/lib/context-manifest-schema.d.ts +28 -0
- package/dist/lib/context-manifest-schema.js +150 -0
- package/dist/lib/context-manifest.js +28 -20
- package/dist/lib/context-templates.js +5 -5
- package/dist/lib/design-md.js +1 -1
- package/dist/lib/doctor.js +54 -1
- package/dist/lib/harness-root.js +25 -8
- package/dist/lib/init.js +6 -3
- package/dist/lib/legacy-managed-scan.js +23 -7
- package/dist/lib/legacy-sdlc-migration.js +21 -11
- package/dist/lib/long-task-acceptance-reference.d.ts +22 -0
- package/dist/lib/long-task-acceptance-reference.js +46 -0
- package/dist/lib/long-task-acceptance-shape.d.ts +5 -0
- package/dist/lib/long-task-acceptance-shape.js +131 -0
- package/dist/lib/long-task-activation-validation.d.ts +21 -0
- package/dist/lib/long-task-activation-validation.js +136 -0
- package/dist/lib/long-task-artifacts.d.ts +5 -0
- package/dist/lib/long-task-artifacts.js +34 -0
- package/dist/lib/long-task-assertions-v2.d.ts +22 -0
- package/dist/lib/long-task-assertions-v2.js +212 -0
- package/dist/lib/long-task-authoring-authority-preview.d.ts +5 -0
- package/dist/lib/long-task-authoring-authority-preview.js +85 -0
- package/dist/lib/long-task-authoring-preflight-diagnostics.d.ts +8 -0
- package/dist/lib/long-task-authoring-preflight-diagnostics.js +179 -0
- package/dist/lib/long-task-authoring-preflight-repair-order.d.ts +8 -0
- package/dist/lib/long-task-authoring-preflight-repair-order.js +95 -0
- package/dist/lib/long-task-authoring-preflight-types.d.ts +46 -0
- package/dist/lib/long-task-authoring-preflight-types.js +38 -0
- package/dist/lib/long-task-authoring-preflight.d.ts +3 -0
- package/dist/lib/long-task-authoring-preflight.js +44 -0
- package/dist/lib/long-task-authority-material-diff.d.ts +17 -0
- package/dist/lib/long-task-authority-material-diff.js +152 -0
- package/dist/lib/long-task-authority-materials.d.ts +7 -0
- package/dist/lib/long-task-authority-materials.js +83 -0
- package/dist/lib/long-task-authority-policy.d.ts +168 -0
- package/dist/lib/long-task-authority-policy.js +181 -0
- package/dist/lib/long-task-authority-revision-details.d.ts +19 -0
- package/dist/lib/long-task-authority-revision-details.js +192 -0
- package/dist/lib/long-task-authority-revision-enforcement.d.ts +3 -0
- package/dist/lib/long-task-authority-revision-enforcement.js +41 -0
- package/dist/lib/long-task-authority-revision-types.d.ts +50 -0
- package/dist/lib/long-task-authority-revision-types.js +1 -0
- package/dist/lib/long-task-authority-revision.d.ts +4 -0
- package/dist/lib/long-task-authority-revision.js +188 -0
- package/dist/lib/long-task-authority-transition-policy.d.ts +62 -0
- package/dist/lib/long-task-authority-transition-policy.js +62 -0
- package/dist/lib/long-task-authority-types.d.ts +96 -0
- package/dist/lib/long-task-authority-types.js +1 -0
- package/dist/lib/long-task-authority.d.ts +7 -0
- package/dist/lib/long-task-authority.js +142 -0
- package/dist/lib/long-task-boundary-check.d.ts +15 -0
- package/dist/lib/long-task-boundary-check.js +9 -0
- package/dist/lib/long-task-check-evidence-decoder.d.ts +3 -0
- package/dist/lib/long-task-check-evidence-decoder.js +74 -0
- package/dist/lib/long-task-check-execution-policy.d.ts +18 -0
- package/dist/lib/long-task-check-execution-policy.js +36 -0
- package/dist/lib/long-task-check-runner.d.ts +2 -0
- package/dist/lib/long-task-check-runner.js +181 -0
- package/dist/lib/long-task-check-shape.d.ts +2 -0
- package/dist/lib/long-task-check-shape.js +136 -0
- package/dist/lib/long-task-claim-definitions.d.ts +7 -0
- package/dist/lib/long-task-claim-definitions.js +78 -0
- package/dist/lib/long-task-claim-proof-policy.d.ts +4 -0
- package/dist/lib/long-task-claim-proof-policy.js +57 -0
- package/dist/lib/long-task-claims.d.ts +10 -0
- package/dist/lib/long-task-claims.js +187 -0
- package/dist/lib/long-task-context-authority-topology.d.ts +7 -0
- package/dist/lib/long-task-context-authority-topology.js +37 -0
- package/dist/lib/long-task-context-authority.d.ts +14 -0
- package/dist/lib/long-task-context-authority.js +66 -0
- package/dist/lib/long-task-contract-types.d.ts +203 -0
- package/dist/lib/long-task-contract-types.js +1 -0
- package/dist/lib/long-task-counterfactual-claim-policy.d.ts +10 -0
- package/dist/lib/long-task-counterfactual-claim-policy.js +57 -0
- package/dist/lib/long-task-counterfactual-sandbox.d.ts +6 -0
- package/dist/lib/long-task-counterfactual-sandbox.js +87 -0
- package/dist/lib/long-task-counterfactual-types.d.ts +24 -0
- package/dist/lib/long-task-counterfactual-types.js +1 -0
- package/dist/lib/long-task-delivery-compiler.d.ts +10 -0
- package/dist/lib/long-task-delivery-compiler.js +129 -0
- package/dist/lib/long-task-delivery-parser.d.ts +13 -0
- package/dist/lib/long-task-delivery-parser.js +90 -0
- package/dist/lib/long-task-delivery-preflight.d.ts +5 -0
- package/dist/lib/long-task-delivery-preflight.js +151 -0
- package/dist/lib/long-task-delivery-shape.d.ts +5 -0
- package/dist/lib/long-task-delivery-shape.js +5 -0
- package/dist/lib/long-task-delivery-types.d.ts +8 -0
- package/dist/lib/long-task-delivery-types.js +8 -0
- package/dist/lib/long-task-delivery-validation.d.ts +3 -0
- package/dist/lib/long-task-delivery-validation.js +175 -0
- package/dist/lib/long-task-evidence-adapter-policy.d.ts +4 -0
- package/dist/lib/long-task-evidence-adapter-policy.js +15 -0
- package/dist/lib/long-task-evidence-adapter-types.d.ts +1 -0
- package/dist/lib/long-task-evidence-adapter-types.js +1 -0
- package/dist/lib/long-task-evidence-findings.d.ts +5 -0
- package/dist/lib/long-task-evidence-findings.js +113 -0
- package/dist/lib/long-task-evidence-sensitivity-policy.d.ts +4 -0
- package/dist/lib/long-task-evidence-sensitivity-policy.js +98 -0
- package/dist/lib/long-task-evidence-v2.d.ts +5 -0
- package/dist/lib/long-task-evidence-v2.js +226 -0
- package/dist/lib/long-task-explain-acceptance-link.d.ts +46 -0
- package/dist/lib/long-task-explain-acceptance-link.js +96 -0
- package/dist/lib/long-task-explain-claim-links.d.ts +45 -0
- package/dist/lib/long-task-explain-claim-links.js +88 -0
- package/dist/lib/long-task-explain-source-links.d.ts +93 -0
- package/dist/lib/long-task-explain-source-links.js +51 -0
- package/dist/lib/long-task-final-v2.d.ts +2 -0
- package/dist/lib/long-task-final-v2.js +124 -0
- package/dist/lib/long-task-finding-context.d.ts +3 -0
- package/dist/lib/long-task-finding-context.js +63 -0
- package/dist/lib/long-task-freshness.d.ts +3 -0
- package/dist/lib/long-task-freshness.js +80 -0
- package/dist/lib/long-task-hook-install.d.ts +9 -0
- package/dist/lib/long-task-hook-install.js +184 -0
- package/dist/lib/long-task-hook-preflight.d.ts +7 -0
- package/dist/lib/long-task-hook-preflight.js +58 -0
- package/dist/lib/long-task-observation-ownership.d.ts +2 -0
- package/dist/lib/long-task-observation-ownership.js +22 -0
- package/dist/lib/long-task-outcome-parser.d.ts +4 -0
- package/dist/lib/long-task-outcome-parser.js +100 -0
- package/dist/lib/long-task-paths.d.ts +49 -0
- package/dist/lib/long-task-paths.js +418 -0
- package/dist/lib/long-task-playwright-counterfactual-policy.d.ts +7 -0
- package/dist/lib/long-task-playwright-counterfactual-policy.js +125 -0
- package/dist/lib/long-task-playwright-evidence.d.ts +6 -0
- package/dist/lib/long-task-playwright-evidence.js +244 -0
- package/dist/lib/long-task-product-shape.d.ts +6 -0
- package/dist/lib/long-task-product-shape.js +73 -0
- package/dist/lib/long-task-progress.d.ts +4 -0
- package/dist/lib/long-task-progress.js +114 -0
- package/dist/lib/long-task-protected-files.d.ts +1 -0
- package/dist/lib/long-task-protected-files.js +30 -0
- package/dist/lib/long-task-required-proof-surfaces.d.ts +2 -0
- package/dist/lib/long-task-required-proof-surfaces.js +13 -0
- package/dist/lib/long-task-requirement-shape.d.ts +2 -0
- package/dist/lib/long-task-requirement-shape.js +18 -0
- package/dist/lib/long-task-risk-surfaces.d.ts +2 -0
- package/dist/lib/long-task-risk-surfaces.js +60 -0
- package/dist/lib/long-task-risk-types.d.ts +5 -0
- package/dist/lib/long-task-risk-types.js +12 -0
- package/dist/lib/long-task-risk.d.ts +9 -0
- package/dist/lib/long-task-risk.js +132 -0
- package/dist/lib/long-task-root-shape.d.ts +4 -0
- package/dist/lib/long-task-root-shape.js +86 -0
- package/dist/lib/long-task-runner-environment.d.ts +7 -0
- package/dist/lib/long-task-runner-environment.js +57 -0
- package/dist/lib/long-task-runner-files.d.ts +3 -0
- package/dist/lib/long-task-runner-files.js +39 -0
- package/dist/lib/long-task-runner-freeze.d.ts +2 -0
- package/dist/lib/long-task-runner-freeze.js +168 -0
- package/dist/lib/long-task-runtime-types.d.ts +214 -0
- package/dist/lib/long-task-runtime-types.js +1 -0
- package/dist/lib/long-task-shape-primitives.d.ts +17 -0
- package/dist/lib/long-task-shape-primitives.js +81 -0
- package/dist/lib/long-task-source-authority-types.d.ts +13 -0
- package/dist/lib/long-task-source-authority-types.js +1 -0
- package/dist/lib/long-task-source-claim-validation.d.ts +5 -0
- package/dist/lib/long-task-source-claim-validation.js +96 -0
- package/dist/lib/long-task-source-continuity.d.ts +4 -0
- package/dist/lib/long-task-source-continuity.js +57 -0
- package/dist/lib/long-task-source-inventory.d.ts +2 -0
- package/dist/lib/long-task-source-inventory.js +21 -0
- package/dist/lib/long-task-source-item-parser.d.ts +3 -0
- package/dist/lib/long-task-source-item-parser.js +112 -0
- package/dist/lib/long-task-source-shape.d.ts +2 -0
- package/dist/lib/long-task-source-shape.js +56 -0
- package/dist/lib/long-task-source-target-continuity.d.ts +4 -0
- package/dist/lib/long-task-source-target-continuity.js +80 -0
- package/dist/lib/long-task-source-target-index.d.ts +12 -0
- package/dist/lib/long-task-source-target-index.js +76 -0
- package/dist/lib/long-task-source-validation.d.ts +3 -0
- package/dist/lib/long-task-source-validation.js +46 -0
- package/dist/lib/long-task-state.d.ts +91 -0
- package/dist/lib/long-task-state.js +611 -0
- package/dist/lib/long-task-status-projection.d.ts +21 -0
- package/dist/lib/long-task-status-projection.js +116 -0
- package/dist/lib/long-task-status-v2.d.ts +37 -0
- package/dist/lib/long-task-status-v2.js +306 -0
- package/dist/lib/long-task-technical-shape.d.ts +3 -0
- package/dist/lib/long-task-technical-shape.js +44 -0
- package/dist/lib/long-task-verifier-authority.d.ts +12 -0
- package/dist/lib/long-task-verifier-authority.js +47 -0
- package/dist/lib/long-task-verifier-identity.d.ts +2 -0
- package/dist/lib/long-task-verifier-identity.js +55 -0
- package/dist/lib/long-task-verifier-v2.d.ts +13 -0
- package/dist/lib/long-task-verifier-v2.js +266 -0
- package/dist/lib/long-task-workspace-runtime-types.d.ts +23 -0
- package/dist/lib/long-task-workspace-runtime-types.js +1 -0
- package/dist/lib/long-task-workspace.d.ts +25 -0
- package/dist/lib/long-task-workspace.js +366 -0
- package/dist/lib/managed-file.js +20 -5
- package/dist/lib/migrations.js +156 -32
- package/dist/lib/modularity.d.ts +27 -1
- package/dist/lib/modularity.js +499 -23
- package/dist/lib/package-json-config.js +14 -5
- package/dist/lib/package-source.js +4 -2
- package/dist/lib/profiles.d.ts +13 -0
- package/dist/lib/profiles.js +66 -0
- package/dist/lib/schema-guard.js +4 -2
- package/dist/lib/source-files.js +14 -7
- package/dist/lib/source-pack-classify.js +91 -19
- package/dist/lib/source-pack-config.js +25 -8
- package/dist/lib/source-pack-export.js +84 -25
- package/dist/lib/source-pack-manifest.js +18 -6
- package/dist/lib/source-pack-records.js +35 -11
- package/dist/lib/source-pack-render.js +47 -18
- package/dist/lib/stable-json.d.ts +2 -0
- package/dist/lib/stable-json.js +21 -0
- package/dist/lib/strict-codec.d.ts +4 -0
- package/dist/lib/strict-codec.js +65 -0
- package/dist/lib/sync-engine.js +76 -24
- package/dist/lib/types.d.ts +8 -1
- package/dist/lib/upgrade.js +4 -3
- package/dist/lib/validators.js +218 -191
- package/dist/long-task-hook.d.ts +2 -0
- package/dist/long-task-hook.js +55 -0
- package/dist/schemas/long-task-delivery-v2/long-task-delivery-v2.schema.json +340 -0
- package/dist/schemas/long-task-delivery-v2/long-task-outcomes-v2.schema.json +18 -0
- package/migrations/README.md +8 -3
- package/package.json +17 -6
- package/source-mappings.yaml +0 -3
- package/assets/protected-harness-baseline.json +0 -20
- package/assets/skills/composite-long-task-workflow/SKILL.md +0 -245
- package/assets/skills/composite-long-task-workflow/assets/execution-binding.template.md +0 -62
- package/assets/skills/composite-long-task-workflow/assets/goal-objective.template.md +0 -17
- package/assets/skills/composite-long-task-workflow/references/composite-long-task-workflow-protocol.md +0 -682
- package/dist/commands/superpowers.d.ts +0 -1
- package/dist/commands/superpowers.js +0 -8
- package/dist/lib/composite-long-task-renderer.d.ts +0 -12
- package/dist/lib/composite-long-task-renderer.js +0 -109
- package/dist/lib/plan-acceptance-artifacts.d.ts +0 -1
- package/dist/lib/plan-acceptance-artifacts.js +0 -220
- package/dist/lib/plan-acceptance-evidence.d.ts +0 -2
- package/dist/lib/plan-acceptance-evidence.js +0 -108
- package/dist/lib/plan-acceptance-json.d.ts +0 -15
- package/dist/lib/plan-acceptance-json.js +0 -133
- package/dist/lib/plan-acceptance-validator.d.ts +0 -2
- package/dist/lib/plan-acceptance-validator.js +0 -209
- package/dist/lib/plan-contract-validator.d.ts +0 -2
- package/dist/lib/plan-contract-validator.js +0 -127
- package/dist/lib/plan-validator-common.d.ts +0 -24
- package/dist/lib/plan-validator-common.js +0 -196
- package/dist/lib/superpowers-task-ac010.d.ts +0 -6
- package/dist/lib/superpowers-task-ac010.js +0 -26
- package/dist/lib/superpowers-task-assertion-normalizers.d.ts +0 -3
- package/dist/lib/superpowers-task-assertion-normalizers.js +0 -74
- package/dist/lib/superpowers-task-assertions.d.ts +0 -20
- package/dist/lib/superpowers-task-assertions.js +0 -257
- package/dist/lib/superpowers-task-attempt.d.ts +0 -4
- package/dist/lib/superpowers-task-attempt.js +0 -102
- package/dist/lib/superpowers-task-command-run-correlation.d.ts +0 -8
- package/dist/lib/superpowers-task-command-run-correlation.js +0 -103
- package/dist/lib/superpowers-task-command-specs.d.ts +0 -3
- package/dist/lib/superpowers-task-command-specs.js +0 -52
- package/dist/lib/superpowers-task-compile-diagnostics.d.ts +0 -5
- package/dist/lib/superpowers-task-compile-diagnostics.js +0 -20
- package/dist/lib/superpowers-task-compile-guards.d.ts +0 -2
- package/dist/lib/superpowers-task-compile-guards.js +0 -66
- package/dist/lib/superpowers-task-compile.d.ts +0 -6
- package/dist/lib/superpowers-task-compile.js +0 -133
- package/dist/lib/superpowers-task-completion-output.d.ts +0 -91
- package/dist/lib/superpowers-task-completion-output.js +0 -374
- package/dist/lib/superpowers-task-conformance.d.ts +0 -2
- package/dist/lib/superpowers-task-conformance.js +0 -24
- package/dist/lib/superpowers-task-contradictions.d.ts +0 -6
- package/dist/lib/superpowers-task-contradictions.js +0 -126
- package/dist/lib/superpowers-task-current-evidence.d.ts +0 -3
- package/dist/lib/superpowers-task-current-evidence.js +0 -176
- package/dist/lib/superpowers-task-delivery.d.ts +0 -4
- package/dist/lib/superpowers-task-delivery.js +0 -96
- package/dist/lib/superpowers-task-derive.d.ts +0 -14
- package/dist/lib/superpowers-task-derive.js +0 -393
- package/dist/lib/superpowers-task-events.d.ts +0 -1
- package/dist/lib/superpowers-task-events.js +0 -13
- package/dist/lib/superpowers-task-evidence-kernel.d.ts +0 -19
- package/dist/lib/superpowers-task-evidence-kernel.js +0 -347
- package/dist/lib/superpowers-task-evidence-records.d.ts +0 -2
- package/dist/lib/superpowers-task-evidence-records.js +0 -55
- package/dist/lib/superpowers-task-evidence.d.ts +0 -10
- package/dist/lib/superpowers-task-evidence.js +0 -147
- package/dist/lib/superpowers-task-fields.d.ts +0 -22
- package/dist/lib/superpowers-task-fields.js +0 -276
- package/dist/lib/superpowers-task-final-card.d.ts +0 -3
- package/dist/lib/superpowers-task-final-card.js +0 -35
- package/dist/lib/superpowers-task-gates.d.ts +0 -12
- package/dist/lib/superpowers-task-gates.js +0 -267
- package/dist/lib/superpowers-task-harness-drift.d.ts +0 -11
- package/dist/lib/superpowers-task-harness-drift.js +0 -90
- package/dist/lib/superpowers-task-next-slices.d.ts +0 -1
- package/dist/lib/superpowers-task-next-slices.js +0 -12
- package/dist/lib/superpowers-task-protected-baseline.d.ts +0 -10
- package/dist/lib/superpowers-task-protected-baseline.js +0 -66
- package/dist/lib/superpowers-task-source-compile.d.ts +0 -5
- package/dist/lib/superpowers-task-source-compile.js +0 -226
- package/dist/lib/superpowers-task-source-parser.d.ts +0 -23
- package/dist/lib/superpowers-task-source-parser.js +0 -218
- package/dist/lib/superpowers-task-state-schema.d.ts +0 -460
- package/dist/lib/superpowers-task-state-schema.js +0 -66
- package/dist/lib/superpowers-task-state-shape.d.ts +0 -3
- package/dist/lib/superpowers-task-state-shape.js +0 -50
- package/dist/lib/superpowers-task-state.d.ts +0 -16
- package/dist/lib/superpowers-task-state.js +0 -246
- package/dist/lib/superpowers-task-status.d.ts +0 -2
- package/dist/lib/superpowers-task-status.js +0 -24
- package/dist/lib/superpowers-task-under-specified.d.ts +0 -7
- package/dist/lib/superpowers-task-under-specified.js +0 -61
- package/dist/lib/superpowers-task-unregistered-evidence.d.ts +0 -11
- package/dist/lib/superpowers-task-unregistered-evidence.js +0 -72
- package/dist/lib/superpowers-task-validator.d.ts +0 -5
- package/dist/lib/superpowers-task-validator.js +0 -293
package/dist/lib/upgrade.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { runDoctor } from "./doctor.js";
|
|
2
|
-
import { createUpgradePlan, formatUpgradePlan, runMigrations } from "./migrations.js";
|
|
2
|
+
import { createUpgradePlan, formatUpgradePlan, runMigrations, } from "./migrations.js";
|
|
3
3
|
import { assertSupportedSchema } from "./schema-guard.js";
|
|
4
4
|
import { runSync } from "./sync-engine.js";
|
|
5
5
|
export class UpgradeBlockedError extends Error {
|
|
@@ -29,11 +29,12 @@ export async function runUpgradeReport(projectRoot) {
|
|
|
29
29
|
}
|
|
30
30
|
const migrationReport = await runMigrations(projectRoot, plan);
|
|
31
31
|
lines.push(`migrations changed=${migrationReport.changed.length} skipped=${migrationReport.skipped.length} manual_required=${migrationReport.manualRequired.length} blocked=${migrationReport.blocked.length}`);
|
|
32
|
-
if (migrationReport.manualRequired.length > 0 ||
|
|
32
|
+
if (migrationReport.manualRequired.length > 0 ||
|
|
33
|
+
migrationReport.blocked.length > 0) {
|
|
33
34
|
lines.push(...formatUpgradePlan({
|
|
34
35
|
safe_pending: [],
|
|
35
36
|
manual_required: migrationReport.manualRequired,
|
|
36
|
-
blocked: migrationReport.blocked
|
|
37
|
+
blocked: migrationReport.blocked,
|
|
37
38
|
}).slice(1));
|
|
38
39
|
}
|
|
39
40
|
const syncReport = await runSync(projectRoot);
|
package/dist/lib/validators.js
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { realpath } from "node:fs/promises";
|
|
2
4
|
import { readConfig } from "./config.js";
|
|
5
|
+
import { parseContextManifest, } from "./context-manifest-schema.js";
|
|
3
6
|
import { harnessPath, harnessRoot } from "./harness-root.js";
|
|
4
7
|
import { listFiles, pathExists, readText } from "./fs.js";
|
|
5
8
|
import { runModularityCheck } from "./modularity.js";
|
|
6
|
-
import { validatePlanAcceptance } from "./plan-acceptance-validator.js";
|
|
7
|
-
import { validatePlanContract } from "./plan-contract-validator.js";
|
|
8
|
-
import { validateSuperpowersState } from "./superpowers-task-validator.js";
|
|
9
9
|
import { unsupportedSchemaMessage } from "./schema-guard.js";
|
|
10
10
|
const VALIDATORS = {
|
|
11
11
|
"validate-context": validateContext,
|
|
12
12
|
"validate-code-modularity": validateCodeModularity,
|
|
13
13
|
"validate-harness": validateHarness,
|
|
14
|
-
"validate-plan-contract": validatePlanContract,
|
|
15
|
-
"validate-plan-acceptance": validatePlanAcceptance,
|
|
16
|
-
"validate-superpowers-state": validateSuperpowersState
|
|
17
14
|
};
|
|
18
15
|
const GLOBAL_REQUIRED_SECTIONS = [
|
|
19
16
|
...sectionSpecs([
|
|
@@ -24,9 +21,9 @@ const GLOBAL_REQUIRED_SECTIONS = [
|
|
|
24
21
|
"Architecture Context",
|
|
25
22
|
"Verification Entry Points",
|
|
26
23
|
"Current State",
|
|
27
|
-
"Next Safe Action"
|
|
24
|
+
"Next Safe Action",
|
|
28
25
|
]),
|
|
29
|
-
sectionSpec("Context Index", ["Context Index", "Module Index"])
|
|
26
|
+
sectionSpec("Context Index", ["Context Index", "Module Index"]),
|
|
30
27
|
];
|
|
31
28
|
const ARCHITECTURE_REQUIRED_SECTIONS = sectionSpecs([
|
|
32
29
|
"System Boundary",
|
|
@@ -35,7 +32,7 @@ const ARCHITECTURE_REQUIRED_SECTIONS = sectionSpecs([
|
|
|
35
32
|
"Design Rationale",
|
|
36
33
|
"Constraints And Tradeoffs",
|
|
37
34
|
"Verification Implications",
|
|
38
|
-
"Open Risks"
|
|
35
|
+
"Open Risks",
|
|
39
36
|
]);
|
|
40
37
|
const ROLE_ALIASES = {
|
|
41
38
|
global: "global",
|
|
@@ -51,9 +48,15 @@ const ROLE_ALIASES = {
|
|
|
51
48
|
"implementation-index": "implementation-index",
|
|
52
49
|
implementation_index: "implementation-index",
|
|
53
50
|
"decision-rationale": "decision-rationale",
|
|
54
|
-
decision_rationale: "decision-rationale"
|
|
51
|
+
decision_rationale: "decision-rationale",
|
|
55
52
|
};
|
|
56
|
-
const VALID_READ_POLICIES = new Set([
|
|
53
|
+
const VALID_READ_POLICIES = new Set([
|
|
54
|
+
"default",
|
|
55
|
+
"always",
|
|
56
|
+
"optional",
|
|
57
|
+
"on-demand",
|
|
58
|
+
"never-default",
|
|
59
|
+
]);
|
|
57
60
|
const EXPORT_ARTIFACT_NAME_PATTERNS = [
|
|
58
61
|
/full-project-context/i,
|
|
59
62
|
/当前项目context/i,
|
|
@@ -62,16 +65,16 @@ const EXPORT_ARTIFACT_NAME_PATTERNS = [
|
|
|
62
65
|
/project-overview/i,
|
|
63
66
|
/context-bundle/i,
|
|
64
67
|
/context-summary/i,
|
|
65
|
-
/context-export/i
|
|
68
|
+
/context-export/i,
|
|
66
69
|
];
|
|
67
70
|
const FAKE_VERIFICATION_PATTERNS = [
|
|
68
71
|
/\btests?\s+(?:pass(?:ed|es)?|green)\b/i,
|
|
69
72
|
/\bverified\b/i,
|
|
70
73
|
/\bdeployed\s+successfully\b/i,
|
|
71
74
|
/\bvalidation\s+passed\b/i,
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
/测试(?:已)?通过/,
|
|
76
|
+
/验证(?:已)?通过/,
|
|
77
|
+
/部署(?:已)?成功/,
|
|
75
78
|
];
|
|
76
79
|
export async function runValidator(projectRoot, gate, args = []) {
|
|
77
80
|
const validator = VALIDATORS[gate];
|
|
@@ -79,8 +82,8 @@ export async function runValidator(projectRoot, gate, args = []) {
|
|
|
79
82
|
return {
|
|
80
83
|
info: [],
|
|
81
84
|
errors: [
|
|
82
|
-
`unknown validator: ${gate}. Minimal Context Harness supports validate-context, validate-code-modularity
|
|
83
|
-
]
|
|
85
|
+
`unknown validator: ${gate}. Minimal Context Harness supports validate-context, validate-code-modularity and validate-harness only.`,
|
|
86
|
+
],
|
|
84
87
|
};
|
|
85
88
|
}
|
|
86
89
|
return validator(projectRoot, args);
|
|
@@ -90,21 +93,33 @@ async function validateHarness(projectRoot) {
|
|
|
90
93
|
const modularityReport = await validateCodeModularity(projectRoot);
|
|
91
94
|
return {
|
|
92
95
|
info: [...contextReport.info, ...modularityReport.info],
|
|
93
|
-
warnings: [
|
|
94
|
-
|
|
95
|
-
|
|
96
|
+
warnings: [
|
|
97
|
+
...(contextReport.warnings ?? []),
|
|
98
|
+
...(modularityReport.warnings ?? []),
|
|
99
|
+
],
|
|
100
|
+
hygiene: [
|
|
101
|
+
...(contextReport.hygiene ?? []),
|
|
102
|
+
...(modularityReport.hygiene ?? []),
|
|
103
|
+
],
|
|
104
|
+
errors: [...contextReport.errors, ...modularityReport.errors],
|
|
96
105
|
};
|
|
97
106
|
}
|
|
98
107
|
async function validateCodeModularity(projectRoot) {
|
|
99
108
|
const report = await runModularityCheck(projectRoot, { touched: true });
|
|
100
109
|
const info = [
|
|
101
|
-
`code modularity audited=${report.files.length} warning=${report.warnings.length} waived=${report.waivedWarnings.length} limit=${report.limit}
|
|
110
|
+
`code modularity audited=${report.files.length} warning=${report.warnings.length} waived=${report.waivedWarnings.length} limit=${report.limit}`,
|
|
102
111
|
];
|
|
103
112
|
if (report.files.length === 0) {
|
|
104
113
|
info.push("No handwritten source files matched the selected scope.");
|
|
105
114
|
}
|
|
106
115
|
for (const file of report.files) {
|
|
107
|
-
const prefix = file.
|
|
116
|
+
const prefix = file.regressed && file.waived
|
|
117
|
+
? "waived"
|
|
118
|
+
: file.regressed
|
|
119
|
+
? "over-limit"
|
|
120
|
+
: file.overLimit
|
|
121
|
+
? "observed-risk"
|
|
122
|
+
: "ok";
|
|
108
123
|
info.push(`${prefix}: ${file.relativePath} ${file.lines} lines`);
|
|
109
124
|
}
|
|
110
125
|
for (const waiver of report.waivedWarnings) {
|
|
@@ -115,11 +130,12 @@ async function validateCodeModularity(projectRoot) {
|
|
|
115
130
|
}
|
|
116
131
|
return {
|
|
117
132
|
info,
|
|
118
|
-
errors: [...report.errors, ...report.warnings]
|
|
133
|
+
errors: [...report.errors, ...report.warnings],
|
|
119
134
|
};
|
|
120
135
|
}
|
|
121
136
|
async function validateContext(projectRoot) {
|
|
122
137
|
const info = [];
|
|
138
|
+
const warnings = [];
|
|
123
139
|
const errors = [];
|
|
124
140
|
const root = await harnessRoot(projectRoot);
|
|
125
141
|
const globalPath = path.join(projectRoot, "project_context", "global.md");
|
|
@@ -128,6 +144,7 @@ async function validateContext(projectRoot) {
|
|
|
128
144
|
const configPath = path.join(projectRoot, harnessPath(root, "config.yaml"));
|
|
129
145
|
const manifestPath = path.join(projectRoot, "project_context", "context.toml");
|
|
130
146
|
const manifestRoles = new Map();
|
|
147
|
+
const manifestReadPolicies = new Map();
|
|
131
148
|
let schemaVersion = "4";
|
|
132
149
|
if (!(await pathExists(configPath))) {
|
|
133
150
|
errors.push(`${harnessPath(root, "config.yaml")} is missing`);
|
|
@@ -137,7 +154,7 @@ async function validateContext(projectRoot) {
|
|
|
137
154
|
const unsupportedSchema = unsupportedSchemaMessage(schemaVersion, "validate-context");
|
|
138
155
|
if (unsupportedSchema) {
|
|
139
156
|
errors.push(unsupportedSchema);
|
|
140
|
-
return { info, errors };
|
|
157
|
+
return { info, warnings, errors };
|
|
141
158
|
}
|
|
142
159
|
}
|
|
143
160
|
if (!(await pathExists(globalPath))) {
|
|
@@ -149,6 +166,7 @@ async function validateContext(projectRoot) {
|
|
|
149
166
|
assertSectionHasContent("project_context/global.md", global, sectionSpec("Verification Entry Points"), errors);
|
|
150
167
|
assertSectionHasContent("project_context/global.md", global, sectionSpec("Next Safe Action"), errors);
|
|
151
168
|
assertNoFakeVerification("project_context/global.md", global, errors);
|
|
169
|
+
validateContextFile(projectRoot, "project_context/global.md", global, "global", errors);
|
|
152
170
|
}
|
|
153
171
|
if (!(await pathExists(architecturePath))) {
|
|
154
172
|
errors.push("project_context/architecture.md is missing");
|
|
@@ -159,11 +177,14 @@ async function validateContext(projectRoot) {
|
|
|
159
177
|
assertSectionHasContent("project_context/architecture.md", architecture, sectionSpec("System Boundary"), errors);
|
|
160
178
|
assertSectionHasContent("project_context/architecture.md", architecture, sectionSpec("Component Map"), errors);
|
|
161
179
|
assertNoFakeVerification("project_context/architecture.md", architecture, errors);
|
|
180
|
+
validateContextFile(projectRoot, "project_context/architecture.md", architecture, "architecture", errors);
|
|
162
181
|
}
|
|
163
182
|
if (await pathExists(manifestPath)) {
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
183
|
+
const parsed = parseContextManifest(await readText(manifestPath));
|
|
184
|
+
errors.push(...parsed.errors);
|
|
185
|
+
if (parsed.manifest) {
|
|
186
|
+
await validateContextManifest(projectRoot, parsed.manifest, manifestRoles, manifestReadPolicies, errors);
|
|
187
|
+
const manifest = parsed.manifest;
|
|
167
188
|
info.push(`loaded project_context/context.toml with ${manifest.areas.length} area(s) and ${manifest.contexts.length} context node(s)`);
|
|
168
189
|
}
|
|
169
190
|
}
|
|
@@ -180,21 +201,35 @@ async function validateContext(projectRoot) {
|
|
|
180
201
|
const content = await readText(file);
|
|
181
202
|
const frontMatter = parseFrontMatter(content);
|
|
182
203
|
const frontMatterRole = frontMatterContextRole(frontMatter, relative, errors);
|
|
183
|
-
if (frontMatter.read_policy &&
|
|
204
|
+
if (frontMatter.read_policy &&
|
|
205
|
+
!VALID_READ_POLICIES.has(frontMatter.read_policy)) {
|
|
184
206
|
errors.push(`${relative} has unsupported read_policy: ${frontMatter.read_policy}`);
|
|
185
207
|
}
|
|
186
|
-
const
|
|
208
|
+
const manifestRole = manifestRoles.get(relative);
|
|
209
|
+
const manifestReadPolicy = manifestReadPolicies.get(relative);
|
|
210
|
+
if (manifestRole && frontMatterRole && manifestRole !== frontMatterRole) {
|
|
211
|
+
errors.push(`${relative} front matter context_role ${frontMatterRole} does not match manifest role ${manifestRole}`);
|
|
212
|
+
}
|
|
213
|
+
if (manifestReadPolicy &&
|
|
214
|
+
frontMatter.read_policy &&
|
|
215
|
+
manifestReadPolicy !== frontMatter.read_policy) {
|
|
216
|
+
errors.push(`${relative} front matter read_policy ${frontMatter.read_policy} does not match manifest read_policy ${manifestReadPolicy}`);
|
|
217
|
+
}
|
|
218
|
+
if (!manifestRole) {
|
|
219
|
+
warnings.push(`${relative} is an unregistered Context Markdown file; add it to project_context/context.toml or move it out of project_context/**`);
|
|
220
|
+
}
|
|
221
|
+
const role = manifestRole ?? frontMatterRole;
|
|
187
222
|
if (!role) {
|
|
188
223
|
continue;
|
|
189
224
|
}
|
|
190
|
-
validateContextFile(relative, content, role, errors);
|
|
225
|
+
validateContextFile(projectRoot, relative, content, role, errors);
|
|
191
226
|
validatedContextFiles.set(relative, role);
|
|
192
227
|
}
|
|
193
228
|
for (const [relative, role] of manifestRoles.entries()) {
|
|
194
229
|
if (!validatedContextFiles.has(relative)) {
|
|
195
230
|
const absolute = path.join(projectRoot, ...relative.split("/"));
|
|
196
231
|
if (await pathExists(absolute)) {
|
|
197
|
-
validateContextFile(relative, await readText(absolute), role, errors);
|
|
232
|
+
validateContextFile(projectRoot, relative, await readText(absolute), role, errors);
|
|
198
233
|
validatedContextFiles.set(relative, role);
|
|
199
234
|
}
|
|
200
235
|
}
|
|
@@ -203,52 +238,62 @@ async function validateContext(projectRoot) {
|
|
|
203
238
|
if (errors.length === 0) {
|
|
204
239
|
info.push("Minimal Context validation passed");
|
|
205
240
|
}
|
|
206
|
-
return { info, errors };
|
|
241
|
+
return { info, warnings, errors };
|
|
207
242
|
}
|
|
208
|
-
function validateContextFile(file, content, role, errors) {
|
|
209
|
-
void role;
|
|
243
|
+
function validateContextFile(projectRoot, file, content, role, errors) {
|
|
210
244
|
assertNoFakeVerification(file, content, errors);
|
|
245
|
+
assertMinimumRecoverability(file, content, errors);
|
|
246
|
+
assertRoleRecoverability(projectRoot, file, content, role, errors);
|
|
211
247
|
}
|
|
212
|
-
async function validateContextManifest(projectRoot, manifest, manifestRoles, errors) {
|
|
248
|
+
async function validateContextManifest(projectRoot, manifest, manifestRoles, manifestReadPolicies, errors) {
|
|
213
249
|
if (manifest.areas.length === 0) {
|
|
214
250
|
errors.push("project_context/context.toml must declare at least one [[areas]] entry");
|
|
215
251
|
}
|
|
216
|
-
|
|
252
|
+
const areaIds = new Set();
|
|
253
|
+
const registeredPaths = new Set();
|
|
254
|
+
const defaultAreas = manifest.areas.filter((area) => area.default);
|
|
255
|
+
if (manifest.areas.length > 0 && defaultAreas.length !== 1) {
|
|
256
|
+
errors.push(`project_context/context.toml must mark exactly one [[areas]] entry with default = true; found ${defaultAreas.length}`);
|
|
257
|
+
}
|
|
217
258
|
for (const area of manifest.areas) {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
const context = stringManifestValue(area, "context", "project_context/context.toml", errors);
|
|
221
|
-
optionalStringManifestValue(area, "kind", "project_context/context.toml", errors);
|
|
222
|
-
const defaultArea = optionalBooleanManifestValue(area, "default", "project_context/context.toml", errors);
|
|
223
|
-
hasDefaultArea = hasDefaultArea || defaultArea === true;
|
|
224
|
-
optionalStringArrayManifestValue(area, "forbidden_runtime_dependencies", "project_context/context.toml", errors);
|
|
225
|
-
if (id && context) {
|
|
226
|
-
await addManifestRole(projectRoot, manifestRoles, context, "area", `area ${id}`, errors);
|
|
259
|
+
if (areaIds.has(area.id)) {
|
|
260
|
+
errors.push(`project_context/context.toml has duplicate area id: ${area.id}`);
|
|
227
261
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
262
|
+
areaIds.add(area.id);
|
|
263
|
+
await validateManifestPath(projectRoot, area.root, projectRoot, `area ${area.id} root`, false, errors);
|
|
264
|
+
const relative = normalizeContextPath(area.context);
|
|
265
|
+
if (registeredPaths.has(relative)) {
|
|
266
|
+
errors.push(`project_context/context.toml has duplicate Context path: ${relative}`);
|
|
267
|
+
}
|
|
268
|
+
registeredPaths.add(relative);
|
|
269
|
+
await addManifestRole(projectRoot, manifestRoles, manifestReadPolicies, area.context, "area", undefined, `area ${area.id}`, errors);
|
|
231
270
|
}
|
|
232
271
|
for (const context of manifest.contexts) {
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
errors.push(`project_context/context.toml line ${context.line} has unsupported read_policy: ${readPolicy}`);
|
|
272
|
+
const relative = normalizeContextPath(context.path);
|
|
273
|
+
if (registeredPaths.has(relative)) {
|
|
274
|
+
errors.push(`project_context/context.toml has duplicate Context path: ${relative}`);
|
|
275
|
+
}
|
|
276
|
+
registeredPaths.add(relative);
|
|
277
|
+
if (context.read_policy && !VALID_READ_POLICIES.has(context.read_policy)) {
|
|
278
|
+
errors.push(`project_context/context.toml line ${context.line} has unsupported read_policy: ${context.read_policy}`);
|
|
241
279
|
}
|
|
242
|
-
const role =
|
|
243
|
-
if (
|
|
244
|
-
errors.push(`project_context/context.toml line ${context.line} has unsupported context role: ${
|
|
280
|
+
const role = normalizeRole(context.role);
|
|
281
|
+
if (!role) {
|
|
282
|
+
errors.push(`project_context/context.toml line ${context.line} has unsupported context role: ${context.role}`);
|
|
283
|
+
continue;
|
|
245
284
|
}
|
|
246
|
-
|
|
247
|
-
|
|
285
|
+
await addManifestRole(projectRoot, manifestRoles, manifestReadPolicies, context.path, role, context.read_policy, `context ${context.path}`, errors);
|
|
286
|
+
}
|
|
287
|
+
for (const context of manifest.contexts) {
|
|
288
|
+
for (const child of context.default_children) {
|
|
289
|
+
const normalizedChild = normalizeContextPath(child);
|
|
290
|
+
if (!registeredPaths.has(normalizedChild)) {
|
|
291
|
+
errors.push(`project_context/context.toml line ${context.line} default_children references unregistered Context path: ${child}`);
|
|
292
|
+
}
|
|
248
293
|
}
|
|
249
294
|
}
|
|
250
295
|
}
|
|
251
|
-
async function addManifestRole(projectRoot, roles, rawPath, role, source, errors) {
|
|
296
|
+
async function addManifestRole(projectRoot, roles, readPolicies, rawPath, role, readPolicy, source, errors) {
|
|
252
297
|
const relative = normalizeContextPath(rawPath);
|
|
253
298
|
if (looksLikeExportArtifact(relative)) {
|
|
254
299
|
errors.push(`project_context/context.toml ${source} must not reference temporary export artifact ${rawPath}; export artifacts belong in tmp/ty-context/context-exports/** and must not be registered as Context graph nodes or implementation-index`);
|
|
@@ -258,16 +303,44 @@ async function addManifestRole(projectRoot, roles, rawPath, role, source, errors
|
|
|
258
303
|
errors.push(`project_context/context.toml ${source} must reference a markdown file under project_context/: ${rawPath}`);
|
|
259
304
|
return;
|
|
260
305
|
}
|
|
261
|
-
|
|
262
|
-
if (existing && existing !== role) {
|
|
263
|
-
errors.push(`project_context/context.toml assigns conflicting roles to ${relative}: ${existing} and ${role}`);
|
|
306
|
+
if (!(await validateManifestPath(projectRoot, rawPath, path.join(projectRoot, "project_context"), source, true, errors))) {
|
|
264
307
|
return;
|
|
265
308
|
}
|
|
266
309
|
roles.set(relative, role);
|
|
267
|
-
if (
|
|
268
|
-
|
|
310
|
+
if (readPolicy) {
|
|
311
|
+
readPolicies.set(relative, readPolicy);
|
|
269
312
|
}
|
|
270
313
|
}
|
|
314
|
+
async function validateManifestPath(projectRoot, rawPath, allowedRoot, source, allowFile, errors) {
|
|
315
|
+
if (path.isAbsolute(rawPath) ||
|
|
316
|
+
rawPath.replace(/\\/g, "/").split("/").includes("..")) {
|
|
317
|
+
errors.push(`project_context/context.toml ${source} path must be relative and must not contain '..': ${rawPath}`);
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
const target = path.resolve(projectRoot, rawPath);
|
|
321
|
+
if (!isWithin(allowedRoot, target)) {
|
|
322
|
+
errors.push(`project_context/context.toml ${source} escapes its allowed root: ${rawPath}`);
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
if (!(await pathExists(target))) {
|
|
326
|
+
errors.push(`project_context/context.toml references missing ${allowFile ? "context file" : "area root"}: ${normalizeContextPath(rawPath)}`);
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
const realAllowedRoot = await realpath(allowedRoot);
|
|
330
|
+
const realTarget = await realpath(target);
|
|
331
|
+
if (!isWithin(realAllowedRoot, realTarget)) {
|
|
332
|
+
errors.push(`project_context/context.toml ${source} resolves through a symbolic link outside its allowed root: ${rawPath}`);
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
return true;
|
|
336
|
+
}
|
|
337
|
+
function isWithin(root, target) {
|
|
338
|
+
const relative = path.relative(root, target);
|
|
339
|
+
return (relative === "" ||
|
|
340
|
+
(!relative.startsWith(`..${path.sep}`) &&
|
|
341
|
+
relative !== ".." &&
|
|
342
|
+
!path.isAbsolute(relative)));
|
|
343
|
+
}
|
|
271
344
|
function assertSections(file, content, sections, errors) {
|
|
272
345
|
for (const section of sections) {
|
|
273
346
|
if (!hasAnyHeading(content, section)) {
|
|
@@ -282,22 +355,87 @@ function assertSectionHasContent(file, content, section, errors) {
|
|
|
282
355
|
}
|
|
283
356
|
}
|
|
284
357
|
function assertNoFakeVerification(file, content, errors) {
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
sectionBody(content, "Deployment Paths") ??
|
|
288
|
-
sectionBody(content, "Test Entry Points") ??
|
|
289
|
-
sectionBody(content, "Verification Implications") ??
|
|
290
|
-
sectionBody(content, "Tests");
|
|
291
|
-
if (!verification) {
|
|
358
|
+
const verificationSections = sectionBodiesByHeading(content, /(?:verification|tests?|deployment|验证|测试|部署)/i);
|
|
359
|
+
if (verificationSections.length === 0) {
|
|
292
360
|
return;
|
|
293
361
|
}
|
|
294
|
-
for (const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
362
|
+
for (const verification of verificationSections) {
|
|
363
|
+
for (const pattern of FAKE_VERIFICATION_PATTERNS) {
|
|
364
|
+
if (pattern.test(verification)) {
|
|
365
|
+
errors.push(`${file} must list verification entry points, not claim tests were already executed`);
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
298
368
|
}
|
|
299
369
|
}
|
|
300
370
|
}
|
|
371
|
+
function assertMinimumRecoverability(file, content, errors) {
|
|
372
|
+
if (!/^#\s+\S.+$/m.test(content)) {
|
|
373
|
+
errors.push(`${file} must contain a non-empty level-one title`);
|
|
374
|
+
}
|
|
375
|
+
const withoutFrontMatter = content.replace(/^---\s*\r?\n[\s\S]*?\r?\n---\s*\r?\n?/, "");
|
|
376
|
+
const facts = withoutFrontMatter
|
|
377
|
+
.split(/\r?\n/)
|
|
378
|
+
.map((line) => line.trim())
|
|
379
|
+
.filter((line) => line &&
|
|
380
|
+
!line.startsWith("#") &&
|
|
381
|
+
!line.startsWith("```") &&
|
|
382
|
+
!line.startsWith("<!--"))
|
|
383
|
+
.map((line) => line.replace(/^[-*+>]\s*/, "").trim())
|
|
384
|
+
.filter((line) => line.length >= 8)
|
|
385
|
+
.filter((line) => !/^(?:todo|tbd|placeholder|coming soon|待补充|占位|暂无)[.!。…\s-]*$/i.test(line));
|
|
386
|
+
if (facts.length === 0) {
|
|
387
|
+
errors.push(`${file} must contain at least one concrete fact paragraph and cannot be only TODO or placeholder text`);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
function assertRoleRecoverability(projectRoot, file, content, role, errors) {
|
|
391
|
+
if (role === "verification") {
|
|
392
|
+
const hasRepeatableEntry = /`[^`\r\n]+`|\b(?:npm|pnpm|yarn|make|node|python|pytest|cargo|go)\s+[^\r\n]+|(?:^|\s)(?:\.\/|[\w.-]+\/)[\w./-]+/im.test(content);
|
|
393
|
+
if (!hasRepeatableEntry) {
|
|
394
|
+
errors.push(`${file} verification Context must contain a command, repository path or repeatable entry point`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
if (role === "implementation-index") {
|
|
398
|
+
const candidates = [...content.matchAll(/`([^`]+)`/g)]
|
|
399
|
+
.map((match) => match[1])
|
|
400
|
+
.filter((value) => /[\\/]/.test(value) && !value.includes(" "));
|
|
401
|
+
const hasRepositoryPath = candidates.some((candidate) => {
|
|
402
|
+
const clean = candidate.replace(/[.:,;]+$/, "").replace(/\\/g, "/");
|
|
403
|
+
if (path.isAbsolute(clean) || clean.split("/").includes("..")) {
|
|
404
|
+
return false;
|
|
405
|
+
}
|
|
406
|
+
const target = path.resolve(projectRoot, clean);
|
|
407
|
+
return isWithin(projectRoot, target) && existsSync(target);
|
|
408
|
+
});
|
|
409
|
+
if (!hasRepositoryPath) {
|
|
410
|
+
errors.push(`${file} implementation-index Context must contain at least one existing repository path`);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
if (role === "contract" &&
|
|
414
|
+
!/(?:producer|consumer|input|output|schema|compatibility|constraint|must|shall|禁止|必须|输入|输出|兼容)/i.test(content)) {
|
|
415
|
+
errors.push(`${file} contract Context must state at least one constraint or input/output semantic`);
|
|
416
|
+
}
|
|
417
|
+
if (role === "decision-rationale") {
|
|
418
|
+
const hasDecision = /^##\s+(?:(?:Decision|Current Design Choice|Design Choice)(?:\b|\s|$)|决定(?:\s|$)|当前设计选择(?:\s|$))/im.test(content);
|
|
419
|
+
const hasReason = /^##\s+(?:(?:Reason|Rationale|Why)(?:\b|\s|$)|原因(?:\s|$)|理由(?:\s|$)|为什么(?:\s|$))/im.test(content);
|
|
420
|
+
if (!hasDecision || !hasReason) {
|
|
421
|
+
errors.push(`${file} decision-rationale Context must contain both a Decision and a Reason`);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
function sectionBodiesByHeading(content, headingPattern) {
|
|
426
|
+
const matches = [...content.matchAll(/^(#{2,6})\s+(.+?)\s*$/gm)];
|
|
427
|
+
const bodies = [];
|
|
428
|
+
for (const [index, match] of matches.entries()) {
|
|
429
|
+
if (!headingPattern.test(match[2])) {
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
const start = (match.index ?? 0) + match[0].length;
|
|
433
|
+
const next = matches[index + 1];
|
|
434
|
+
const end = next?.index ?? content.length;
|
|
435
|
+
bodies.push(content.slice(start, end).trim());
|
|
436
|
+
}
|
|
437
|
+
return bodies;
|
|
438
|
+
}
|
|
301
439
|
function hasAnyHeading(content, section) {
|
|
302
440
|
return section.headings.some((heading) => hasHeading(content, heading));
|
|
303
441
|
}
|
|
@@ -364,118 +502,6 @@ function parseFrontMatter(content) {
|
|
|
364
502
|
}
|
|
365
503
|
return {};
|
|
366
504
|
}
|
|
367
|
-
function parseContextManifest(content, file, errors) {
|
|
368
|
-
const manifest = { areas: [], contexts: [] };
|
|
369
|
-
let current;
|
|
370
|
-
const lines = content.split(/\r?\n/);
|
|
371
|
-
for (let index = 0; index < lines.length; index += 1) {
|
|
372
|
-
const lineNumber = index + 1;
|
|
373
|
-
const line = stripTomlComment(lines[index]).trim();
|
|
374
|
-
if (!line) {
|
|
375
|
-
continue;
|
|
376
|
-
}
|
|
377
|
-
const tableMatch = /^\[\[(areas|context)\]\]$/.exec(line);
|
|
378
|
-
if (tableMatch) {
|
|
379
|
-
current = { table: tableMatch[1], line: lineNumber, values: {} };
|
|
380
|
-
if (current.table === "areas") {
|
|
381
|
-
manifest.areas.push(current);
|
|
382
|
-
}
|
|
383
|
-
else {
|
|
384
|
-
manifest.contexts.push(current);
|
|
385
|
-
}
|
|
386
|
-
continue;
|
|
387
|
-
}
|
|
388
|
-
if (!current) {
|
|
389
|
-
errors.push(`${file} line ${lineNumber} must appear inside [[areas]] or [[context]]`);
|
|
390
|
-
continue;
|
|
391
|
-
}
|
|
392
|
-
const assignment = /^([A-Za-z0-9_-]+)\s*=\s*(.+)$/.exec(line);
|
|
393
|
-
if (!assignment) {
|
|
394
|
-
errors.push(`${file} line ${lineNumber} is not a supported manifest assignment`);
|
|
395
|
-
continue;
|
|
396
|
-
}
|
|
397
|
-
const parsed = parseTomlValue(assignment[2]);
|
|
398
|
-
if (parsed === undefined) {
|
|
399
|
-
errors.push(`${file} line ${lineNumber} has an unsupported value; use strings, booleans or string arrays`);
|
|
400
|
-
continue;
|
|
401
|
-
}
|
|
402
|
-
current.values[assignment[1]] = parsed;
|
|
403
|
-
}
|
|
404
|
-
return manifest;
|
|
405
|
-
}
|
|
406
|
-
function parseTomlValue(raw) {
|
|
407
|
-
const value = raw.trim();
|
|
408
|
-
if (value === "true") {
|
|
409
|
-
return true;
|
|
410
|
-
}
|
|
411
|
-
if (value === "false") {
|
|
412
|
-
return false;
|
|
413
|
-
}
|
|
414
|
-
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
415
|
-
return stripQuotes(value);
|
|
416
|
-
}
|
|
417
|
-
if (value.startsWith("[") && value.endsWith("]")) {
|
|
418
|
-
const inner = value.slice(1, -1).trim();
|
|
419
|
-
if (!inner) {
|
|
420
|
-
return [];
|
|
421
|
-
}
|
|
422
|
-
const values = [];
|
|
423
|
-
for (const part of inner.split(",")) {
|
|
424
|
-
const item = part.trim();
|
|
425
|
-
if (!((item.startsWith('"') && item.endsWith('"')) || (item.startsWith("'") && item.endsWith("'")))) {
|
|
426
|
-
return undefined;
|
|
427
|
-
}
|
|
428
|
-
values.push(stripQuotes(item));
|
|
429
|
-
}
|
|
430
|
-
return values;
|
|
431
|
-
}
|
|
432
|
-
return undefined;
|
|
433
|
-
}
|
|
434
|
-
function stripTomlComment(line) {
|
|
435
|
-
let quote;
|
|
436
|
-
for (let index = 0; index < line.length; index += 1) {
|
|
437
|
-
const char = line[index];
|
|
438
|
-
if ((char === '"' || char === "'") && line[index - 1] !== "\\") {
|
|
439
|
-
quote = quote === char ? undefined : quote ?? char;
|
|
440
|
-
}
|
|
441
|
-
if (char === "#" && !quote) {
|
|
442
|
-
return line.slice(0, index);
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
return line;
|
|
446
|
-
}
|
|
447
|
-
function stringManifestValue(entry, key, file, errors) {
|
|
448
|
-
const value = entry.values[key];
|
|
449
|
-
if (typeof value === "string") {
|
|
450
|
-
return value;
|
|
451
|
-
}
|
|
452
|
-
errors.push(`${file} line ${entry.line} must include string field ${key}`);
|
|
453
|
-
return undefined;
|
|
454
|
-
}
|
|
455
|
-
function optionalStringManifestValue(entry, key, file, errors) {
|
|
456
|
-
const value = entry.values[key];
|
|
457
|
-
if (value === undefined || typeof value === "string") {
|
|
458
|
-
return value;
|
|
459
|
-
}
|
|
460
|
-
errors.push(`${file} line ${entry.line} field ${key} must be a string`);
|
|
461
|
-
return undefined;
|
|
462
|
-
}
|
|
463
|
-
function optionalBooleanManifestValue(entry, key, file, errors) {
|
|
464
|
-
const value = entry.values[key];
|
|
465
|
-
if (value === undefined || typeof value === "boolean") {
|
|
466
|
-
return value;
|
|
467
|
-
}
|
|
468
|
-
errors.push(`${file} line ${entry.line} field ${key} must be a boolean`);
|
|
469
|
-
return undefined;
|
|
470
|
-
}
|
|
471
|
-
function optionalStringArrayManifestValue(entry, key, file, errors) {
|
|
472
|
-
const value = entry.values[key];
|
|
473
|
-
if (value === undefined || (Array.isArray(value) && value.every((item) => typeof item === "string"))) {
|
|
474
|
-
return value;
|
|
475
|
-
}
|
|
476
|
-
errors.push(`${file} line ${entry.line} field ${key} must be an array of strings`);
|
|
477
|
-
return undefined;
|
|
478
|
-
}
|
|
479
505
|
function normalizeRole(value) {
|
|
480
506
|
return ROLE_ALIASES[value.trim().toLowerCase()];
|
|
481
507
|
}
|
|
@@ -487,7 +513,8 @@ function looksLikeExportArtifact(value) {
|
|
|
487
513
|
return EXPORT_ARTIFACT_NAME_PATTERNS.some((pattern) => pattern.test(normalized));
|
|
488
514
|
}
|
|
489
515
|
function stripQuotes(value) {
|
|
490
|
-
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
516
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
517
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
491
518
|
return value.slice(1, -1);
|
|
492
519
|
}
|
|
493
520
|
return value;
|