project-tiny-context-harness 0.2.84 → 0.6.0
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 +163 -428
- package/assets/README.md +283 -455
- package/assets/README.zh-CN.md +199 -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 +62 -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 +379 -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 -157
- 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 +15 -3
- 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 -244
- package/assets/skills/composite-long-task-workflow/assets/execution-binding.template.md +0 -57
- 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 -675
- 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 -52
- package/dist/lib/superpowers-task-completion-output.js +0 -228
- 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 -382
- 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 -24
- package/dist/lib/superpowers-task-gates.d.ts +0 -12
- package/dist/lib/superpowers-task-gates.js +0 -155
- 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 -440
- 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 -290
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { compileDeliveryContract } from "./long-task-delivery-compiler.js";
|
|
2
|
+
import { activeAuthorityIdentityMatches, assertMatchingActiveBinding, loadActiveLongTaskAuthority, writeFinalReceipt, } from "./long-task-state.js";
|
|
3
|
+
import { assertVerifierAuthorityCurrent } from "./long-task-freshness.js";
|
|
4
|
+
import { allCompiledChecks, runDeliveryChecks, } from "./long-task-verifier-v2.js";
|
|
5
|
+
import { captureWorkspaceFingerprint, createWorkspaceSnapshot, currentGitState, repositoryRoot, } from "./long-task-workspace.js";
|
|
6
|
+
export async function runDeliveryFinalGate(workdirInput) {
|
|
7
|
+
const startedAt = new Date().toISOString();
|
|
8
|
+
const repository = await repositoryRoot(process.cwd());
|
|
9
|
+
const active = (await loadActiveLongTaskAuthority(repository, { migrate_legacy: true })).authority;
|
|
10
|
+
if (!active)
|
|
11
|
+
throw new Error("active_task_missing");
|
|
12
|
+
if (active.workdir !== (await resolved(workdirInput)))
|
|
13
|
+
throw new Error("active_task_workdir_mismatch");
|
|
14
|
+
await assertVerifierAuthorityCurrent(repository, active.verifier_identity);
|
|
15
|
+
const acceptedAuthority = {
|
|
16
|
+
task_id: active.task_id,
|
|
17
|
+
authority_revision: active.authority_revision,
|
|
18
|
+
compiled_identity: active.active_authority_identity,
|
|
19
|
+
worktree_identity: active.worktree_identity,
|
|
20
|
+
};
|
|
21
|
+
const candidate = await currentGitState(repository);
|
|
22
|
+
if (candidate.dirty.length)
|
|
23
|
+
throw new Error(`final_gate_requires_clean_candidate_commit:${candidate.dirty.join(",")}`);
|
|
24
|
+
const compiled = await compileDeliveryContract(active.workdir, repository, {
|
|
25
|
+
live_gate: true,
|
|
26
|
+
initial_task_base: active.initial_task_base,
|
|
27
|
+
authority_revision: active.authority_revision,
|
|
28
|
+
require_completion_gate: true,
|
|
29
|
+
});
|
|
30
|
+
await assertMatchingActiveBinding(compiled);
|
|
31
|
+
const before = await captureWorkspaceFingerprint(repository);
|
|
32
|
+
const snapshot = await createWorkspaceSnapshot(repository, active.workdir, `final-${compiled.task.id}`);
|
|
33
|
+
try {
|
|
34
|
+
const run = await runDeliveryChecks(compiled, snapshot, allCompiledChecks(compiled), true, true);
|
|
35
|
+
const after = await captureWorkspaceFingerprint(repository);
|
|
36
|
+
const gitAfter = await currentGitState(repository);
|
|
37
|
+
if (after.identity !== before.identity ||
|
|
38
|
+
gitAfter.head !== candidate.head ||
|
|
39
|
+
gitAfter.tree !== candidate.tree ||
|
|
40
|
+
gitAfter.dirty.length)
|
|
41
|
+
run.findings.push({
|
|
42
|
+
code: "workspace_changed_during_final_gate",
|
|
43
|
+
outcome_key: null,
|
|
44
|
+
check_key: null,
|
|
45
|
+
message: "The workspace changed while Live Final Gate was running.",
|
|
46
|
+
next_action: "Stop concurrent mutation and rerun the complete Live Final Gate.",
|
|
47
|
+
});
|
|
48
|
+
let activeAuthorityChanged = false;
|
|
49
|
+
try {
|
|
50
|
+
const currentActive = (await loadActiveLongTaskAuthority(repository))
|
|
51
|
+
.authority;
|
|
52
|
+
activeAuthorityChanged =
|
|
53
|
+
!currentActive ||
|
|
54
|
+
!activeAuthorityIdentityMatches(currentActive, acceptedAuthority);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
activeAuthorityChanged = true;
|
|
58
|
+
}
|
|
59
|
+
if (activeAuthorityChanged)
|
|
60
|
+
run.findings.push({
|
|
61
|
+
code: "active_authority_changed_during_final_gate",
|
|
62
|
+
outcome_key: null,
|
|
63
|
+
check_key: null,
|
|
64
|
+
message: "Active Authority changed while Live Final Gate was running.",
|
|
65
|
+
next_action: "Review the new Authority Revision and rerun the complete Live Final Gate.",
|
|
66
|
+
});
|
|
67
|
+
const outcomeResults = projectOutcomes(compiled.outcomes.map((outcome) => outcome.key), run.check_results, run.findings);
|
|
68
|
+
const globalChecks = run.check_results.filter((check) => check.outcome_key === null);
|
|
69
|
+
const globalHardFailure = globalChecks.some((check) => check.status !== "passed" && check.status !== "blocked_external");
|
|
70
|
+
const globalBlocked = globalChecks.some((check) => check.status === "blocked_external");
|
|
71
|
+
const failed = globalHardFailure ||
|
|
72
|
+
Object.values(outcomeResults).includes("failed") ||
|
|
73
|
+
run.findings.some((finding) => finding.code !== "blocked_external");
|
|
74
|
+
const blocked = globalBlocked ||
|
|
75
|
+
Object.values(outcomeResults).includes("blocked_external") ||
|
|
76
|
+
run.findings.some((finding) => finding.code === "blocked_external");
|
|
77
|
+
const workflowStatus = failed
|
|
78
|
+
? "needs_work"
|
|
79
|
+
: blocked
|
|
80
|
+
? "blocked_external"
|
|
81
|
+
: compiled.global.acceptance.external_confirmations.length
|
|
82
|
+
? "machine_accepted_external_pending"
|
|
83
|
+
: "machine_accepted";
|
|
84
|
+
return writeFinalReceipt(repository, active.workdir, {
|
|
85
|
+
schema_version: "long-task-final-receipt-v2",
|
|
86
|
+
authority_scope: "audit_only",
|
|
87
|
+
reusable_for_acceptance: false,
|
|
88
|
+
workflow_status: workflowStatus,
|
|
89
|
+
compiled_identity: compiled.compiled_identity,
|
|
90
|
+
contract_sha256: compiled.contract_sha256,
|
|
91
|
+
snapshot_sha256: snapshot.manifest.snapshot_sha256,
|
|
92
|
+
git_head: candidate.head,
|
|
93
|
+
git_tree: candidate.tree,
|
|
94
|
+
source_hashes: compiled.source_hashes,
|
|
95
|
+
context_hashes: compiled.context_snapshot.sha256,
|
|
96
|
+
verifier_identity: compiled.verifier_identity,
|
|
97
|
+
check_results: run.check_results,
|
|
98
|
+
outcome_results: outcomeResults,
|
|
99
|
+
external_confirmations: compiled.global.acceptance.external_confirmations,
|
|
100
|
+
findings: run.findings,
|
|
101
|
+
snapshot_preparation_ms: snapshot.preparation_ms,
|
|
102
|
+
started_at: startedAt,
|
|
103
|
+
completed_at: new Date().toISOString(),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
await snapshot.dispose();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function projectOutcomes(outcomeKeys, checks, findings) {
|
|
111
|
+
return Object.fromEntries(outcomeKeys.map((outcomeKey) => {
|
|
112
|
+
const owned = checks.filter((check) => check.outcome_key === outcomeKey);
|
|
113
|
+
const ownFindings = findings.filter((finding) => finding.outcome_key === outcomeKey || finding.outcome_key === null);
|
|
114
|
+
const status = owned.some((check) => check.status !== "passed" && check.status !== "blocked_external") || ownFindings.some((finding) => finding.code !== "blocked_external")
|
|
115
|
+
? "failed"
|
|
116
|
+
: owned.some((check) => check.status === "blocked_external")
|
|
117
|
+
? "blocked_external"
|
|
118
|
+
: "passed";
|
|
119
|
+
return [outcomeKey, status];
|
|
120
|
+
}));
|
|
121
|
+
}
|
|
122
|
+
async function resolved(workdir) {
|
|
123
|
+
return (await import("node:path")).default.resolve(workdir);
|
|
124
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { CheckExecutionResultV2, CompiledDeliveryContractV2, LongTaskFindingV2 } from "./long-task-delivery-types.js";
|
|
2
|
+
export declare function enrichCheckResultFindings(compiled: CompiledDeliveryContractV2, result: CheckExecutionResultV2): CheckExecutionResultV2;
|
|
3
|
+
export declare function enrichFinding(compiled: CompiledDeliveryContractV2, finding: LongTaskFindingV2): LongTaskFindingV2;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { resolveAcceptanceAssertion } from "./long-task-acceptance-reference.js";
|
|
2
|
+
export function enrichCheckResultFindings(compiled, result) {
|
|
3
|
+
return {
|
|
4
|
+
...result,
|
|
5
|
+
findings: result.findings.map((finding) => enrichFinding(compiled, finding)),
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
export function enrichFinding(compiled, finding) {
|
|
9
|
+
const outcome = finding.outcome_key
|
|
10
|
+
? compiled.outcomes.find((item) => item.key === finding.outcome_key)
|
|
11
|
+
: null;
|
|
12
|
+
const sourceClaims = sourceClaimsForFinding(compiled, finding);
|
|
13
|
+
const sourceTargets = compiled.source_claims
|
|
14
|
+
.filter((source) => sourceClaims.includes(source.key))
|
|
15
|
+
.flatMap((source) => dispositionTargetRefs(source.disposition))
|
|
16
|
+
.filter((reference, index, all) => all.indexOf(reference) === index)
|
|
17
|
+
.sort();
|
|
18
|
+
return {
|
|
19
|
+
...finding,
|
|
20
|
+
...(sourceClaims.length ? { source_claim_keys: sourceClaims } : {}),
|
|
21
|
+
...(sourceTargets.length ? { source_target_refs: sourceTargets } : {}),
|
|
22
|
+
...(outcome && !finding.owner_paths
|
|
23
|
+
? { owner_paths: outcome.product.owner.path_globs }
|
|
24
|
+
: {}),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function dispositionTargetRefs(disposition) {
|
|
28
|
+
if (disposition.type === "outcome_result")
|
|
29
|
+
return [disposition.ref];
|
|
30
|
+
if (disposition.type === "claim" ||
|
|
31
|
+
disposition.type === "acceptance" ||
|
|
32
|
+
disposition.type === "global_constraint" ||
|
|
33
|
+
disposition.type === "risk_fact" ||
|
|
34
|
+
disposition.type === "external_confirmation")
|
|
35
|
+
return disposition.refs;
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
function sourceClaimsForFinding(compiled, finding) {
|
|
39
|
+
const acceptanceRef = finding.check_key && finding.assertion_key
|
|
40
|
+
? `${finding.outcome_key ?? "GLOBAL"}.${finding.check_key}.${finding.assertion_key}`
|
|
41
|
+
: null;
|
|
42
|
+
const resolvedAcceptance = acceptanceRef
|
|
43
|
+
? resolveAcceptanceAssertion(compiled, acceptanceRef)
|
|
44
|
+
: null;
|
|
45
|
+
const productRefs = new Set((finding.claim_keys ?? []).map((claim) => finding.outcome_key ? `${finding.outcome_key}.${claim}` : claim));
|
|
46
|
+
return compiled.source_claims
|
|
47
|
+
.filter((source) => {
|
|
48
|
+
if (source.disposition.type === "acceptance" &&
|
|
49
|
+
resolvedAcceptance &&
|
|
50
|
+
source.disposition.refs.includes(resolvedAcceptance.ref))
|
|
51
|
+
return true;
|
|
52
|
+
if (source.disposition.type === "claim" &&
|
|
53
|
+
source.disposition.refs.some((reference) => productRefs.has(reference)))
|
|
54
|
+
return true;
|
|
55
|
+
if (source.disposition.type === "outcome_result" &&
|
|
56
|
+
productRefs.has(source.disposition.ref))
|
|
57
|
+
return true;
|
|
58
|
+
return (source.disposition.type === "global_constraint" &&
|
|
59
|
+
source.disposition.refs.some((reference) => productRefs.has(reference)));
|
|
60
|
+
})
|
|
61
|
+
.map((source) => source.key)
|
|
62
|
+
.sort();
|
|
63
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { CompiledDeliveryContractV2, VerifierIdentityV2 } from "./long-task-delivery-types.js";
|
|
2
|
+
export declare function deliveryCompileFreshness(compiled: CompiledDeliveryContractV2): Promise<string[]>;
|
|
3
|
+
export declare function assertVerifierAuthorityCurrent(repositoryRoot: string, expected: VerifierIdentityV2): Promise<void>;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { captureContextGraphSnapshot } from "./context-graph-snapshot.js";
|
|
4
|
+
import { canonicalValueJson, sha256Hex } from "./strict-codec.js";
|
|
5
|
+
import { captureVerifierIdentity } from "./long-task-verifier-identity.js";
|
|
6
|
+
import { verifierAuthorityDiff } from "./long-task-verifier-authority.js";
|
|
7
|
+
import { parseDeliveryContractBundle } from "./long-task-delivery-parser.js";
|
|
8
|
+
export async function deliveryCompileFreshness(compiled) {
|
|
9
|
+
const findings = [];
|
|
10
|
+
try {
|
|
11
|
+
const currentContract = await parseDeliveryContractBundle(compiled.workdir);
|
|
12
|
+
if (sha256Hex(canonicalValueJson(currentContract.contract)) !==
|
|
13
|
+
compiled.contract_sha256)
|
|
14
|
+
findings.push(`contract_changed_after_compile:${compiled.contract_file}`);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
findings.push(`contract_changed_after_compile:${compiled.contract_file}`);
|
|
18
|
+
}
|
|
19
|
+
for (const [file, hash] of Object.entries(compiled.contract_files))
|
|
20
|
+
await compareFile(path.join(compiled.repository_root, file), hash, `contract_changed_after_compile:${file}`, findings);
|
|
21
|
+
for (const [file, hash] of Object.entries(compiled.source_hashes))
|
|
22
|
+
await compareFile(path.join(compiled.repository_root, file), hash, `source_changed_after_compile:${file}`, findings);
|
|
23
|
+
try {
|
|
24
|
+
const current = await captureContextGraphSnapshot(compiled.repository_root, compiled.task.context_refs, compiled.context_snapshot.mode);
|
|
25
|
+
if (current.topology_sha256 !== compiled.context_snapshot.topology_sha256)
|
|
26
|
+
findings.push("context_changed_after_compile:topology");
|
|
27
|
+
if (canonicalValueJson(current.files) !==
|
|
28
|
+
canonicalValueJson(compiled.context_snapshot.files))
|
|
29
|
+
findings.push("context_changed_after_compile:file_set");
|
|
30
|
+
for (const [file, hash] of Object.entries(compiled.context_snapshot.sha256))
|
|
31
|
+
if (current.sha256[file] !== hash)
|
|
32
|
+
findings.push(`context_changed_after_compile:${file}`);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
findings.push(`context_changed_after_compile:${message(error)}`);
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const currentVerifier = await captureVerifierIdentity(compiled.repository_root, compiled.verifier_identity.hook_sha256 !== "not-required");
|
|
39
|
+
const verifierDiff = verifierAuthorityDiff(compiled.verifier_identity, currentVerifier);
|
|
40
|
+
if (verifierDiff.verifier_content_changed)
|
|
41
|
+
findings.push("verifier_changed_after_compile:bundle");
|
|
42
|
+
if (verifierDiff.verifier_runtime_locator_changed)
|
|
43
|
+
findings.push("verifier_changed_after_compile:runtime_locator");
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
findings.push("verifier_changed_after_compile:bundle");
|
|
47
|
+
}
|
|
48
|
+
const checks = [
|
|
49
|
+
...compiled.global.acceptance.checks,
|
|
50
|
+
...compiled.outcomes.flatMap((outcome) => outcome.acceptance.checks),
|
|
51
|
+
];
|
|
52
|
+
for (const check of checks)
|
|
53
|
+
for (const [file, hash] of Object.entries(check.runner.frozen_files))
|
|
54
|
+
await compareFile(path.join(compiled.repository_root, file), hash, `runner_changed_after_compile:${check.internal_id}:${file}`, findings);
|
|
55
|
+
return [...new Set(findings)].sort();
|
|
56
|
+
}
|
|
57
|
+
export async function assertVerifierAuthorityCurrent(repositoryRoot, expected) {
|
|
58
|
+
let current;
|
|
59
|
+
try {
|
|
60
|
+
current = await captureVerifierIdentity(repositoryRoot, expected.hook_sha256 !== "not-required");
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
throw new Error("verifier_authority_migration_required");
|
|
64
|
+
}
|
|
65
|
+
const diff = verifierAuthorityDiff(expected, current);
|
|
66
|
+
if (diff.verifier_content_changed || diff.verifier_runtime_locator_changed)
|
|
67
|
+
throw new Error("verifier_authority_migration_required");
|
|
68
|
+
}
|
|
69
|
+
async function compareFile(file, expected, finding, findings) {
|
|
70
|
+
try {
|
|
71
|
+
if (sha256Hex(await readFile(file)) !== expected)
|
|
72
|
+
findings.push(finding);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
findings.push(finding);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function message(error) {
|
|
79
|
+
return error instanceof Error ? error.message : String(error);
|
|
80
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SyncReport } from "./sync-engine.js";
|
|
2
|
+
export declare function installLongTaskHooks(projectRoot: string, report: SyncReport): Promise<void>;
|
|
3
|
+
export declare function uninstallLongTaskHooks(projectRoot: string, report: SyncReport): Promise<void>;
|
|
4
|
+
export declare function packageOwnedCommand(entry: string): string;
|
|
5
|
+
export declare function removeManagedHookEntries(groups: unknown, currentPackageCommand?: string): {
|
|
6
|
+
groups: unknown[];
|
|
7
|
+
removed: number;
|
|
8
|
+
};
|
|
9
|
+
export declare function isManagedHookEntry(entry: unknown, currentPackageCommand?: string): boolean;
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { pathExists } from "./fs.js";
|
|
5
|
+
const MANAGED_STATUS = "Tiny Context long-task live authority gate";
|
|
6
|
+
const LEGACY_MANAGED_STATUSES = new Set([
|
|
7
|
+
"Tiny Context long-task live authority gate",
|
|
8
|
+
"Tiny Context long-task authority gate",
|
|
9
|
+
"Tiny Context long-task completion gate",
|
|
10
|
+
"Tiny Context composite completion gate",
|
|
11
|
+
]);
|
|
12
|
+
const LEGACY_REPO_LOCAL_COMMANDS = new Set([
|
|
13
|
+
'node "$(git rev-parse --show-toplevel)/.codex/hooks/long-task-hook.mjs"',
|
|
14
|
+
"powershell -NoProfile -Command \"$r=(git rev-parse --show-toplevel); node (Join-Path $r '.codex/hooks/long-task-hook.mjs')\"",
|
|
15
|
+
"node .codex/hooks/long-task-hook.mjs",
|
|
16
|
+
'node ".codex/hooks/long-task-hook.mjs"',
|
|
17
|
+
"node .codex\\hooks\\long-task-hook.mjs",
|
|
18
|
+
'node ".codex\\hooks\\long-task-hook.mjs"',
|
|
19
|
+
]);
|
|
20
|
+
export async function installLongTaskHooks(projectRoot, report) {
|
|
21
|
+
const packageRoot = fileURLToPath(new URL("../../", import.meta.url));
|
|
22
|
+
const entry = path.join(packageRoot, "dist", "long-task-hook.js");
|
|
23
|
+
if (!(await pathExists(entry))) {
|
|
24
|
+
report.blocked.push("package-owned dist/long-task-hook.js is missing");
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const legacyScript = path.join(projectRoot, ".codex", "hooks", "long-task-hook.mjs");
|
|
28
|
+
if (await pathExists(legacyScript)) {
|
|
29
|
+
await rm(legacyScript, { force: true });
|
|
30
|
+
report.changed.push(".codex/hooks/long-task-hook.mjs");
|
|
31
|
+
}
|
|
32
|
+
const command = packageOwnedCommand(entry);
|
|
33
|
+
const config = path.join(projectRoot, ".codex", "hooks.json");
|
|
34
|
+
let root = {};
|
|
35
|
+
if (await pathExists(config)) {
|
|
36
|
+
try {
|
|
37
|
+
root = JSON.parse(await readFile(config, "utf8"));
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
report.blocked.push(".codex/hooks.json: invalid JSON");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const hooks = object(root.hooks);
|
|
45
|
+
root.hooks = hooks;
|
|
46
|
+
for (const event of ["SessionStart", "PostCompact", "Stop"]) {
|
|
47
|
+
const groups = Array.isArray(hooks[event]) ? hooks[event] : [];
|
|
48
|
+
const cleaned = removeManagedHookEntries(groups, command);
|
|
49
|
+
cleaned.groups.push({
|
|
50
|
+
hooks: [
|
|
51
|
+
{
|
|
52
|
+
type: "command",
|
|
53
|
+
command,
|
|
54
|
+
commandWindows: command,
|
|
55
|
+
timeout: event === "Stop" ? 3600 : 10,
|
|
56
|
+
statusMessage: MANAGED_STATUS,
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
});
|
|
60
|
+
hooks[event] = cleaned.groups;
|
|
61
|
+
}
|
|
62
|
+
if (await writeIfChanged(config, `${JSON.stringify(root, null, 2)}\n`))
|
|
63
|
+
report.changed.push(".codex/hooks.json");
|
|
64
|
+
else
|
|
65
|
+
report.skipped.push(".codex/hooks.json");
|
|
66
|
+
}
|
|
67
|
+
export async function uninstallLongTaskHooks(projectRoot, report) {
|
|
68
|
+
const packageRoot = fileURLToPath(new URL("../../", import.meta.url));
|
|
69
|
+
const command = packageOwnedCommand(path.join(packageRoot, "dist", "long-task-hook.js"));
|
|
70
|
+
const legacyScript = path.join(projectRoot, ".codex", "hooks", "long-task-hook.mjs");
|
|
71
|
+
if (await pathExists(legacyScript)) {
|
|
72
|
+
await rm(legacyScript, { force: true });
|
|
73
|
+
report.changed.push(".codex/hooks/long-task-hook.mjs");
|
|
74
|
+
}
|
|
75
|
+
const config = path.join(projectRoot, ".codex", "hooks.json");
|
|
76
|
+
if (!(await pathExists(config)))
|
|
77
|
+
return;
|
|
78
|
+
let root;
|
|
79
|
+
try {
|
|
80
|
+
root = JSON.parse(await readFile(config, "utf8"));
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
report.skipped.push(".codex/hooks.json: invalid JSON");
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const hooks = object(root.hooks);
|
|
87
|
+
let changed = false;
|
|
88
|
+
for (const event of ["SessionStart", "PostCompact", "Stop"]) {
|
|
89
|
+
if (!Array.isArray(hooks[event]))
|
|
90
|
+
continue;
|
|
91
|
+
const cleaned = removeManagedHookEntries(hooks[event], command);
|
|
92
|
+
if (cleaned.removed === 0)
|
|
93
|
+
continue;
|
|
94
|
+
changed = true;
|
|
95
|
+
if (cleaned.groups.length)
|
|
96
|
+
hooks[event] = cleaned.groups;
|
|
97
|
+
else
|
|
98
|
+
delete hooks[event];
|
|
99
|
+
}
|
|
100
|
+
if (!changed)
|
|
101
|
+
return;
|
|
102
|
+
root.hooks = hooks;
|
|
103
|
+
if (Object.keys(hooks).length === 0 && Object.keys(root).length === 1)
|
|
104
|
+
await rm(config, { force: true });
|
|
105
|
+
else
|
|
106
|
+
await writeIfChanged(config, `${JSON.stringify(root, null, 2)}\n`);
|
|
107
|
+
report.changed.push(".codex/hooks.json");
|
|
108
|
+
}
|
|
109
|
+
export function packageOwnedCommand(entry) {
|
|
110
|
+
return `node "${path.resolve(entry).replace(/"/gu, '\\"')}"`;
|
|
111
|
+
}
|
|
112
|
+
export function removeManagedHookEntries(groups, currentPackageCommand = "") {
|
|
113
|
+
if (!Array.isArray(groups))
|
|
114
|
+
return { groups: [], removed: 0 };
|
|
115
|
+
const retained = [];
|
|
116
|
+
let removed = 0;
|
|
117
|
+
for (const groupValue of groups) {
|
|
118
|
+
if (!isObject(groupValue) || !Array.isArray(groupValue.hooks)) {
|
|
119
|
+
retained.push(groupValue);
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
const hooks = groupValue.hooks.filter((entry) => {
|
|
123
|
+
if (!isManagedHookEntry(entry, currentPackageCommand))
|
|
124
|
+
return true;
|
|
125
|
+
removed += 1;
|
|
126
|
+
return false;
|
|
127
|
+
});
|
|
128
|
+
if (hooks.length > 0) {
|
|
129
|
+
retained.push({ ...groupValue, hooks });
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (Object.keys(groupValue).some((key) => key !== "hooks"))
|
|
133
|
+
retained.push({ ...groupValue, hooks });
|
|
134
|
+
}
|
|
135
|
+
return { groups: retained, removed };
|
|
136
|
+
}
|
|
137
|
+
export function isManagedHookEntry(entry, currentPackageCommand = "") {
|
|
138
|
+
const row = object(entry);
|
|
139
|
+
if (row.type !== "command")
|
|
140
|
+
return false;
|
|
141
|
+
const commands = [row.command, row.commandWindows]
|
|
142
|
+
.filter((value) => typeof value === "string")
|
|
143
|
+
.map((value) => value.trim());
|
|
144
|
+
const repoLocal = commands.some((command) => LEGACY_REPO_LOCAL_COMMANDS.has(command));
|
|
145
|
+
if (repoLocal)
|
|
146
|
+
return true;
|
|
147
|
+
const status = String(row.statusMessage ?? "");
|
|
148
|
+
if (!LEGACY_MANAGED_STATUSES.has(status))
|
|
149
|
+
return false;
|
|
150
|
+
return commands.some((command) => (Boolean(currentPackageCommand) && command === currentPackageCommand) ||
|
|
151
|
+
isHistoricalPackageOwnedCommand(command));
|
|
152
|
+
}
|
|
153
|
+
function isHistoricalPackageOwnedCommand(command) {
|
|
154
|
+
const match = /^node "([^"\r\n]+)"$/u.exec(command);
|
|
155
|
+
if (!match)
|
|
156
|
+
return false;
|
|
157
|
+
const absolute = match[1];
|
|
158
|
+
if (!path.isAbsolute(absolute) &&
|
|
159
|
+
!path.win32.isAbsolute(absolute) &&
|
|
160
|
+
!path.posix.isAbsolute(absolute))
|
|
161
|
+
return false;
|
|
162
|
+
const normalized = absolute.replace(/\\/gu, "/");
|
|
163
|
+
return (/\/node_modules\/project-tiny-context-harness\/dist\/long-task-hook\.js$/u.test(normalized) || /\/packages\/ty-context\/dist\/long-task-hook\.js$/u.test(normalized));
|
|
164
|
+
}
|
|
165
|
+
function object(value) {
|
|
166
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
167
|
+
? value
|
|
168
|
+
: {};
|
|
169
|
+
}
|
|
170
|
+
function isObject(value) {
|
|
171
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
172
|
+
}
|
|
173
|
+
async function writeIfChanged(file, content) {
|
|
174
|
+
try {
|
|
175
|
+
if ((await readFile(file, "utf8")) === content)
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
catch { }
|
|
179
|
+
await mkdir(path.dirname(file), { recursive: true });
|
|
180
|
+
const temp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
181
|
+
await writeFile(temp, content, { flag: "wx" });
|
|
182
|
+
await rename(temp, file);
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface CompletionGateCheck {
|
|
2
|
+
status: "available" | "completion_gate_unavailable";
|
|
3
|
+
bundle_sha256: string;
|
|
4
|
+
hook_sha256: string;
|
|
5
|
+
findings: string[];
|
|
6
|
+
}
|
|
7
|
+
export declare function checkLongTaskCompletionGate(repositoryRoot: string, packageRoot: string): Promise<CompletionGateCheck>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { packageOwnedCommand } from "./long-task-hook-install.js";
|
|
4
|
+
import { assertProtectedRepositoryFile } from "./long-task-protected-files.js";
|
|
5
|
+
import { sha256Hex } from "./strict-codec.js";
|
|
6
|
+
export async function checkLongTaskCompletionGate(repositoryRoot, packageRoot) {
|
|
7
|
+
const config = path.join(path.resolve(repositoryRoot), ".codex", "hooks.json");
|
|
8
|
+
const entry = path.join(path.resolve(packageRoot), "dist", "long-task-hook.js");
|
|
9
|
+
const expectedCommand = packageOwnedCommand(entry);
|
|
10
|
+
const findings = [];
|
|
11
|
+
let configHash = "missing";
|
|
12
|
+
let hookHash = "missing";
|
|
13
|
+
try {
|
|
14
|
+
hookHash = sha256Hex(await readFile(await assertProtectedRepositoryFile(packageRoot, entry, "package_owned_hook")));
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
findings.push("package_owned_hook_missing");
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
const content = await readFile(await assertProtectedRepositoryFile(repositoryRoot, config, "completion_hook_config"), "utf8");
|
|
21
|
+
configHash = sha256Hex(content);
|
|
22
|
+
const value = JSON.parse(content);
|
|
23
|
+
for (const event of ["SessionStart", "PostCompact", "Stop"]) {
|
|
24
|
+
const handlers = managedHandlers(value.hooks?.[event], expectedCommand);
|
|
25
|
+
if (handlers.length !== 1)
|
|
26
|
+
findings.push(`completion_hook_event_missing:${event}`);
|
|
27
|
+
else {
|
|
28
|
+
const timeout = handlers[0].timeout;
|
|
29
|
+
const expected = event === "Stop" ? 3600 : 10;
|
|
30
|
+
if (timeout !== expected)
|
|
31
|
+
findings.push(`completion_hook_timeout_invalid:${event}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
findings.push("hooks_config_missing_or_invalid");
|
|
37
|
+
}
|
|
38
|
+
const bundle_sha256 = sha256Hex(`${configHash}:${hookHash}`);
|
|
39
|
+
return {
|
|
40
|
+
status: findings.length ? "completion_gate_unavailable" : "available",
|
|
41
|
+
bundle_sha256,
|
|
42
|
+
hook_sha256: hookHash,
|
|
43
|
+
findings,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function managedHandlers(value, expectedCommand) {
|
|
47
|
+
if (Array.isArray(value))
|
|
48
|
+
return value.flatMap((item) => managedHandlers(item, expectedCommand));
|
|
49
|
+
if (value && typeof value === "object") {
|
|
50
|
+
const item = value;
|
|
51
|
+
if (item.type === "command" &&
|
|
52
|
+
item.command === expectedCommand &&
|
|
53
|
+
item.commandWindows === expectedCommand)
|
|
54
|
+
return [item];
|
|
55
|
+
return Object.values(item).flatMap((child) => managedHandlers(child, expectedCommand));
|
|
56
|
+
}
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export function validateRawExecutionObservationOwnership(checks) {
|
|
2
|
+
const byExecution = new Map();
|
|
3
|
+
for (const check of checks) {
|
|
4
|
+
let observations = byExecution.get(check.raw_execution_identity);
|
|
5
|
+
if (!observations) {
|
|
6
|
+
observations = new Map();
|
|
7
|
+
byExecution.set(check.raw_execution_identity, observations);
|
|
8
|
+
}
|
|
9
|
+
for (const assertion of [
|
|
10
|
+
...check.positive_assertions,
|
|
11
|
+
...check.negative_assertions,
|
|
12
|
+
]) {
|
|
13
|
+
if (!assertion.claims.length)
|
|
14
|
+
continue;
|
|
15
|
+
const owner = `${check.outcome_key ?? "GLOBAL"}.${check.key}.${assertion.key}`;
|
|
16
|
+
const previous = observations.get(assertion.observation);
|
|
17
|
+
if (previous)
|
|
18
|
+
throw new Error(`raw_execution_observation_reused:${check.raw_execution_identity}:${assertion.observation}:${previous}:${owner}`);
|
|
19
|
+
observations.set(assertion.observation, owner);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { DeliveryOutcomeV2 } from "./long-task-delivery-types.js";
|
|
2
|
+
export declare function parseOutcome(value: unknown, label: string): DeliveryOutcomeV2;
|
|
3
|
+
export declare function parseOutcomeFragment(raw: string, relative: string): DeliveryOutcomeV2[];
|
|
4
|
+
export declare function safeFragmentPath(value: string, index: number): string;
|