@1aboveio/skills 0.10.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/LICENSE +3 -0
- package/README.md +90 -0
- package/bin/1aboveio-skills.mjs +18 -0
- package/package.json +28 -0
- package/runtime/skills/distribution/generated/recipes.json +1189 -0
- package/runtime/skills/distribution/scripts/bundles.mjs +280 -0
- package/runtime/skills/engineering/engineering-runtime/scripts/main-module.mjs +80 -0
- package/skills/backend/airflow-dag-develop/LICENSE +3 -0
- package/skills/backend/airflow-dag-develop/SKILL.md +111 -0
- package/skills/backend/app-debug/LICENSE +3 -0
- package/skills/backend/app-debug/SKILL.md +109 -0
- package/skills/backend/app-debug/references/common-errors.md +128 -0
- package/skills/backend/python-backend/LICENSE +3 -0
- package/skills/backend/python-backend/SKILL.md +326 -0
- package/skills/cicd-pipeline/cloud-build/LICENSE +3 -0
- package/skills/cicd-pipeline/cloud-build/SKILL.md +707 -0
- package/skills/cicd-pipeline/cloud-debug/LICENSE +3 -0
- package/skills/cicd-pipeline/cloud-debug/SKILL.md +316 -0
- package/skills/cicd-pipeline/cloud-debug/references/build-failures.md +238 -0
- package/skills/cicd-pipeline/cloud-debug/references/deploy-failures.md +376 -0
- package/skills/cicd-pipeline/cloud-debug/references/pipeline-failures.md +378 -0
- package/skills/cicd-pipeline/cloud-deploy/LICENSE +3 -0
- package/skills/cicd-pipeline/cloud-deploy/SKILL.md +229 -0
- package/skills/cicd-pipeline/cloud-deploy/references/config-templates.md +257 -0
- package/skills/cicd-pipeline/docker/LICENSE +3 -0
- package/skills/cicd-pipeline/docker/SKILL.md +126 -0
- package/skills/cicd-pipeline/google-cloud/LICENSE +3 -0
- package/skills/cicd-pipeline/google-cloud/SKILL.md +118 -0
- package/skills/cicd-pipeline/google-cloud/references/gcs.md +469 -0
- package/skills/cicd-pipeline/google-cloud/references/iam.md +451 -0
- package/skills/cicd-pipeline/google-cloud/references/project.md +349 -0
- package/skills/cicd-pipeline/google-cloud/references/secrets.md +336 -0
- package/skills/cicd-pipeline/google-cloud/references/vpc.md +312 -0
- package/skills/cicd-pipeline/google-cloud/scripts/create-sa.sh +36 -0
- package/skills/cicd-pipeline/google-cloud/scripts/gcp-config.sh +31 -0
- package/skills/cicd-pipeline/google-cloud/scripts/grant-iap.sh +41 -0
- package/skills/cicd-pipeline/google-cloud/scripts/setup-secrets.sh +48 -0
- package/skills/cicd-pipeline/mergify/LICENSE +3 -0
- package/skills/cicd-pipeline/mergify/SKILL.md +138 -0
- package/skills/cicd-pipeline/mergify/assets/templates/mergify.yml +237 -0
- package/skills/cicd-pipeline/mergify/assets/templates/ruleset.json +46 -0
- package/skills/cicd-pipeline/mergify/references/branch-protection.md +277 -0
- package/skills/cicd-pipeline/mergify/references/configuration.md +183 -0
- package/skills/cicd-pipeline/mergify/references/diagnosis.md +73 -0
- package/skills/cicd-pipeline/mergify/references/traps.md +78 -0
- package/skills/cicd-pipeline/mergify/references/watch-contract.md +218 -0
- package/skills/cicd-pipeline/mergify/scripts/audit-core.mjs +131 -0
- package/skills/cicd-pipeline/mergify/scripts/audit.mjs +4 -0
- package/skills/cicd-pipeline/mergify/scripts/watch-pr-delivery-core.mjs +663 -0
- package/skills/cicd-pipeline/mergify/scripts/watch-pr-delivery.mjs +4 -0
- package/skills/cicd-pipeline/podman/LICENSE +3 -0
- package/skills/cicd-pipeline/podman/SKILL.md +70 -0
- package/skills/cicd-pipeline/podman/agents/openai.yaml +4 -0
- package/skills/cicd-pipeline/podman/assets/templates/podman-compose-socket-directory.yml +6 -0
- package/skills/cicd-pipeline/podman/assets/templates/podman-service-override.conf +3 -0
- package/skills/cicd-pipeline/podman/references/compose-compatibility.md +70 -0
- package/skills/cicd-pipeline/podman/references/networking-and-ports.md +74 -0
- package/skills/cicd-pipeline/podman/references/rootless-services-and-sockets.md +156 -0
- package/skills/cicd-pipeline/podman/references/troubleshooting.md +98 -0
- package/skills/engineering/e2e-test/LICENSE +3 -0
- package/skills/engineering/e2e-test/SKILL.md +156 -0
- package/skills/engineering/e2e-test/assets/ci-gates.cloudbuild.yaml +272 -0
- package/skills/engineering/e2e-test/assets/ci-gates.github.yml +451 -0
- package/skills/engineering/e2e-test/assets/e2e-workflow.yml +282 -0
- package/skills/engineering/e2e-test/references/authoring/auth-flows.md +159 -0
- package/skills/engineering/e2e-test/references/authoring/playwright-config.md +71 -0
- package/skills/engineering/e2e-test/references/authoring/playwright-patterns.md +219 -0
- package/skills/engineering/e2e-test/references/authoring/test-skipping.md +44 -0
- package/skills/engineering/e2e-test/references/ci-integration.md +121 -0
- package/skills/engineering/e2e-test/references/ci-playwright-container.md +280 -0
- package/skills/engineering/e2e-test/references/debugging.md +36 -0
- package/skills/engineering/e2e-test/references/presentation-sweep.md +131 -0
- package/skills/engineering/e2e-test/references/reviewing.md +39 -0
- package/skills/engineering/e2e-test/references/route-discovery.md +50 -0
- package/skills/engineering/e2e-test/references/route-manifest.md +44 -0
- package/skills/engineering/e2e-test/scripts/detect-routes-fastapi.py +290 -0
- package/skills/engineering/e2e-test/scripts/detect-routes-nextjs.mjs +200 -0
- package/skills/engineering/e2e-test/scripts/post-visual-evidence.mjs +158 -0
- package/skills/engineering/e2e-test/scripts/presentation-checks.mjs +171 -0
- package/skills/engineering/e2e-test/scripts/presentation-perceivability.mjs +179 -0
- package/skills/engineering/e2e-test/scripts/presentation-reachability.mjs +154 -0
- package/skills/engineering/e2e-test/scripts/presentation-render-health.mjs +141 -0
- package/skills/engineering/e2e-test/scripts/presentation-sweep.mjs +148 -0
- package/skills/engineering/e2e-test/scripts/presentation-temporal.mjs +127 -0
- package/skills/engineering/e2e-test/scripts/presentation-visual.mjs +84 -0
- package/skills/engineering/e2e-test/scripts/project-route-manifest.mjs +75 -0
- package/skills/engineering/e2e-test/scripts/validate-manifest.mjs +106 -0
- package/skills/engineering/engineering-runtime/LICENSE +3 -0
- package/skills/engineering/engineering-runtime/SKILL.md +10 -0
- package/skills/engineering/engineering-runtime/agents/openai.yaml +6 -0
- package/skills/engineering/engineering-runtime/coherence/workflow.json +553 -0
- package/skills/engineering/engineering-runtime/scripts/exact-head-artifact.mjs +131 -0
- package/skills/engineering/engineering-runtime/scripts/head-check-set.mjs +398 -0
- package/skills/engineering/engineering-runtime/scripts/main-module.mjs +80 -0
- package/skills/engineering/engineering-runtime/scripts/mergify-yaml.mjs +11 -0
- package/skills/engineering/engineering-runtime/scripts/package-lock.json +43 -0
- package/skills/engineering/engineering-runtime/scripts/package.json +10 -0
- package/skills/engineering/engineering-runtime/scripts/required-check-plan.mjs +223 -0
- package/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +576 -0
- package/skills/engineering/engineering-runtime/scripts/workflow-policy.mjs +166 -0
- package/skills/engineering/ensure-coverage/LICENSE +3 -0
- package/skills/engineering/ensure-coverage/SKILL.md +136 -0
- package/skills/engineering/ensure-coverage/evals/evals.json +125 -0
- package/skills/engineering/ensure-coverage/references/breadth/coverage-ledger.md +91 -0
- package/skills/engineering/ensure-coverage/references/breadth/inventory-contract.md +83 -0
- package/skills/engineering/ensure-coverage/references/breadth/surface-baseline.md +44 -0
- package/skills/engineering/ensure-coverage/references/breadth/surface-discovery.md +16 -0
- package/skills/engineering/ensure-coverage/references/depth/characterization.md +68 -0
- package/skills/engineering/ensure-coverage/references/depth/coverage.config.example.json +25 -0
- package/skills/engineering/ensure-coverage/references/depth/grading.md +35 -0
- package/skills/engineering/ensure-coverage/references/depth/mock-policy.md +87 -0
- package/skills/engineering/ensure-coverage/references/depth/test-smells.md +23 -0
- package/skills/engineering/ensure-coverage/references/enforcement/ci-contract.md +164 -0
- package/skills/engineering/ensure-coverage/references/enforcement/hooks.md +85 -0
- package/skills/engineering/ensure-coverage/references/examples/coverage-ledger.md +109 -0
- package/skills/engineering/ensure-coverage/references/examples/refund-flow.md +33 -0
- package/skills/engineering/ensure-coverage/references/presentation/axis.md +78 -0
- package/skills/engineering/ensure-coverage/references/presentation/runner-contract.md +74 -0
- package/skills/engineering/ensure-coverage/references/process/audit-mode.md +33 -0
- package/skills/engineering/ensure-coverage/references/process/output-template.md +139 -0
- package/skills/engineering/ensure-coverage/references/process/review-contract-template.md +119 -0
- package/skills/engineering/ensure-coverage/references/process/scope-class.md +178 -0
- package/skills/engineering/ensure-coverage/references/process/test-strategy.md +55 -0
- package/skills/engineering/ensure-coverage/schemas/coverage-config.schema.json +45 -0
- package/skills/engineering/ensure-coverage/schemas/coverage-file.schema.json +93 -0
- package/skills/engineering/ensure-coverage/scripts/adapters/nextjs-inventory.mjs +178 -0
- package/skills/engineering/ensure-coverage/scripts/check-quarantine-expiry.mjs +101 -0
- package/skills/engineering/ensure-coverage/scripts/ci-audit.mjs +358 -0
- package/skills/engineering/ensure-coverage/scripts/coverage-checklist.mjs +494 -0
- package/skills/engineering/ensure-coverage/scripts/coverage-ledger.mjs +663 -0
- package/skills/engineering/ensure-coverage/scripts/design-parity.mjs +591 -0
- package/skills/engineering/ensure-coverage/scripts/evidence-block.mjs +367 -0
- package/skills/engineering/ensure-coverage/scripts/lint-tests.mjs +269 -0
- package/skills/engineering/ensure-coverage/scripts/mock-policy-config.mjs +176 -0
- package/skills/engineering/ensure-coverage/scripts/package-lock.json +76 -0
- package/skills/engineering/ensure-coverage/scripts/package.json +19 -0
- package/skills/engineering/ensure-coverage/scripts/scope-class.mjs +554 -0
- package/skills/engineering/harness-runtime/LICENSE +3 -0
- package/skills/engineering/harness-runtime/SKILL.md +18 -0
- package/skills/engineering/harness-runtime/agents/openai.yaml +6 -0
- package/skills/engineering/harness-runtime/bin/discover-models.mjs +4 -0
- package/skills/engineering/harness-runtime/bin/model-catalog.mjs +4 -0
- package/skills/engineering/harness-runtime/contracts.md +15 -0
- package/skills/engineering/harness-runtime/discover-models.mjs +392 -0
- package/skills/engineering/harness-runtime/fixtures/native-question-schemas.json +33 -0
- package/skills/engineering/harness-runtime/fixtures/question-responses.json +54 -0
- package/skills/engineering/harness-runtime/index.mjs +767 -0
- package/skills/engineering/harness-runtime/model-catalog.mjs +787 -0
- package/skills/engineering/harness-runtime/native-question-contracts.md +37 -0
- package/skills/engineering/harness-runtime/references/model-catalog-seed.json +159 -0
- package/skills/engineering/harness-runtime/references/model-catalog.md +57 -0
- package/skills/engineering/implement-and-pr/LICENSE +3 -0
- package/skills/engineering/implement-and-pr/SKILL.md +176 -0
- package/skills/engineering/implement-and-pr/references/ci-iteration.md +10 -0
- package/skills/engineering/implement-and-pr/references/closeout.md +27 -0
- package/skills/engineering/implement-and-pr/references/contract-complete-fix-rounds.md +34 -0
- package/skills/engineering/implement-and-pr/references/evidence-rules.md +39 -0
- package/skills/engineering/implement-and-pr/references/incremental-plan.md +16 -0
- package/skills/engineering/implement-and-pr/references/self-review.md +23 -0
- package/skills/engineering/implement-and-pr/references/tdd-mode.md +18 -0
- package/skills/engineering/resolve-issues/LICENSE +3 -0
- package/skills/engineering/resolve-issues/SKILL.md +167 -0
- package/skills/engineering/resolve-issues/generated/workflow-repair-policy.json +448 -0
- package/skills/engineering/resolve-issues/references/breaker.md +82 -0
- package/skills/engineering/resolve-issues/references/deliverables.md +27 -0
- package/skills/engineering/resolve-issues/references/delivery.md +108 -0
- package/skills/engineering/resolve-issues/references/evidence-lane.md +21 -0
- package/skills/engineering/resolve-issues/references/exact-head-ci.md +287 -0
- package/skills/engineering/resolve-issues/references/fan-out.md +33 -0
- package/skills/engineering/resolve-issues/references/finalization.md +68 -0
- package/skills/engineering/resolve-issues/references/guarantees.md +10 -0
- package/skills/engineering/resolve-issues/references/high-risk.md +29 -0
- package/skills/engineering/resolve-issues/references/incidents/848/README.md +156 -0
- package/skills/engineering/resolve-issues/references/intake.md +86 -0
- package/skills/engineering/resolve-issues/references/integration-gate.md +53 -0
- package/skills/engineering/resolve-issues/references/interference.md +87 -0
- package/skills/engineering/resolve-issues/references/loop.md +134 -0
- package/skills/engineering/resolve-issues/references/model-catalog.md +9 -0
- package/skills/engineering/resolve-issues/references/postmortem.md +27 -0
- package/skills/engineering/resolve-issues/references/pre-flight-model-slots.md +41 -0
- package/skills/engineering/resolve-issues/references/pre-flight-recording-and-checkout.md +48 -0
- package/skills/engineering/resolve-issues/references/pre-flight.md +41 -0
- package/skills/engineering/resolve-issues/references/regression-checklist.md +26 -0
- package/skills/engineering/resolve-issues/references/run-state.md +288 -0
- package/skills/engineering/resolve-issues/references/sandboxed-testing.md +48 -0
- package/skills/engineering/resolve-issues/references/spawn-contract.md +96 -0
- package/skills/engineering/resolve-issues/references/terminal-evidence-journal.md +40 -0
- package/skills/engineering/resolve-issues/references/why.md +653 -0
- package/skills/engineering/resolve-issues/schemas/fix-round.schema.json +49 -0
- package/skills/engineering/resolve-issues/scripts/combine-and-verify.mjs +721 -0
- package/skills/engineering/resolve-issues/scripts/component-candidate.mjs +962 -0
- package/skills/engineering/resolve-issues/scripts/contract-revision.mjs +220 -0
- package/skills/engineering/resolve-issues/scripts/detect-delivery-mode.mjs +420 -0
- package/skills/engineering/resolve-issues/scripts/detect-target-branch.mjs +256 -0
- package/skills/engineering/resolve-issues/scripts/detect-workspace-mode.mjs +168 -0
- package/skills/engineering/resolve-issues/scripts/discover-models.mjs +9 -0
- package/skills/engineering/resolve-issues/scripts/doctrine.mjs +62 -0
- package/skills/engineering/resolve-issues/scripts/evidence-lifecycle-contract.mjs +191 -0
- package/skills/engineering/resolve-issues/scripts/exact-head-ci.mjs +413 -0
- package/skills/engineering/resolve-issues/scripts/exact-head-github-provider.mjs +332 -0
- package/skills/engineering/resolve-issues/scripts/finalize.mjs +488 -0
- package/skills/engineering/resolve-issues/scripts/fix-rounds.mjs +3307 -0
- package/skills/engineering/resolve-issues/scripts/fixtures/evidence-lifecycle-circular-1001.json +16 -0
- package/skills/engineering/resolve-issues/scripts/fixtures/evidence-lifecycle-valid-sequencing.json +51 -0
- package/skills/engineering/resolve-issues/scripts/fixtures/fmm-express-830-component-candidate.json +17 -0
- package/skills/engineering/resolve-issues/scripts/fixtures/head-check-set-1081.json +166 -0
- package/skills/engineering/resolve-issues/scripts/gate-value-series.mjs +92 -0
- package/skills/engineering/resolve-issues/scripts/guide-index.mjs +73 -0
- package/skills/engineering/resolve-issues/scripts/head-check-set.mjs +159 -0
- package/skills/engineering/resolve-issues/scripts/interference.mjs +427 -0
- package/skills/engineering/resolve-issues/scripts/model-catalog.mjs +9 -0
- package/skills/engineering/resolve-issues/scripts/next-operations.mjs +419 -0
- package/skills/engineering/resolve-issues/scripts/postmortem.mjs +909 -0
- package/skills/engineering/resolve-issues/scripts/preflight-questions.mjs +322 -0
- package/skills/engineering/resolve-issues/scripts/reconcile-contained-unit-prs.mjs +415 -0
- package/skills/engineering/resolve-issues/scripts/release-state-contract.mjs +697 -0
- package/skills/engineering/resolve-issues/scripts/report.mjs +494 -0
- package/skills/engineering/resolve-issues/scripts/required-check-plan.mjs +172 -0
- package/skills/engineering/resolve-issues/scripts/round-metadata.mjs +79 -0
- package/skills/engineering/resolve-issues/scripts/run-state-review6-cases.mjs +334 -0
- package/skills/engineering/resolve-issues/scripts/run-state.mjs +4784 -0
- package/skills/engineering/resolve-issues/scripts/sandbox-selftest.mjs +395 -0
- package/skills/engineering/resolve-issues/scripts/spawn-contract.mjs +290 -0
- package/skills/engineering/resolve-issues/scripts/terminal-dispositions.mjs +170 -0
- package/skills/engineering/resolve-issues/scripts/terminal-evidence-journal.mjs +293 -0
- package/skills/engineering/resolve-issues/scripts/unit-kind.mjs +197 -0
- package/skills/engineering/resolve-issues/scripts/unit-lifecycle.mjs +127 -0
- package/skills/engineering/resolve-issues/scripts/watch-delivery.mjs +893 -0
- package/skills/engineering/resolve-issues/scripts/workspaces.mjs +829 -0
- package/skills/engineering/resolve-issues/workflows/independent-review.workflow.js +290 -0
- package/skills/engineering/resolve-issues/workflows/prior-art-scan.workflow.js +80 -0
- package/skills/engineering/resolve-issues/workflows/workflow-smoke.mjs +102 -0
- package/skills/engineering/resolve-release/LICENSE +3 -0
- package/skills/engineering/resolve-release/SKILL.md +112 -0
- package/skills/engineering/resolve-release/references/assembly.md +137 -0
- package/skills/engineering/resolve-release/references/auto-when-green.md +56 -0
- package/skills/engineering/resolve-release/references/candidate.md +167 -0
- package/skills/engineering/resolve-release/references/exposure.md +178 -0
- package/skills/engineering/resolve-release/references/handoff.md +24 -0
- package/skills/engineering/resolve-release/references/postmortem.md +230 -0
- package/skills/engineering/resolve-release/references/preflight.md +207 -0
- package/skills/engineering/resolve-release/references/principles.md +94 -0
- package/skills/engineering/resolve-release/references/regression-checklist.md +36 -0
- package/skills/engineering/resolve-release/references/related-skills.md +12 -0
- package/skills/engineering/resolve-release/references/routing.md +149 -0
- package/skills/engineering/resolve-release/references/verified-sha-github-flow.md +285 -0
- package/skills/engineering/resolve-release/references/versioning.md +202 -0
- package/skills/engineering/resolve-release/references/why.md +53 -0
- package/skills/engineering/resolve-release/scripts/adapter-completion-artifact.mjs +389 -0
- package/skills/engineering/resolve-release/scripts/build-changes.mjs +209 -0
- package/skills/engineering/resolve-release/scripts/candidate-hygiene.mjs +407 -0
- package/skills/engineering/resolve-release/scripts/candidate-identity.mjs +904 -0
- package/skills/engineering/resolve-release/scripts/candidate-traffic.mjs +81 -0
- package/skills/engineering/resolve-release/scripts/checked-adapter-loader.mjs +612 -0
- package/skills/engineering/resolve-release/scripts/close-attempt.mjs +135 -0
- package/skills/engineering/resolve-release/scripts/closeout-release.mjs +161 -0
- package/skills/engineering/resolve-release/scripts/doctrine.mjs +106 -0
- package/skills/engineering/resolve-release/scripts/durable-processing.mjs +522 -0
- package/skills/engineering/resolve-release/scripts/ensure-target-green.mjs +659 -0
- package/skills/engineering/resolve-release/scripts/evidence-bundle.mjs +1014 -0
- package/skills/engineering/resolve-release/scripts/finalize-release.mjs +526 -0
- package/skills/engineering/resolve-release/scripts/fixtures/durable-processing-adapter.mjs +169 -0
- package/skills/engineering/resolve-release/scripts/green-gate.mjs +599 -0
- package/skills/engineering/resolve-release/scripts/isolated-adapter-evaluator.mjs +752 -0
- package/skills/engineering/resolve-release/scripts/metadata-pr-status.mjs +56 -0
- package/skills/engineering/resolve-release/scripts/metadata-sync.mjs +1538 -0
- package/skills/engineering/resolve-release/scripts/postmortem.mjs +381 -0
- package/skills/engineering/resolve-release/scripts/preflight-probes.mjs +498 -0
- package/skills/engineering/resolve-release/scripts/production-endpoints.mjs +326 -0
- package/skills/engineering/resolve-release/scripts/rc-circuit-breaker.mjs +272 -0
- package/skills/engineering/resolve-release/scripts/report.mjs +417 -0
- package/skills/engineering/resolve-release/scripts/reprobe-credentials.mjs +114 -0
- package/skills/engineering/resolve-release/scripts/revalidate-candidate.mjs +238 -0
- package/skills/engineering/resolve-release/scripts/review-packet.mjs +503 -0
- package/skills/engineering/resolve-release/scripts/rollback-floor.mjs +263 -0
- package/skills/engineering/resolve-release/scripts/version-assert.mjs +339 -0
- package/skills/engineering/resolve-release/scripts/version-postmortem.mjs +485 -0
- package/skills/engineering/resolve-release/scripts/version.mjs +1199 -0
- package/skills/engineering/resolve-release/scripts/watch-candidate-delivery.mjs +449 -0
- package/skills/engineering/resolve-release/vendor/ACORN-LICENSE +21 -0
- package/skills/engineering/resolve-release/vendor/README.md +60 -0
- package/skills/engineering/resolve-release/vendor/acorn.mjs +6233 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/LICENSE +21 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/README.md +341 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/README.template.md +70 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-TAV5CUKK.mjs +2 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-TAV5CUKK.mjs.map +1 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-V2S4ZYJR.mjs +7 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-V2S4ZYJR.mjs.map +1 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.d.mts +2033 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.d.ts +2033 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.js +7 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.js.map +1 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.mjs +2 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.mjs.map +1 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-ES6BEMUI.mjs +2 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-ES6BEMUI.mjs.map +1 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-asyncify-2EFITU5U.mjs +2 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-asyncify-2EFITU5U.mjs.map +1 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/package.json +49 -0
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/LICENSE +21 -0
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/README.md +5 -0
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.d.mts +549 -0
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.d.ts +549 -0
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.js +2 -0
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.js.map +1 -0
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.mjs +2 -0
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.mjs.map +1 -0
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/package.json +36 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/LICENSE +47 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/README.md +82 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.browser.d.ts +11 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.browser.mjs +22 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.cjs +21 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.cloudflare.cjs +21 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.cloudflare.d.ts +11 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.d.ts +11 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.mjs +25 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.wasm +0 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.d.mts +85 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.d.ts +85 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.js +2 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.js.map +1 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.mjs +2 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.mjs.map +1 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.d.mts +20 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.d.ts +20 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.js +2 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.js.map +1 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.mjs +2 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.mjs.map +1 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/package.json +61 -0
- package/skills/engineering/review-pr/LICENSE +3 -0
- package/skills/engineering/review-pr/SKILL.md +123 -0
- package/skills/engineering/review-pr/references/adversarial-reviewer-prompt.md +42 -0
- package/skills/engineering/review-pr/references/code-correctness.md +5 -0
- package/skills/engineering/review-pr/references/contract-freshness.md +9 -0
- package/skills/engineering/review-pr/references/coordination.md +18 -0
- package/skills/engineering/review-pr/references/domain-hazards.md +123 -0
- package/skills/engineering/review-pr/references/finding-themes.md +7 -0
- package/skills/engineering/review-pr/references/github-posting.md +98 -0
- package/skills/engineering/review-pr/references/golden-path-smoke.md +5 -0
- package/skills/engineering/review-pr/references/incremental-output.md +16 -0
- package/skills/engineering/review-pr/references/inputs-and-discovery.md +31 -0
- package/skills/engineering/review-pr/references/output-format.md +99 -0
- package/skills/engineering/review-pr/references/over-mock-screen.md +7 -0
- package/skills/engineering/review-pr/references/promotion-prs.md +16 -0
- package/skills/engineering/review-pr/references/re-review.md +18 -0
- package/skills/engineering/review-pr/references/review-method.md +199 -0
- package/skills/engineering/review-pr/references/review-mode.md +30 -0
- package/skills/engineering/review-pr/references/review-posture.md +53 -0
- package/skills/engineering/review-pr/references/round1-depth.md +62 -0
- package/skills/engineering/review-pr/references/scripts.md +17 -0
- package/skills/engineering/review-pr/references/workflow.md +16 -0
- package/skills/engineering/review-pr/schemas/findings.schema.json +282 -0
- package/skills/engineering/review-pr/scripts/finding-contract.mjs +285 -0
- package/skills/engineering/review-pr/scripts/post-review.mjs +405 -0
- package/skills/engineering/review-pr/scripts/pr-context.mjs +207 -0
- package/skills/engineering/review-pr/scripts/scan-diff.mjs +365 -0
- package/skills/engineering/review-pr/scripts/theme-contract.mjs +57 -0
- package/skills/engineering/smoke/LICENSE +3 -0
- package/skills/engineering/smoke/SKILL.md +131 -0
- package/skills/engineering/smoke/assets/smoke.manifest.example.json +53 -0
- package/skills/engineering/smoke/references/manifest.md +192 -0
- package/skills/engineering/smoke/scripts/smoke.mjs +713 -0
- package/skills/fullstack/better-auth/LICENSE +3 -0
- package/skills/fullstack/better-auth/SKILL.md +601 -0
- package/skills/fullstack/better-auth/references/feishu-api.md +270 -0
- package/skills/fullstack/monorepo/LICENSE +3 -0
- package/skills/fullstack/monorepo/SKILL.md +465 -0
- package/skills/fullstack/nextjs-fullstack/LICENSE +3 -0
- package/skills/fullstack/nextjs-fullstack/SKILL.md +210 -0
- package/skills/fullstack/nextjs-fullstack/conventions.md +318 -0
- package/skills/fullstack/nextjs-fullstack/frontend-conventions.md +61 -0
- package/skills/fullstack/nextjs-fullstack/nextjs16.md +287 -0
- package/skills/fullstack/nextjs-fullstack/server-actions.md +409 -0
- package/skills/fullstack/prisma-setup/LICENSE +3 -0
- package/skills/fullstack/prisma-setup/SKILL.md +180 -0
- package/skills/fullstack/prisma-setup/nextjs.md +258 -0
- package/skills/fullstack/prisma-setup/turborepo.md +301 -0
- package/skills/fullstack/shadcn/LICENSE +3 -0
- package/skills/fullstack/shadcn/SKILL.md +119 -0
- package/skills/fullstack/shadcn/assets/shadcn-small.png +0 -0
- package/skills/fullstack/shadcn/assets/shadcn.png +0 -0
- package/skills/fullstack/shadcn/cli.md +411 -0
- package/skills/fullstack/shadcn/customization.md +224 -0
- package/skills/fullstack/shadcn/evals/evals.json +90 -0
- package/skills/fullstack/shadcn/mcp.md +101 -0
- package/skills/fullstack/shadcn/rules/base-vs-radix.md +323 -0
- package/skills/fullstack/shadcn/rules/component-selection.md +67 -0
- package/skills/fullstack/shadcn/rules/composition.md +195 -0
- package/skills/fullstack/shadcn/rules/data-table.md +201 -0
- package/skills/fullstack/shadcn/rules/forms.md +255 -0
- package/skills/fullstack/shadcn/rules/icons.md +103 -0
- package/skills/fullstack/shadcn/rules/styling.md +167 -0
- package/skills/fullstack/zod-v4/LICENSE +3 -0
- package/skills/fullstack/zod-v4/SKILL.md +287 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { createHash } from 'node:crypto'
|
|
4
|
+
import fs from 'node:fs'
|
|
5
|
+
import os from 'node:os'
|
|
6
|
+
import path from 'node:path'
|
|
7
|
+
import { isMainModule } from '../../engineering-runtime/scripts/main-module.mjs'
|
|
8
|
+
import { deliveryForUnit } from './terminal-dispositions.mjs'
|
|
9
|
+
|
|
10
|
+
export const TERMINAL_EVIDENCE_SCHEMA_VERSION = 1
|
|
11
|
+
export const TERMINAL_EVIDENCE_KIND = 'resolver-terminal-provider-evidence'
|
|
12
|
+
export const TERMINAL_EVIDENCE_DIGEST_ALGORITHM = 'sha256-canonical-json-v1'
|
|
13
|
+
const SHA = /^[0-9a-f]{40}$/i
|
|
14
|
+
const CHECK_CONCLUSIONS = new Set([
|
|
15
|
+
'success', 'failure', 'cancelled', 'skipped', 'neutral', 'timed_out',
|
|
16
|
+
'action_required', 'stale', 'startup_failure',
|
|
17
|
+
])
|
|
18
|
+
|
|
19
|
+
const object = (value) => value != null && typeof value === 'object' && !Array.isArray(value)
|
|
20
|
+
const text = (value) => typeof value === 'string' && value.trim().length > 0
|
|
21
|
+
const same = (left, right) => JSON.stringify(left) === JSON.stringify(right)
|
|
22
|
+
|
|
23
|
+
function exact(value, fields, label, errors) {
|
|
24
|
+
if (!object(value)) {
|
|
25
|
+
errors.push(`${label} must be an object`)
|
|
26
|
+
return false
|
|
27
|
+
}
|
|
28
|
+
const unknown = Object.keys(value).filter((field) => !fields.includes(field))
|
|
29
|
+
if (unknown.length) errors.push(`${label} has unknown field ${unknown[0]}`)
|
|
30
|
+
return true
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function iso(value, label, errors) {
|
|
34
|
+
if (!text(value) || !Number.isFinite(Date.parse(value))) errors.push(`${label} must be an ISO timestamp`)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function fullSha(value, label, errors) {
|
|
38
|
+
if (!text(value) || !SHA.test(value)) errors.push(`${label} must be a full 40-character commit SHA`)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function positivePr(value) {
|
|
42
|
+
if (Number.isInteger(value) && value > 0) return value
|
|
43
|
+
const match = String(value ?? '').match(/^#?(\d+)$/)
|
|
44
|
+
return match && Number(match[1]) > 0 ? Number(match[1]) : null
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function canonicalEntryBytes(entry) {
|
|
48
|
+
return `${JSON.stringify(entry)}\n`
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function terminalEvidenceDigest(entry) {
|
|
52
|
+
return createHash('sha256').update(canonicalEntryBytes(entry)).digest('hex')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function validateTerminalEvidenceEntry(entry) {
|
|
56
|
+
const errors = []
|
|
57
|
+
if (!exact(entry, ['schemaVersion', 'kind', 'run', 'subject', 'provider', 'observations'], 'entry', errors)) return { ok: false, errors }
|
|
58
|
+
if (entry.schemaVersion !== TERMINAL_EVIDENCE_SCHEMA_VERSION) errors.push(`entry.schemaVersion must be ${TERMINAL_EVIDENCE_SCHEMA_VERSION}`)
|
|
59
|
+
if (entry.kind !== TERMINAL_EVIDENCE_KIND) errors.push(`entry.kind must be ${TERMINAL_EVIDENCE_KIND}`)
|
|
60
|
+
|
|
61
|
+
if (exact(entry.run, ['slug', 'repository', 'targetBranch'], 'entry.run', errors)) {
|
|
62
|
+
if (!text(entry.run.slug)) errors.push('entry.run.slug is required')
|
|
63
|
+
if (!text(entry.run.repository) || !/^[^/\s]+\/[^/\s]+$/.test(entry.run.repository)) errors.push('entry.run.repository must be owner/name')
|
|
64
|
+
if (!text(entry.run.targetBranch)) errors.push('entry.run.targetBranch is required')
|
|
65
|
+
}
|
|
66
|
+
if (exact(entry.subject, ['unitId', 'componentId', 'pr', 'headSha'], 'entry.subject', errors)) {
|
|
67
|
+
if (!text(entry.subject.unitId)) errors.push('entry.subject.unitId is required')
|
|
68
|
+
if (!text(entry.subject.componentId)) errors.push('entry.subject.componentId is required')
|
|
69
|
+
if (positivePr(entry.subject.pr) !== entry.subject.pr) errors.push('entry.subject.pr must be a positive integer')
|
|
70
|
+
fullSha(entry.subject.headSha, 'entry.subject.headSha', errors)
|
|
71
|
+
}
|
|
72
|
+
if (exact(entry.provider, ['id', 'observedAt'], 'entry.provider', errors)) {
|
|
73
|
+
if (!text(entry.provider.id)) errors.push('entry.provider.id is required')
|
|
74
|
+
iso(entry.provider.observedAt, 'entry.provider.observedAt', errors)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const observations = entry.observations
|
|
78
|
+
if (exact(observations, ['pullRequest', 'checks', 'targetAncestry', 'queueExecutionArtifact'], 'entry.observations', errors)) {
|
|
79
|
+
const pr = observations.pullRequest
|
|
80
|
+
if (exact(pr, ['state', 'headSha', 'mergeCommitSha', 'mergedAt'], 'entry.observations.pullRequest', errors)) {
|
|
81
|
+
if (pr.state !== 'MERGED') errors.push('entry.observations.pullRequest.state must be MERGED')
|
|
82
|
+
fullSha(pr.headSha, 'entry.observations.pullRequest.headSha', errors)
|
|
83
|
+
fullSha(pr.mergeCommitSha, 'entry.observations.pullRequest.mergeCommitSha', errors)
|
|
84
|
+
iso(pr.mergedAt, 'entry.observations.pullRequest.mergedAt', errors)
|
|
85
|
+
if (entry.subject?.headSha && pr.headSha !== entry.subject.headSha) errors.push('pull request head contradicts the bound exact head')
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!Array.isArray(observations.checks) || observations.checks.length === 0) {
|
|
89
|
+
errors.push('entry.observations.checks must be a non-empty array')
|
|
90
|
+
} else {
|
|
91
|
+
const names = new Set()
|
|
92
|
+
for (const [index, check] of observations.checks.entries()) {
|
|
93
|
+
const label = `entry.observations.checks[${index}]`
|
|
94
|
+
if (!exact(check, ['name', 'headSha', 'conclusion', 'completedAt'], label, errors)) continue
|
|
95
|
+
if (!text(check.name)) errors.push(`${label}.name is required`)
|
|
96
|
+
else if (names.has(check.name)) errors.push(`entry.observations.checks has duplicate check ${check.name}`)
|
|
97
|
+
else names.add(check.name)
|
|
98
|
+
fullSha(check.headSha, `${label}.headSha`, errors)
|
|
99
|
+
if (entry.subject?.headSha && check.headSha !== entry.subject.headSha) errors.push(`${label}.headSha contradicts the bound exact head`)
|
|
100
|
+
if (!CHECK_CONCLUSIONS.has(check.conclusion)) errors.push(`${label}.conclusion is unsupported`)
|
|
101
|
+
if (check.completedAt !== undefined && check.completedAt !== null) iso(check.completedAt, `${label}.completedAt`, errors)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const ancestry = observations.targetAncestry
|
|
106
|
+
if (exact(ancestry, ['targetRef', 'targetHeadSha', 'mergeCommitSha', 'isAncestor', 'observedAt'], 'entry.observations.targetAncestry', errors)) {
|
|
107
|
+
if (!text(ancestry.targetRef)) errors.push('entry.observations.targetAncestry.targetRef is required')
|
|
108
|
+
fullSha(ancestry.targetHeadSha, 'entry.observations.targetAncestry.targetHeadSha', errors)
|
|
109
|
+
fullSha(ancestry.mergeCommitSha, 'entry.observations.targetAncestry.mergeCommitSha', errors)
|
|
110
|
+
if (ancestry.isAncestor !== true) errors.push('entry.observations.targetAncestry.isAncestor must be true')
|
|
111
|
+
iso(ancestry.observedAt, 'entry.observations.targetAncestry.observedAt', errors)
|
|
112
|
+
if (pr?.mergeCommitSha && ancestry.mergeCommitSha !== pr.mergeCommitSha) errors.push('target ancestry merge commit contradicts the pull request merge commit')
|
|
113
|
+
}
|
|
114
|
+
if (observations.queueExecutionArtifact !== undefined && !object(observations.queueExecutionArtifact)) {
|
|
115
|
+
errors.push('entry.observations.queueExecutionArtifact must be an object when present')
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return { ok: errors.length === 0, errors }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function validateTerminalEvidenceEnvelope(envelope) {
|
|
122
|
+
const errors = []
|
|
123
|
+
if (!exact(envelope, ['schemaVersion', 'kind', 'digestAlgorithm', 'digest', 'entry'], 'envelope', errors)) return { ok: false, errors }
|
|
124
|
+
if (envelope.schemaVersion !== TERMINAL_EVIDENCE_SCHEMA_VERSION) errors.push(`envelope.schemaVersion must be ${TERMINAL_EVIDENCE_SCHEMA_VERSION}`)
|
|
125
|
+
if (envelope.kind !== 'resolver-terminal-evidence-journal-entry') errors.push('envelope.kind must be resolver-terminal-evidence-journal-entry')
|
|
126
|
+
if (envelope.digestAlgorithm !== TERMINAL_EVIDENCE_DIGEST_ALGORITHM) errors.push(`envelope.digestAlgorithm must be ${TERMINAL_EVIDENCE_DIGEST_ALGORITHM}`)
|
|
127
|
+
const entryVerdict = validateTerminalEvidenceEntry(envelope.entry)
|
|
128
|
+
errors.push(...entryVerdict.errors)
|
|
129
|
+
const expected = object(envelope.entry) ? terminalEvidenceDigest(envelope.entry) : null
|
|
130
|
+
if (!/^[0-9a-f]{64}$/.test(envelope.digest ?? '') || envelope.digest !== expected) {
|
|
131
|
+
errors.push('envelope digest does not match the canonical entry content address')
|
|
132
|
+
}
|
|
133
|
+
return { ok: errors.length === 0, errors }
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function resolveTerminalEvidenceBinding(manifest, subject) {
|
|
137
|
+
if (!object(manifest) || manifest.skill !== 'resolve-issues') throw new Error('terminal evidence applies only to a resolve-issues run manifest')
|
|
138
|
+
if (!object(subject)) throw new Error('terminal evidence subject must be an object')
|
|
139
|
+
const unit = (Array.isArray(manifest.units) ? manifest.units : [])
|
|
140
|
+
.find((candidate) => candidate?.id === subject.unitId)
|
|
141
|
+
if (!unit) throw new Error(`terminal evidence unit ${subject.unitId ?? '(missing)'} is not in this run`)
|
|
142
|
+
const component = (Array.isArray(manifest.components) ? manifest.components : [])
|
|
143
|
+
.find((candidate) => candidate?.id === subject.componentId)
|
|
144
|
+
if (!component || !Array.isArray(component.units) || !component.units.includes(unit.id)) {
|
|
145
|
+
throw new Error(`terminal evidence component ${subject.componentId ?? '(missing)'} does not own unit ${unit.id}`)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
let delivery = deliveryForUnit(unit, manifest)
|
|
149
|
+
let pr = positivePr(unit.pr)
|
|
150
|
+
let headSha = unit.pushedSha
|
|
151
|
+
let canonicalComponentDelivery = object(component.delivery) && delivery === component.delivery
|
|
152
|
+
if (unit.deliveryRef !== undefined) {
|
|
153
|
+
const ref = unit.deliveryRef
|
|
154
|
+
if (!object(ref) || ref.componentId !== component.id) {
|
|
155
|
+
throw new Error(`terminal evidence unit ${unit.id} is not bound to component delivery ${component.id}`)
|
|
156
|
+
}
|
|
157
|
+
delivery = object(component.delivery) && component.delivery.generation === ref.generation
|
|
158
|
+
? component.delivery
|
|
159
|
+
: null
|
|
160
|
+
if (!delivery) throw new Error(`terminal evidence unit ${unit.id} has a stale canonical component delivery binding`)
|
|
161
|
+
pr = positivePr(delivery.pr)
|
|
162
|
+
headSha = delivery.authorizedHeadSha
|
|
163
|
+
canonicalComponentDelivery = true
|
|
164
|
+
}
|
|
165
|
+
if (pr == null) throw new Error(`terminal evidence delivery for unit ${unit.id} has no positive PR identity`)
|
|
166
|
+
if (!text(headSha) || !SHA.test(headSha)) throw new Error(`terminal evidence delivery for unit ${unit.id} has no full exact head SHA`)
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
unit,
|
|
170
|
+
component,
|
|
171
|
+
delivery,
|
|
172
|
+
canonicalComponentDelivery,
|
|
173
|
+
subject: { unitId: unit.id, componentId: component.id, pr, headSha },
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function boundEntry(manifest, observation) {
|
|
178
|
+
if (!object(manifest) || manifest.skill !== 'resolve-issues') throw new Error('terminal evidence applies only to a resolve-issues run manifest')
|
|
179
|
+
if (!text(manifest.runSlug) || !text(manifest.repository) || !text(manifest.targetBranch)) {
|
|
180
|
+
throw new Error('terminal evidence requires run slug, repository, and target branch bindings')
|
|
181
|
+
}
|
|
182
|
+
if (!object(observation)) throw new Error('terminal evidence observation must be an object')
|
|
183
|
+
const binding = resolveTerminalEvidenceBinding(manifest, observation)
|
|
184
|
+
const expectedTargetRef = `origin/${manifest.targetBranch}`
|
|
185
|
+
if (observation.pullRequest?.headSha !== binding.subject.headSha) {
|
|
186
|
+
throw new Error(`terminal evidence pull request head does not match delivery for unit ${binding.unit.id} exact head`)
|
|
187
|
+
}
|
|
188
|
+
if (observation.targetAncestry?.targetRef !== expectedTargetRef) throw new Error(`terminal evidence target ancestry must bind ${expectedTargetRef}`)
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
schemaVersion: TERMINAL_EVIDENCE_SCHEMA_VERSION,
|
|
192
|
+
kind: TERMINAL_EVIDENCE_KIND,
|
|
193
|
+
run: { slug: manifest.runSlug, repository: manifest.repository, targetBranch: manifest.targetBranch },
|
|
194
|
+
subject: binding.subject,
|
|
195
|
+
provider: structuredClone(observation.provider),
|
|
196
|
+
observations: {
|
|
197
|
+
pullRequest: structuredClone(observation.pullRequest),
|
|
198
|
+
checks: structuredClone(observation.checks),
|
|
199
|
+
targetAncestry: structuredClone(observation.targetAncestry),
|
|
200
|
+
...(observation.queueExecutionArtifact !== undefined
|
|
201
|
+
? { queueExecutionArtifact: structuredClone(observation.queueExecutionArtifact) }
|
|
202
|
+
: {}),
|
|
203
|
+
},
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function defaultTerminalEvidenceDirectory(env = process.env) {
|
|
208
|
+
if (text(env.RESOLVER_TERMINAL_EVIDENCE_DIR)) return path.resolve(env.RESOLVER_TERMINAL_EVIDENCE_DIR)
|
|
209
|
+
const stateHome = text(env.XDG_STATE_HOME) ? env.XDG_STATE_HOME : path.join(env.HOME || os.homedir(), '.local', 'state')
|
|
210
|
+
return path.resolve(stateHome, 'resolver-terminal-evidence')
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function appendTerminalEvidence(manifest, observation, { directory = defaultTerminalEvidenceDirectory() } = {}) {
|
|
214
|
+
const entry = boundEntry(manifest, observation)
|
|
215
|
+
const verdict = validateTerminalEvidenceEntry(entry)
|
|
216
|
+
if (!verdict.ok) throw new Error(`terminal evidence is invalid: ${verdict.errors[0]}`)
|
|
217
|
+
const digest = terminalEvidenceDigest(entry)
|
|
218
|
+
const envelope = {
|
|
219
|
+
schemaVersion: TERMINAL_EVIDENCE_SCHEMA_VERSION,
|
|
220
|
+
kind: 'resolver-terminal-evidence-journal-entry',
|
|
221
|
+
digestAlgorithm: TERMINAL_EVIDENCE_DIGEST_ALGORITHM,
|
|
222
|
+
digest,
|
|
223
|
+
entry,
|
|
224
|
+
}
|
|
225
|
+
const bytes = `${JSON.stringify(envelope, null, 2)}\n`
|
|
226
|
+
fs.mkdirSync(directory, { recursive: true, mode: 0o700 })
|
|
227
|
+
const destination = path.join(directory, `${digest}.json`)
|
|
228
|
+
let descriptor = null
|
|
229
|
+
try {
|
|
230
|
+
descriptor = fs.openSync(destination, 'wx', 0o444)
|
|
231
|
+
fs.writeFileSync(descriptor, bytes, 'utf8')
|
|
232
|
+
fs.fsyncSync(descriptor)
|
|
233
|
+
} catch (error) {
|
|
234
|
+
if (error.code !== 'EEXIST') throw error
|
|
235
|
+
const existing = fs.readFileSync(destination, 'utf8')
|
|
236
|
+
if (existing !== bytes) throw new Error(`terminal evidence content-address collision or tampering at ${destination}`)
|
|
237
|
+
} finally {
|
|
238
|
+
if (descriptor != null) fs.closeSync(descriptor)
|
|
239
|
+
}
|
|
240
|
+
fs.chmodSync(destination, 0o444)
|
|
241
|
+
return { digest, path: destination, entry }
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export function readTerminalEvidence(file) {
|
|
245
|
+
const envelope = JSON.parse(fs.readFileSync(file, 'utf8'))
|
|
246
|
+
const verdict = validateTerminalEvidenceEnvelope(envelope)
|
|
247
|
+
if (!verdict.ok) throw new Error(`terminal evidence journal entry is invalid: ${verdict.errors[0]}`)
|
|
248
|
+
const basename = path.basename(file)
|
|
249
|
+
if (basename !== `${envelope.digest}.json`) throw new Error('terminal evidence filename does not match its content address')
|
|
250
|
+
return envelope
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function parseArgs(argv) {
|
|
254
|
+
const command = argv[0]
|
|
255
|
+
const options = {}
|
|
256
|
+
const positional = []
|
|
257
|
+
for (let index = 1; index < argv.length; index += 1) {
|
|
258
|
+
const argument = argv[index]
|
|
259
|
+
if (['--manifest', '--input', '--dir'].includes(argument)) options[argument.slice(2)] = argv[++index]
|
|
260
|
+
else positional.push(argument)
|
|
261
|
+
}
|
|
262
|
+
return { command, options, positional }
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export function runTerminalEvidenceJournalCli(argv) {
|
|
266
|
+
const { command, options, positional } = parseArgs(argv)
|
|
267
|
+
try {
|
|
268
|
+
if (['--help', '-h', 'help'].includes(command)) {
|
|
269
|
+
process.stdout.write('usage: terminal-evidence-journal.mjs append --manifest <run.json> --input <observation.json> [--dir <journal-dir>] | validate <entry.json>\n')
|
|
270
|
+
return 0
|
|
271
|
+
}
|
|
272
|
+
if (command === 'append') {
|
|
273
|
+
if (!options.manifest || !options.input) throw new Error('append requires --manifest <run.json> --input <observation.json> [--dir <journal-dir>]')
|
|
274
|
+
const manifest = JSON.parse(fs.readFileSync(options.manifest, 'utf8'))
|
|
275
|
+
const observation = JSON.parse(fs.readFileSync(options.input, 'utf8'))
|
|
276
|
+
const result = appendTerminalEvidence(manifest, observation, options.dir ? { directory: path.resolve(options.dir) } : {})
|
|
277
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`)
|
|
278
|
+
return 0
|
|
279
|
+
}
|
|
280
|
+
if (command === 'validate') {
|
|
281
|
+
if (positional.length !== 1) throw new Error('validate requires <content-addressed-entry.json>')
|
|
282
|
+
const envelope = readTerminalEvidence(positional[0])
|
|
283
|
+
process.stdout.write(`terminal-evidence-journal: valid ${envelope.digest}\n`)
|
|
284
|
+
return 0
|
|
285
|
+
}
|
|
286
|
+
throw new Error('usage: terminal-evidence-journal.mjs append --manifest <run.json> --input <observation.json> [--dir <journal-dir>] | validate <entry.json>')
|
|
287
|
+
} catch (error) {
|
|
288
|
+
process.stderr.write(`terminal-evidence-journal: ${error.message}\n`)
|
|
289
|
+
return 2
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (isMainModule(import.meta.url)) process.exitCode = runTerminalEvidenceJournalCli(process.argv.slice(2))
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs, { realpathSync } from 'node:fs'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
|
+
|
|
6
|
+
export const UNIT_KINDS = ['implementation', 'external-verification', 'human-decision', 'prerequisite']
|
|
7
|
+
|
|
8
|
+
const nonEmpty = (value) => typeof value === 'string' && value.trim().length > 0
|
|
9
|
+
|
|
10
|
+
export const EVIDENCE_LIFECYCLE_CLASSES = ['ordinary', 'evidence-authorization']
|
|
11
|
+
|
|
12
|
+
const EVIDENCE_SEMANTICS = /\b(authorization|authorisation|required[- ]checks?|admission|delivery evidence|enqueue|queue[- ]head|synthetic queue|mergeable|target[- ]branch ancestry)\b/i
|
|
13
|
+
const EVIDENCE_CONTROL_SURFACES = /(?:^|\/)resolve-issues\/scripts\/(?:unit-kind|spawn-contract|evidence-lifecycle-contract|required-check-plan|combine-and-verify|component-candidate|exact-head(?:-ci|-github-provider)?|watch-delivery|finalize)(?:\.|\/|$)/i
|
|
14
|
+
const EVIDENCE_DOCTRINE_SURFACES = /(?:^|\/)resolve-issues\/(?:SKILL\.md|references\/(?:breaker|deliverables|delivery|evidence-lane|exact-head-ci|fan-out|finalization|high-risk|intake|integration-gate|interference|loop|pre-flight(?:-[^/]+)?|regression-checklist|run-state|spawn-contract|why)\.md)$/i
|
|
15
|
+
|
|
16
|
+
const isEvidenceLifecycleSurface = (path) => (
|
|
17
|
+
EVIDENCE_CONTROL_SURFACES.test(path) || EVIDENCE_DOCTRINE_SURFACES.test(path)
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
function classifyEvidenceLifecycle(input, text) {
|
|
21
|
+
const signals = []
|
|
22
|
+
const declaration = input.declaredScope
|
|
23
|
+
if (declaration !== undefined) {
|
|
24
|
+
if (declaration == null || typeof declaration !== 'object' || Array.isArray(declaration)
|
|
25
|
+
|| !EVIDENCE_LIFECYCLE_CLASSES.includes(declaration.classification)) {
|
|
26
|
+
signals.push({ type: 'unknown-declared-scope', source: 'declaredScope' })
|
|
27
|
+
} else if (declaration.classification === 'evidence-authorization') {
|
|
28
|
+
signals.push({ type: 'declared-lifecycle-scope', source: 'declaredScope' })
|
|
29
|
+
}
|
|
30
|
+
if (nonEmpty(declaration?.summary) && EVIDENCE_SEMANTICS.test(declaration.summary)) {
|
|
31
|
+
signals.push({ type: 'declared-lifecycle-semantics', source: 'declaredScope.summary' })
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (input.changedSurfaces !== undefined && !Array.isArray(input.changedSurfaces)) {
|
|
36
|
+
signals.push({ type: 'unknown-changed-surface', source: 'changedSurfaces' })
|
|
37
|
+
} else {
|
|
38
|
+
for (const [index, surface] of (input.changedSurfaces || []).entries()) {
|
|
39
|
+
if (surface == null || typeof surface !== 'object' || Array.isArray(surface)
|
|
40
|
+
|| !nonEmpty(surface.path) || !EVIDENCE_LIFECYCLE_CLASSES.includes(surface.classification)) {
|
|
41
|
+
signals.push({ type: 'unknown-changed-surface', source: `changedSurfaces[${index}]` })
|
|
42
|
+
continue
|
|
43
|
+
}
|
|
44
|
+
if (surface.classification === 'evidence-authorization') {
|
|
45
|
+
signals.push({ type: 'declared-lifecycle-surface', source: surface.path })
|
|
46
|
+
}
|
|
47
|
+
if (isEvidenceLifecycleSurface(surface.path)) {
|
|
48
|
+
signals.push({ type: 'sensitive-changed-surface', source: surface.path })
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (EVIDENCE_SEMANTICS.test(text)) signals.push({ type: 'lifecycle-semantics', source: 'title/body' })
|
|
54
|
+
const required = signals.length > 0
|
|
55
|
+
return {
|
|
56
|
+
classification: required ? 'evidence-authorization' : 'ordinary',
|
|
57
|
+
required,
|
|
58
|
+
denyByDefault: true,
|
|
59
|
+
signals,
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const SIGNALS = [
|
|
64
|
+
{
|
|
65
|
+
type: 'physical-presence',
|
|
66
|
+
pattern: /\b(physically present|physical presence|on[- ]site|in person|live (?:application|terminal|device|lane) operator)\b/i,
|
|
67
|
+
recommendation: 'external-verification',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
type: 'external-dispatch',
|
|
71
|
+
pattern: /\b(dispatch|send)\b[^.\n]{0,80}\b(external|operator|technician|auditor)\b|\bexternal dispatch\b/i,
|
|
72
|
+
recommendation: 'external-verification',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
type: 'provider-observation',
|
|
76
|
+
pattern: /\b(provider|acquirer|issuer|third[- ]party)\b[^.\n]{0,100}\b(observe|observations?|attest|certif(?:y|ication))\b|\b(observe|observations?)\b[^.\n]{0,100}\b(provider|acquirer|issuer|third[- ]party)\b/i,
|
|
77
|
+
recommendation: 'external-verification',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
type: 'third-party-approval',
|
|
81
|
+
pattern: /\b(provider|acquirer|issuer|third[- ]party|legal|regulator)\b[^.\n]{0,100}\b(approve|approval|sign[- ]off|consent)\b/i,
|
|
82
|
+
recommendation: 'external-verification',
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
type: 'human-decision',
|
|
86
|
+
pattern: /\b(product owner|human|legal|compliance|stakeholder)\b[^.\n]{0,100}\b(decide|decision|choose|select|approve)\b|\b(decide|decision|choose)\b[^.\n]{0,100}\b(policy|option|whether)\b/i,
|
|
87
|
+
recommendation: 'human-decision',
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
type: 'credentials',
|
|
91
|
+
pattern: /\b(credential|credentials|api key|secret|certificate|production access)\b/i,
|
|
92
|
+
recommendation: 'prerequisite',
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
type: 'hardware',
|
|
96
|
+
pattern: /\b(hardware|security key|physical terminal|device)\b/i,
|
|
97
|
+
recommendation: 'prerequisite',
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
type: 'prerequisite-input',
|
|
101
|
+
pattern: /\b(obtain|provision|procure|provide|land)\b[^.\n]{0,100}\b(before|prerequisite|input|required)\b|\b(before .* can begin|before implementation)\b/i,
|
|
102
|
+
recommendation: 'prerequisite',
|
|
103
|
+
},
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
function recommend(signals) {
|
|
107
|
+
const kinds = new Set(signals.map((signal) => signal.recommendation))
|
|
108
|
+
if (kinds.has('human-decision')) return 'human-decision'
|
|
109
|
+
if (kinds.has('external-verification')) return 'external-verification'
|
|
110
|
+
if (kinds.has('prerequisite')) return 'prerequisite'
|
|
111
|
+
return 'implementation'
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function classifyUnitIntake(input = {}) {
|
|
115
|
+
if (input == null || typeof input !== 'object' || Array.isArray(input)) {
|
|
116
|
+
throw new TypeError('intake must be a JSON object')
|
|
117
|
+
}
|
|
118
|
+
if (input.selectedKind !== undefined && !UNIT_KINDS.includes(input.selectedKind)) {
|
|
119
|
+
throw new TypeError(`selectedKind must be one of ${UNIT_KINDS.join('|')}`)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const text = `${nonEmpty(input.title) ? input.title : ''}\n${nonEmpty(input.body) ? input.body : ''}`
|
|
123
|
+
const signals = SIGNALS
|
|
124
|
+
.filter((signal) => signal.pattern.test(text))
|
|
125
|
+
.map(({ type, recommendation }) => ({ type, recommendation }))
|
|
126
|
+
const recommendedKind = recommend(signals)
|
|
127
|
+
const evidenceLifecycle = classifyEvidenceLifecycle(input, text)
|
|
128
|
+
const unitKind = input.selectedKind ?? recommendedKind
|
|
129
|
+
const required = recommendedKind !== 'implementation' || unitKind !== 'implementation'
|
|
130
|
+
const confirmed = required && nonEmpty(input.confirmedBy) && nonEmpty(input.confirmedAt)
|
|
131
|
+
const confirmation = {
|
|
132
|
+
required,
|
|
133
|
+
status: required ? (confirmed ? 'confirmed' : 'pending') : 'not-required',
|
|
134
|
+
kind: unitKind,
|
|
135
|
+
...(confirmed ? { confirmedBy: input.confirmedBy.trim(), confirmedAt: input.confirmedAt.trim() } : {}),
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return { unitKind, recommendedKind, signals, confirmation, evidenceLifecycle }
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function intakePatch(input = {}) {
|
|
142
|
+
const result = classifyUnitIntake(input)
|
|
143
|
+
return {
|
|
144
|
+
unitKind: result.unitKind,
|
|
145
|
+
kindRecommendation: {
|
|
146
|
+
kind: result.recommendedKind,
|
|
147
|
+
signals: result.signals,
|
|
148
|
+
},
|
|
149
|
+
kindConfirmation: result.confirmation,
|
|
150
|
+
evidenceLifecycle: result.evidenceLifecycle,
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function usage() {
|
|
155
|
+
return 'usage: unit-kind.mjs classify --stdin\n'
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function main(argv = process.argv.slice(2)) {
|
|
159
|
+
if (argv.length === 1 && ['--help', '-h', 'help'].includes(argv[0])) {
|
|
160
|
+
process.stdout.write(usage())
|
|
161
|
+
return 0
|
|
162
|
+
}
|
|
163
|
+
if (argv.length !== 2 || argv[0] !== 'classify' || argv[1] !== '--stdin') {
|
|
164
|
+
process.stderr.write(usage())
|
|
165
|
+
return 2
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
const input = JSON.parse(fs.readFileSync(0, 'utf8'))
|
|
169
|
+
process.stdout.write(`${JSON.stringify(intakePatch(input), null, 2)}\n`)
|
|
170
|
+
return 0
|
|
171
|
+
} catch (error) {
|
|
172
|
+
process.stderr.write(`unit-kind: ${error.message}\n`)
|
|
173
|
+
return 2
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
let launchedDirectly = false
|
|
178
|
+
let mainModuleError = null
|
|
179
|
+
try {
|
|
180
|
+
const { isMainModule } = await import('../../engineering-runtime/scripts/main-module.mjs')
|
|
181
|
+
if (typeof isMainModule !== 'function') throw new Error('main-module.mjs does not export isMainModule')
|
|
182
|
+
launchedDirectly = isMainModule(import.meta.url)
|
|
183
|
+
} catch (error) {
|
|
184
|
+
mainModuleError = error
|
|
185
|
+
try {
|
|
186
|
+
launchedDirectly = realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url))
|
|
187
|
+
} catch {
|
|
188
|
+
launchedDirectly = false
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (launchedDirectly && mainModuleError) {
|
|
193
|
+
console.error(`unit-kind: cannot load engineering-runtime main-module helper: ${mainModuleError.message}`)
|
|
194
|
+
process.exitCode = 1
|
|
195
|
+
} else if (launchedDirectly) {
|
|
196
|
+
process.exitCode = main()
|
|
197
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { UNIT_KINDS } from './unit-kind.mjs'
|
|
2
|
+
|
|
3
|
+
export const EXTERNAL_COMPLETION_SOURCES = ['authorized-actor-observation', 'third-party-decision']
|
|
4
|
+
|
|
5
|
+
const nonEmpty = (value) => typeof value === 'string' && value.trim().length > 0
|
|
6
|
+
const isObject = (value) => value != null && typeof value === 'object' && !Array.isArray(value)
|
|
7
|
+
const stringList = (value) => Array.isArray(value) && value.length > 0 && value.every(nonEmpty)
|
|
8
|
+
|
|
9
|
+
export function effectiveUnitKind(unit) {
|
|
10
|
+
return UNIT_KINDS.includes(unit?.unitKind) ? unit.unitKind : 'implementation'
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function validateConfirmation(unit, label, errors) {
|
|
14
|
+
const recommendation = isObject(unit.kindRecommendation) ? unit.kindRecommendation.kind : 'implementation'
|
|
15
|
+
const kind = effectiveUnitKind(unit)
|
|
16
|
+
const required = kind !== 'implementation' || recommendation !== 'implementation'
|
|
17
|
+
if (!required) return
|
|
18
|
+
const confirmation = unit.kindConfirmation
|
|
19
|
+
if (!isObject(confirmation) || confirmation.status !== 'confirmed') {
|
|
20
|
+
errors.push(`${label}.kindConfirmation must be explicitly confirmed before ${kind} enters the roster`)
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
if (confirmation.kind !== kind) errors.push(`${label}.kindConfirmation.kind must match unitKind ${kind}`)
|
|
24
|
+
if (!nonEmpty(confirmation.confirmedBy)) errors.push(`${label}.kindConfirmation.confirmedBy is required`)
|
|
25
|
+
if (!nonEmpty(confirmation.confirmedAt)) errors.push(`${label}.kindConfirmation.confirmedAt is required`)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function validateExternalProtocol(protocol, label, errors) {
|
|
29
|
+
if (!isObject(protocol)) {
|
|
30
|
+
errors.push(`${label}.externalProtocol is required for external-verification`)
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
for (const field of ['prerequisites', 'authorizedActors', 'allowedOperations']) {
|
|
34
|
+
if (!stringList(protocol[field])) errors.push(`${label}.externalProtocol.${field} must be a non-empty string array`)
|
|
35
|
+
}
|
|
36
|
+
for (const field of ['environmentIdentity', 'stopPolicy', 'retryPolicy', 'completionSource']) {
|
|
37
|
+
if (!nonEmpty(protocol[field])) errors.push(`${label}.externalProtocol.${field} is required`)
|
|
38
|
+
}
|
|
39
|
+
if (!EXTERNAL_COMPLETION_SOURCES.includes(protocol.completionSource)) {
|
|
40
|
+
errors.push(`${label}.externalProtocol.completionSource must be one of ${EXTERNAL_COMPLETION_SOURCES.join('|')}`)
|
|
41
|
+
}
|
|
42
|
+
if (!isObject(protocol.evidenceSchema) || !stringList(protocol.evidenceSchema.required)) {
|
|
43
|
+
errors.push(`${label}.externalProtocol.evidenceSchema.required must be a non-empty string array`)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function externalCompletionErrors(unit, label) {
|
|
48
|
+
const errors = []
|
|
49
|
+
const completion = unit.externalCompletion
|
|
50
|
+
if (completion === undefined || completion === null) return errors
|
|
51
|
+
const protocol = unit.externalProtocol
|
|
52
|
+
if (!isObject(completion)) return [`${label}.externalCompletion must be an object`]
|
|
53
|
+
if (!isObject(protocol)) return [`${label}.externalCompletion cannot be checked without externalProtocol`]
|
|
54
|
+
if (completion.source !== protocol.completionSource) errors.push(`${label}.externalCompletion.source must match externalProtocol.completionSource`)
|
|
55
|
+
if (!Array.isArray(protocol.authorizedActors) || !protocol.authorizedActors.includes(completion.actor)) {
|
|
56
|
+
errors.push(`${label}.externalCompletion.actor must be one of externalProtocol.authorizedActors`)
|
|
57
|
+
}
|
|
58
|
+
if (completion.environmentIdentity !== protocol.environmentIdentity) {
|
|
59
|
+
errors.push(`${label}.externalCompletion.environmentIdentity must match externalProtocol.environmentIdentity`)
|
|
60
|
+
}
|
|
61
|
+
if (!Array.isArray(protocol.allowedOperations) || !protocol.allowedOperations.includes(completion.operation)) {
|
|
62
|
+
errors.push(`${label}.externalCompletion.operation must be one of externalProtocol.allowedOperations`)
|
|
63
|
+
}
|
|
64
|
+
if (!nonEmpty(completion.observedAt)) errors.push(`${label}.externalCompletion.observedAt is required`)
|
|
65
|
+
if (!isObject(completion.evidence)) {
|
|
66
|
+
errors.push(`${label}.externalCompletion.evidence must satisfy externalProtocol.evidenceSchema`)
|
|
67
|
+
} else {
|
|
68
|
+
const missing = (protocol.evidenceSchema?.required ?? []).filter((key) => !nonEmpty(completion.evidence[key]))
|
|
69
|
+
if (missing.length) errors.push(`${label}.externalCompletion.evidenceSchema missing required evidence: ${missing.join(', ')}`)
|
|
70
|
+
}
|
|
71
|
+
return errors
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function validateUnitKind(unit, label = 'unit') {
|
|
75
|
+
const errors = []
|
|
76
|
+
if (!isObject(unit)) return [`${label} must be an object`]
|
|
77
|
+
if (unit.unitKind !== undefined && !UNIT_KINDS.includes(unit.unitKind)) {
|
|
78
|
+
errors.push(`${label}.unitKind must be one of ${UNIT_KINDS.join('|')}`)
|
|
79
|
+
return errors
|
|
80
|
+
}
|
|
81
|
+
if (unit.kindRecommendation !== undefined) {
|
|
82
|
+
if (!isObject(unit.kindRecommendation) || !UNIT_KINDS.includes(unit.kindRecommendation.kind)) {
|
|
83
|
+
errors.push(`${label}.kindRecommendation.kind must be one of ${UNIT_KINDS.join('|')}`)
|
|
84
|
+
} else if (!Array.isArray(unit.kindRecommendation.signals)) {
|
|
85
|
+
errors.push(`${label}.kindRecommendation.signals must be an array`)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
validateConfirmation(unit, label, errors)
|
|
89
|
+
const kind = effectiveUnitKind(unit)
|
|
90
|
+
if (kind === 'external-verification') {
|
|
91
|
+
validateExternalProtocol(unit.externalProtocol, label, errors)
|
|
92
|
+
errors.push(...externalCompletionErrors(unit, label))
|
|
93
|
+
}
|
|
94
|
+
if (kind !== 'implementation' && nonEmpty(unit.pr)) {
|
|
95
|
+
errors.push(`${label}: ${kind} may reference artifacts but may not own an implementation PR`)
|
|
96
|
+
}
|
|
97
|
+
if (kind !== 'implementation' && (unit.status === 'merge-ready' || unit.delivery !== undefined || unit.lastVerdict !== undefined)) {
|
|
98
|
+
errors.push(`${label}: ${kind} never uses code delivery or review PASS; an artifact PASS is not ${kind} completion`)
|
|
99
|
+
}
|
|
100
|
+
if (unit.completionDependsOn !== undefined && (!Array.isArray(unit.completionDependsOn) || !unit.completionDependsOn.every(nonEmpty))) {
|
|
101
|
+
errors.push(`${label}.completionDependsOn must be an array of non-empty unit ids`)
|
|
102
|
+
}
|
|
103
|
+
return errors
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function unitKindComplete(unit) {
|
|
107
|
+
const kind = effectiveUnitKind(unit)
|
|
108
|
+
if (kind === 'implementation') return false
|
|
109
|
+
if (kind === 'external-verification') {
|
|
110
|
+
const protocolErrors = []
|
|
111
|
+
validateExternalProtocol(unit.externalProtocol, 'unit', protocolErrors)
|
|
112
|
+
return protocolErrors.length === 0 && externalCompletionErrors(unit, 'unit').length === 0 && isObject(unit.externalCompletion)
|
|
113
|
+
}
|
|
114
|
+
if (kind === 'human-decision') {
|
|
115
|
+
return isObject(unit.humanDecision) && nonEmpty(unit.humanDecision.decision)
|
|
116
|
+
&& nonEmpty(unit.humanDecision.decidedBy) && nonEmpty(unit.humanDecision.decidedAt)
|
|
117
|
+
}
|
|
118
|
+
return isObject(unit.prerequisiteCompletion) && nonEmpty(unit.prerequisiteCompletion.source)
|
|
119
|
+
&& nonEmpty(unit.prerequisiteCompletion.completedAt) && nonEmpty(unit.prerequisiteCompletion.evidence)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function completionBlockers(manifest, unit) {
|
|
123
|
+
const units = Array.isArray(manifest?.units) ? manifest.units : []
|
|
124
|
+
const byId = new Map(units.filter(isObject).map((entry) => [entry.id, entry]))
|
|
125
|
+
return (Array.isArray(unit?.completionDependsOn) ? unit.completionDependsOn : [])
|
|
126
|
+
.filter((id) => !unitKindComplete(byId.get(id)))
|
|
127
|
+
}
|