@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,526 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// finalize-release — record a verified release's closure as ONE durable transition (#963).
|
|
3
|
+
//
|
|
4
|
+
// The failure this exists for. Forex v0.1.12 passed serving identity and the prod-active smoke,
|
|
5
|
+
// and the attempt was closed `released` — but the run manifest still read `step: verify` with no
|
|
6
|
+
// top-level `verification`, because assembling that record was a manual act nobody performed.
|
|
7
|
+
// The mandatory post-mortem then raised HIGH stopped-mid-machine and MEDIUM
|
|
8
|
+
// no-verification-record on a release production had already verified: the lease said free, the
|
|
9
|
+
// resume key said mid-exposure, and a resumed session had to reconcile live production to learn
|
|
10
|
+
// a state the standard workflow could have recorded atomically.
|
|
11
|
+
//
|
|
12
|
+
// What this operation is. One standard finalization: it VALIDATES every observation the released
|
|
13
|
+
// path owes — the candidate verdict, the post-authorization fence readings, the serving
|
|
14
|
+
// identity/digest confirmation, the durable-processing quiescence/resume where declared, the
|
|
15
|
+
// live (prod-active) verdict, and the attempt's `released` close — and then, in a single atomic
|
|
16
|
+
// manifest write, records the attempt close (when still open), the top-level `verification`
|
|
17
|
+
// block, and the terminal `step: done`. Any observation missing, stale, mismatched, or
|
|
18
|
+
// internally inconsistent REFUSES, and a refusal writes nothing.
|
|
19
|
+
//
|
|
20
|
+
// Resume. The write is one `writeManifest` rename, so a crash leaves exactly one of two states:
|
|
21
|
+
// `step: verify` with no `verification` (finalize has not happened — run it) or `step: done`
|
|
22
|
+
// with `verification` (it has — re-running re-validates the recorded transition against the
|
|
23
|
+
// `release.*` evidence and no-ops byte-identically; a recorded terminal state that no longer
|
|
24
|
+
// matches the evidence refuses rather than re-asserting it). Any other combination —
|
|
25
|
+
// done-without-verification, verify-with-verification — is a hand-assembled ambiguity and
|
|
26
|
+
// refuses.
|
|
27
|
+
//
|
|
28
|
+
// What it deliberately does NOT do (issue non-goals): the final tag, the GitHub Release, and
|
|
29
|
+
// the evidence-bundle emit/attach are network/durable-artifact operations that stay OUTSIDE
|
|
30
|
+
// this filesystem transaction, and nothing here weakens the observed-production exit criteria —
|
|
31
|
+
// every check below is a comparison against recorded observations, which is what makes a
|
|
32
|
+
// refusal a statement about evidence rather than about the world.
|
|
33
|
+
|
|
34
|
+
import { isMainModule } from '../../engineering-runtime/scripts/main-module.mjs'
|
|
35
|
+
import {
|
|
36
|
+
ATTEMPT_CLOSE_CAUSES,
|
|
37
|
+
appendReleaseStateTransition,
|
|
38
|
+
readManifest,
|
|
39
|
+
recordReleaseAttemptState,
|
|
40
|
+
validate,
|
|
41
|
+
writeManifest,
|
|
42
|
+
} from '../../resolve-issues/scripts/run-state.mjs'
|
|
43
|
+
import { verifyCompletionArtifact } from './adapter-completion-artifact.mjs'
|
|
44
|
+
import { evaluateFloorReadiness } from './green-gate.mjs'
|
|
45
|
+
|
|
46
|
+
export const FINALIZER = 'finalize-release.mjs'
|
|
47
|
+
|
|
48
|
+
const SHA = /^[0-9a-f]{40}$/i
|
|
49
|
+
const nonEmpty = (v) => typeof v === 'string' && v.trim() !== ''
|
|
50
|
+
const isObject = (v) => v != null && typeof v === 'object' && !Array.isArray(v)
|
|
51
|
+
|
|
52
|
+
function canonical(value) {
|
|
53
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(',')}]`
|
|
54
|
+
if (isObject(value)) {
|
|
55
|
+
const keys = Object.keys(value).filter((k) => value[k] !== undefined).sort()
|
|
56
|
+
return `{${keys.map((k) => `${JSON.stringify(k)}:${canonical(value[k])}`).join(',')}}`
|
|
57
|
+
}
|
|
58
|
+
return JSON.stringify(value)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const equivalent = (a, b) => canonical(a) === canonical(b)
|
|
62
|
+
|
|
63
|
+
function requireTime(value, label) {
|
|
64
|
+
const t = typeof value === 'string' ? Date.parse(value) : NaN
|
|
65
|
+
if (!Number.isFinite(t)) throw new Error(`${label} must be an ISO timestamp (got ${JSON.stringify(value)})`)
|
|
66
|
+
return t
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function requireObject(value, label) {
|
|
70
|
+
if (!isObject(value)) throw new Error(`${label} is required and must be an object`)
|
|
71
|
+
return value
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function shaEqual(a, b) {
|
|
75
|
+
return SHA.test(a) && SHA.test(b) && String(a).toLowerCase() === String(b).toLowerCase()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function digestEqual(a, b) {
|
|
79
|
+
return nonEmpty(a) && nonEmpty(b) && String(a).toLowerCase() === String(b).toLowerCase()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ---- the validations ----
|
|
83
|
+
|
|
84
|
+
function validateAttempt(release) {
|
|
85
|
+
const attempt = requireObject(release.attempt, 'release.attempt')
|
|
86
|
+
if (attempt.state === 'open' || attempt.state === 'released') return attempt
|
|
87
|
+
throw new Error(
|
|
88
|
+
`release.attempt.state is ${JSON.stringify(attempt.state)} — the finalizer records only the `
|
|
89
|
+
+ 'verified released transition; failed-pre-promotion, rolled-back, revoked and blocked-manual '
|
|
90
|
+
+ 'exits have their own closes (routing.md step 5)',
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function validateCandidate(release) {
|
|
95
|
+
const identity = requireObject(release.candidateIdentity, 'release.candidateIdentity')
|
|
96
|
+
if (!SHA.test(identity.sourceSha)) {
|
|
97
|
+
throw new Error('release.candidateIdentity.sourceSha must be one exact 40-character SHA')
|
|
98
|
+
}
|
|
99
|
+
for (const field of ['revision', 'url', 'imageDigest']) {
|
|
100
|
+
if (!nonEmpty(identity[field])) throw new Error(`release.candidateIdentity.${field} is required`)
|
|
101
|
+
}
|
|
102
|
+
if (identity.revision !== release.candidateRevision) {
|
|
103
|
+
throw new Error(`release.candidateIdentity.revision (${identity.revision}) does not match release.candidateRevision (${release.candidateRevision})`)
|
|
104
|
+
}
|
|
105
|
+
if (identity.url !== release.candidateUrl) {
|
|
106
|
+
throw new Error(`release.candidateIdentity.url (${identity.url}) does not match release.candidateUrl (${release.candidateUrl})`)
|
|
107
|
+
}
|
|
108
|
+
const sha = release.candidateSha || release.tagSha || identity.sourceSha
|
|
109
|
+
if (!SHA.test(sha)) throw new Error('release.candidateSha must be one exact 40-character SHA')
|
|
110
|
+
if (!shaEqual(identity.sourceSha, sha)) {
|
|
111
|
+
throw new Error('release.candidateIdentity.sourceSha does not match the recorded candidateSha/tagSha')
|
|
112
|
+
}
|
|
113
|
+
if (nonEmpty(release.tagSha) && !shaEqual(release.tagSha, sha)) {
|
|
114
|
+
throw new Error('release.tagSha does not match the candidate SHA — the final tag must name the verified SHA (principle 10)')
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const verdict = requireObject(release.candidateVerdict, 'release.candidateVerdict')
|
|
118
|
+
if (verdict.state !== 'ALIVE') {
|
|
119
|
+
throw new Error(`release.candidateVerdict.state must be ALIVE on the released path (got ${JSON.stringify(verdict.state)})`)
|
|
120
|
+
}
|
|
121
|
+
if (!nonEmpty(verdict.artifactPath)) throw new Error('release.candidateVerdict.artifactPath is required — the verdict names its durable artifact')
|
|
122
|
+
requireTime(verdict.generatedAt, 'release.candidateVerdict.generatedAt')
|
|
123
|
+
if (!nonEmpty(verdict.target) || verdict.target !== release.candidateUrl) {
|
|
124
|
+
throw new Error('release.candidateVerdict.target must be the exact recorded candidateUrl')
|
|
125
|
+
}
|
|
126
|
+
return { identity, sha, verdict, verdictAt: Date.parse(verdict.generatedAt) }
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function validateFence(manifest, candidate) {
|
|
130
|
+
const release = manifest.release
|
|
131
|
+
const fence = requireObject(release.recheckFence, 'release.recheckFence')
|
|
132
|
+
const gate = requireObject(fence.gate, 'release.recheckFence.gate')
|
|
133
|
+
if (gate.decision !== 'fire' || gate.phase !== 'shift') {
|
|
134
|
+
throw new Error(`release.recheckFence.gate must record decision "fire" at phase "shift" (got ${JSON.stringify(gate.decision)}/${JSON.stringify(gate.phase)}) — the pre-exposure gate did not authorize this shift`)
|
|
135
|
+
}
|
|
136
|
+
if (!Array.isArray(gate.failed) || gate.failed.length !== 0) {
|
|
137
|
+
throw new Error(`release.recheckFence.gate.failed must be empty on an authorized shift (got ${JSON.stringify(gate.failed)})`)
|
|
138
|
+
}
|
|
139
|
+
const evidence = requireObject(gate.evidence, 'release.recheckFence.gate.evidence')
|
|
140
|
+
if (evidence.verdict !== 'ALIVE' || evidence.verdictTarget !== release.candidateUrl || evidence.noMutations !== true) {
|
|
141
|
+
throw new Error('release.recheckFence.gate.evidence must record a read-only ALIVE verdict on the exact candidate URL (verdict, verdictTarget, noMutations)')
|
|
142
|
+
}
|
|
143
|
+
if (evidence.generatedAt !== candidate.verdict.generatedAt) {
|
|
144
|
+
throw new Error('release.recheckFence.gate.evidence.generatedAt does not match the recorded candidateVerdict — the fence evaluated a different verdict artifact')
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Freshness as the fence recorded it — a historical reading, re-checked for internal
|
|
148
|
+
// consistency rather than re-taken: the age must sit inside the recorded window AND agree
|
|
149
|
+
// with the verdict stamp and the fence's own checkedAt.
|
|
150
|
+
const age = evidence.verdictAgeMinutes
|
|
151
|
+
const window = evidence.verdictFreshnessMinutes
|
|
152
|
+
if (!Number.isFinite(age) || !Number.isFinite(window) || window <= 0) {
|
|
153
|
+
throw new Error('release.recheckFence.gate.evidence must record verdictAgeMinutes and a positive verdictFreshnessMinutes')
|
|
154
|
+
}
|
|
155
|
+
if (age < 0 || age > window) {
|
|
156
|
+
throw new Error(`the fence recorded a stale verdict: age ${age}m outside the ${window}m window — an expired verdict routes back to 4a, never onward to the shift`)
|
|
157
|
+
}
|
|
158
|
+
const fenceAt = requireTime(fence.checkedAt, 'release.recheckFence.checkedAt')
|
|
159
|
+
const observedAge = (fenceAt - candidate.verdictAt) / 60_000
|
|
160
|
+
if (observedAge < 0 || observedAge > window || Math.abs(observedAge - age) > 0.01) {
|
|
161
|
+
throw new Error(`the fence freshness record is inconsistent: recorded age ${age}m but the verdict stamp and fence checkedAt give ${observedAge.toFixed(2)}m`)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// The floor the fence recorded must be the floor the manifest still declares — a floor that
|
|
165
|
+
// moved between the shift and the finalization is exactly the staleness this operation refuses.
|
|
166
|
+
const floor = evaluateFloorReadiness(manifest, fenceAt)
|
|
167
|
+
if (!floor.ok) throw new Error(`production floor was not ready at the traffic fence: ${floor.detail}`)
|
|
168
|
+
if (!equivalent(evidence.floorReadiness, floor.evidence.floorReadiness)) {
|
|
169
|
+
throw new Error('release.recheckFence.gate.evidence.floorReadiness does not match the manifest\'s current floor readiness record')
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (fence.candidateResolvesTo !== candidate.identity.revision) {
|
|
173
|
+
throw new Error(`release.recheckFence.candidateResolvesTo (${JSON.stringify(fence.candidateResolvesTo)}) does not match the verified candidate revision — the fence must record what the URL resolved to, not a judgment`)
|
|
174
|
+
}
|
|
175
|
+
if (fence.candidatePercent !== 0 || fence.previousRevisionPercent !== 100) {
|
|
176
|
+
throw new Error('release.recheckFence must record the candidate at 0% and the previous revision at exactly 100%')
|
|
177
|
+
}
|
|
178
|
+
return { fence, fenceAt }
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function validateServing(release, candidate) {
|
|
182
|
+
const serving = requireObject(release.servingConfirmation, 'release.servingConfirmation')
|
|
183
|
+
const gate = requireObject(serving.gate, 'release.servingConfirmation.gate')
|
|
184
|
+
if (gate.decision !== 'accept' || gate.phase !== 'serving') {
|
|
185
|
+
throw new Error(`release.servingConfirmation.gate must record decision "accept" at phase "serving" (got ${JSON.stringify(gate.decision)}/${JSON.stringify(gate.phase)})`)
|
|
186
|
+
}
|
|
187
|
+
if (!Array.isArray(gate.failed) || gate.failed.length !== 0) {
|
|
188
|
+
throw new Error(`release.servingConfirmation.gate.failed must be empty on an accepted serving confirmation (got ${JSON.stringify(gate.failed)})`)
|
|
189
|
+
}
|
|
190
|
+
const evidence = requireObject(gate.evidence, 'release.servingConfirmation.gate.evidence')
|
|
191
|
+
if (!equivalent(evidence.recorded, candidate.identity)) {
|
|
192
|
+
throw new Error('release.servingConfirmation.gate.evidence.recorded must be the complete candidateIdentity tuple the serving check ran against')
|
|
193
|
+
}
|
|
194
|
+
if (isObject(evidence.serving)) {
|
|
195
|
+
const live = evidence.serving
|
|
196
|
+
if (!shaEqual(live.sourceSha, candidate.sha)
|
|
197
|
+
|| live.revision !== candidate.identity.revision
|
|
198
|
+
|| !digestEqual(live.imageDigest, candidate.identity.imageDigest)) {
|
|
199
|
+
throw new Error('release.servingConfirmation serving tuple does not exactly match the candidate identity (sourceSha, revision, imageDigest)')
|
|
200
|
+
}
|
|
201
|
+
} else if (isObject(evidence.fallback)) {
|
|
202
|
+
// The declared no-attesting-endpoint weakening (pre-flight item 3): the serving revision's
|
|
203
|
+
// configured digest against the candidate's — a comparison, never a revision-name read.
|
|
204
|
+
if (!digestEqual(evidence.fallback.servingImageDigest, candidate.identity.imageDigest)) {
|
|
205
|
+
throw new Error('release.servingConfirmation fallback serving image digest does not match the candidate identity digest')
|
|
206
|
+
}
|
|
207
|
+
} else {
|
|
208
|
+
throw new Error('release.servingConfirmation.gate.evidence records no serving observation — neither the re-attested tuple nor the declared digest fallback')
|
|
209
|
+
}
|
|
210
|
+
if (serving.candidatePercent !== 100 || serving.previousRevisionPercent !== 0) {
|
|
211
|
+
throw new Error('release.servingConfirmation must record the candidate at 100% and the previous revision at 0%')
|
|
212
|
+
}
|
|
213
|
+
const servingAt = requireTime(serving.checkedAt, 'release.servingConfirmation.checkedAt')
|
|
214
|
+
return { serving, servingAt }
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function validateQuiescence(manifest, candidate, fenceAt, servingAt, liveAt) {
|
|
218
|
+
const declared = manifest.envelope && manifest.envelope.durableProcessing
|
|
219
|
+
if (!isObject(declared) || typeof declared.present !== 'boolean') {
|
|
220
|
+
throw new Error('envelope.durableProcessing must explicitly declare present true or false — an unasked question is not an absent record (pre-flight item 6)')
|
|
221
|
+
}
|
|
222
|
+
const recorded = manifest.release.quiescence
|
|
223
|
+
if (declared.present === false) {
|
|
224
|
+
if (recorded !== undefined && recorded !== null) {
|
|
225
|
+
throw new Error('release.quiescence must be absent when durable processing is declared absent — a record here contradicts the declaration')
|
|
226
|
+
}
|
|
227
|
+
return null
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const q = requireObject(recorded, 'release.quiescence')
|
|
231
|
+
if (!Array.isArray(q.pauseOrder) || q.pauseOrder.length === 0 || q.pauseOrder.some((s) => !nonEmpty(s))) {
|
|
232
|
+
throw new Error('release.quiescence.pauseOrder must record the non-empty pause order as executed')
|
|
233
|
+
}
|
|
234
|
+
if (!equivalent(q.pauseOrder, declared.pauseOrder)) {
|
|
235
|
+
throw new Error('release.quiescence.pauseOrder does not match the order declared at pre-flight (envelope.durableProcessing.pauseOrder)')
|
|
236
|
+
}
|
|
237
|
+
if (!Array.isArray(q.resumeOrder) || !equivalent(q.resumeOrder, [...q.pauseOrder].reverse())) {
|
|
238
|
+
throw new Error('release.quiescence.resumeOrder must be the exact reverse of the executed pause order (principle 14)')
|
|
239
|
+
}
|
|
240
|
+
const proof = requireObject(q.proof, 'release.quiescence.proof')
|
|
241
|
+
const readings = Object.entries(proof).filter(([k]) => k !== 'checkedAt')
|
|
242
|
+
if (readings.length === 0 || 'quiesced' in proof || readings.every(([, v]) => typeof v === 'boolean')) {
|
|
243
|
+
throw new Error('release.quiescence.proof must record the observed processing-state value (an in-flight count, a queue depth), never `quiesced: true` — the judgment one field deeper')
|
|
244
|
+
}
|
|
245
|
+
const pausedAt = requireTime(q.pausedAt, 'release.quiescence.pausedAt')
|
|
246
|
+
const proofAt = requireTime(proof.checkedAt, 'release.quiescence.proof.checkedAt')
|
|
247
|
+
const resumedAt = requireTime(q.resumedAt, 'release.quiescence.resumedAt')
|
|
248
|
+
if (!(pausedAt <= proofAt && proofAt <= fenceAt && fenceAt <= servingAt && servingAt <= resumedAt && resumedAt <= liveAt)) {
|
|
249
|
+
throw new Error('release.quiescence is not in the durable order pause -> prove-quiescent -> fence -> serving-identity -> reverse-resume -> live (principle 14)')
|
|
250
|
+
}
|
|
251
|
+
if (q.servingRevisionAtResume !== candidate.identity.revision) {
|
|
252
|
+
throw new Error(`release.quiescence.servingRevisionAtResume (${JSON.stringify(q.servingRevisionAtResume)}) must be the verified candidate revision — processing was resumed against a revision nobody confirmed`)
|
|
253
|
+
}
|
|
254
|
+
return q
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function validateLiveVerdict(manifest, release, fenceAt) {
|
|
258
|
+
const verdict = requireObject(release.liveVerdict, 'release.liveVerdict')
|
|
259
|
+
if (verdict.state !== 'ALIVE') {
|
|
260
|
+
throw new Error(`release.liveVerdict.state must be ALIVE on the released path (got ${JSON.stringify(verdict.state)})`)
|
|
261
|
+
}
|
|
262
|
+
if (!nonEmpty(verdict.artifactPath)) throw new Error('release.liveVerdict.artifactPath is required — the active verdict names its durable artifact')
|
|
263
|
+
const liveAt = requireTime(verdict.generatedAt, 'release.liveVerdict.generatedAt')
|
|
264
|
+
if (!nonEmpty(verdict.target)) throw new Error('release.liveVerdict.target is required')
|
|
265
|
+
const floor = evaluateFloorReadiness(manifest, fenceAt)
|
|
266
|
+
const floorTarget = floor.ok && floor.evidence.floorReadiness && floor.evidence.floorReadiness.target
|
|
267
|
+
if (nonEmpty(floorTarget) && verdict.target !== floorTarget) {
|
|
268
|
+
throw new Error(`release.liveVerdict.target (${verdict.target}) does not match the declared prod-active floor target (${floorTarget})`)
|
|
269
|
+
}
|
|
270
|
+
return { verdict, liveAt }
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ---- the one transition ----
|
|
274
|
+
|
|
275
|
+
// The checked completion record and observation blocks the one transition assembles into `verification` — the REQUIRED keys
|
|
276
|
+
// of the terminal record. report.mjs's step-5 reader checks this exact list (via
|
|
277
|
+
// isFinalizedVerification below) instead of accepting any object-valued verification, so the
|
|
278
|
+
// writer's shape and the reader's question share one source.
|
|
279
|
+
const VERIFICATION_OBSERVATIONS = ['adapterCompletion', 'candidate', 'candidateVerdict', 'recheckFence', 'servingConfirmation', 'liveVerdict']
|
|
280
|
+
|
|
281
|
+
function assembleVerification(release, now, quiescence, adapterCompletion) {
|
|
282
|
+
return {
|
|
283
|
+
finalizedAt: now,
|
|
284
|
+
finalizedBy: FINALIZER,
|
|
285
|
+
adapterCompletion,
|
|
286
|
+
candidate: release.candidateIdentity,
|
|
287
|
+
candidateVerdict: release.candidateVerdict,
|
|
288
|
+
recheckFence: release.recheckFence,
|
|
289
|
+
servingConfirmation: release.servingConfirmation,
|
|
290
|
+
liveVerdict: release.liveVerdict,
|
|
291
|
+
...(quiescence ? { quiescence } : {}),
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// The reader-level half of the terminal record: is this the verification block the one
|
|
296
|
+
// transition writes? Presence and shape only — full cross-field consistency is validateAll's
|
|
297
|
+
// job on the write/resume path; a checkpoint reader owes "the required observations are
|
|
298
|
+
// recorded", not a re-verdict. A hand-assembled or empty `verification` (the
|
|
299
|
+
// release-finalization-terminal-proof review finding: `{}` rendered report.mjs's Step 5 done
|
|
300
|
+
// while this finalizer refused the same state) fails here exactly as it fails finalize.
|
|
301
|
+
export function isFinalizedVerification(verification) {
|
|
302
|
+
if (!isObject(verification)) return false
|
|
303
|
+
if (verification.finalizedBy !== FINALIZER) return false
|
|
304
|
+
if (!Number.isFinite(Date.parse(verification.finalizedAt || ''))) return false
|
|
305
|
+
return VERIFICATION_OBSERVATIONS.every((key) => {
|
|
306
|
+
const block = verification[key]
|
|
307
|
+
return isObject(block) && Object.keys(block).length > 0
|
|
308
|
+
})
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function validateAll(manifest) {
|
|
312
|
+
if (manifest.skill !== 'resolve-release') {
|
|
313
|
+
throw new Error(`finalize-release requires a resolve-release manifest (got skill ${JSON.stringify(manifest.skill)})`)
|
|
314
|
+
}
|
|
315
|
+
const check = validate(manifest)
|
|
316
|
+
if (!check.ok) throw new Error(`release manifest is invalid before finalization: ${check.errors.join('; ')}`)
|
|
317
|
+
const release = requireObject(manifest.release, 'release')
|
|
318
|
+
const attempt = validateAttempt(release)
|
|
319
|
+
const candidate = validateCandidate(release)
|
|
320
|
+
const { fenceAt } = validateFence(manifest, candidate)
|
|
321
|
+
const { servingAt } = validateServing(release, candidate)
|
|
322
|
+
const { liveAt } = validateLiveVerdict(manifest, release, fenceAt)
|
|
323
|
+
if (!(candidate.verdictAt <= fenceAt && fenceAt <= servingAt && servingAt <= liveAt)) {
|
|
324
|
+
throw new Error('the verification evidence is not in the durable order candidate-verdict → fence → serving → live')
|
|
325
|
+
}
|
|
326
|
+
const quiescence = validateQuiescence(manifest, candidate, fenceAt, servingAt, liveAt)
|
|
327
|
+
return { release, attempt, candidate, quiescence }
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Acquires completion evidence, validates, then returns the finalized manifest; the CLI owns the manifest write.
|
|
331
|
+
// Throws with a named reason on any missing, stale, mismatched, or inconsistent observation.
|
|
332
|
+
export function finalizeRelease(manifest, {
|
|
333
|
+
now = new Date().toISOString(),
|
|
334
|
+
adapterCompletionArtifact,
|
|
335
|
+
adapterComponentHead,
|
|
336
|
+
adapterCompletionSha256,
|
|
337
|
+
} = {}) {
|
|
338
|
+
requireTime(now, 'now')
|
|
339
|
+
if (!isObject(manifest)) throw new Error('the manifest must be a JSON object')
|
|
340
|
+
const adapterCompletion = verifyCompletionArtifact(adapterCompletionArtifact, {
|
|
341
|
+
expectedComponentHead: adapterComponentHead,
|
|
342
|
+
expectedSha256: adapterCompletionSha256,
|
|
343
|
+
})
|
|
344
|
+
const hasVerification = isObject(manifest.verification)
|
|
345
|
+
|
|
346
|
+
if (manifest.step === 'done') {
|
|
347
|
+
if (!hasVerification) {
|
|
348
|
+
throw new Error('step is "done" but no top-level verification block is recorded — an ambiguous hand-assembled state; reconcile the manifest by hand, the finalizer will not guess at it')
|
|
349
|
+
}
|
|
350
|
+
// Resume: re-validate every observation, then require the recorded terminal state to be
|
|
351
|
+
// exactly what this operation would have written — anything less is drift, not a resume.
|
|
352
|
+
const { release, attempt, candidate, quiescence } = validateAll(manifest)
|
|
353
|
+
if (attempt.state !== 'released' || attempt.closedBy !== ATTEMPT_CLOSE_CAUSES.released) {
|
|
354
|
+
throw new Error('a finalized manifest must carry the attempt closed released by verified-release')
|
|
355
|
+
}
|
|
356
|
+
const expected = assembleVerification(release, null, quiescence, adapterCompletion)
|
|
357
|
+
const recorded = { ...manifest.verification }
|
|
358
|
+
delete recorded.finalizedAt
|
|
359
|
+
delete expected.finalizedAt
|
|
360
|
+
if (!equivalent(recorded, expected)) {
|
|
361
|
+
throw new Error('the recorded verification block does not match the re-verified release.* evidence — the terminal state drifted from its observations; reconcile by hand')
|
|
362
|
+
}
|
|
363
|
+
if (nonEmpty(manifest.shippedSha) && !shaEqual(manifest.shippedSha, candidate.sha)) {
|
|
364
|
+
throw new Error('shippedSha does not match the verified candidate SHA')
|
|
365
|
+
}
|
|
366
|
+
return { manifest, resumed: true, finalizedAt: manifest.verification.finalizedAt }
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (manifest.step !== 'verify') {
|
|
370
|
+
throw new Error(`finalize-release runs at step "verify" (or "done" on resume); got step ${JSON.stringify(manifest.step)}`)
|
|
371
|
+
}
|
|
372
|
+
if (hasVerification) {
|
|
373
|
+
throw new Error('step is "verify" but a verification block is already recorded — an ambiguous hand-assembled state; reconcile the manifest by hand')
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const { release, attempt, candidate, quiescence } = validateAll(manifest)
|
|
377
|
+
|
|
378
|
+
let out = manifest
|
|
379
|
+
if (attempt.state === 'open') {
|
|
380
|
+
// Use the canonical attempt writer for the closure shape and invariants. Its intermediate
|
|
381
|
+
// history event is intentionally not persisted: finalization is one atomic transition from
|
|
382
|
+
// verify/open directly to done/released, so the combined event is appended after the full
|
|
383
|
+
// terminal manifest has been assembled.
|
|
384
|
+
const evidence = `serving ${candidate.identity.revision} at 100% for ${candidate.sha}; prod-active ALIVE (${release.liveVerdict.artifactPath})`
|
|
385
|
+
out = recordReleaseAttemptState(out, {
|
|
386
|
+
state: 'released',
|
|
387
|
+
closedBy: ATTEMPT_CLOSE_CAUSES.released,
|
|
388
|
+
evidence,
|
|
389
|
+
}, { at: now })
|
|
390
|
+
// #963's transaction clock owns the terminal attempt record; #913's writer clock
|
|
391
|
+
// independently owns the history event timestamp.
|
|
392
|
+
out.release.attempt.closedAt = now
|
|
393
|
+
if (manifest.releaseHistory === undefined) delete out.releaseHistory
|
|
394
|
+
else out.releaseHistory = structuredClone(manifest.releaseHistory)
|
|
395
|
+
}
|
|
396
|
+
out = {
|
|
397
|
+
...structuredClone(out),
|
|
398
|
+
verification: assembleVerification(out.release, now, quiescence, adapterCompletion),
|
|
399
|
+
step: 'done',
|
|
400
|
+
shippedSha: candidate.sha,
|
|
401
|
+
updatedAt: now,
|
|
402
|
+
}
|
|
403
|
+
out = appendReleaseStateTransition(manifest, out)
|
|
404
|
+
const check = validate(out)
|
|
405
|
+
if (!check.ok) throw new Error(`the finalized manifest is invalid: ${check.errors.join('; ')}`)
|
|
406
|
+
return { manifest: out, resumed: false, finalizedAt: now }
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// ---- CLI ----
|
|
410
|
+
|
|
411
|
+
const USAGE = `finalize-release — validate a verified release's observations and record the
|
|
412
|
+
terminal closure as one durable transition.
|
|
413
|
+
|
|
414
|
+
Usage:
|
|
415
|
+
finalize-release.mjs <run-manifest.json> --adapter-completion-artifact <path>
|
|
416
|
+
--adapter-component-head <40-hex-sha> --adapter-completion-sha256 <64-hex-sha256>
|
|
417
|
+
[--now <iso>]
|
|
418
|
+
|
|
419
|
+
Runs at step "verify" on the released path: validates the candidate verdict, the checked
|
|
420
|
+
isolated-adapter completion artifact under the finalizer-owned suite policy, the
|
|
421
|
+
post-authorization re-check fence, the serving identity/digest confirmation, the
|
|
422
|
+
durable-processing quiescence/resume where declared, and the live prod-active verdict; then, in
|
|
423
|
+
one atomic manifest write, closes the attempt released/verified-release (when still open),
|
|
424
|
+
records the top-level verification block, and advances step to done.
|
|
425
|
+
|
|
426
|
+
Any missing, stale, mismatched, or inconsistent observation refuses (exit 1) and writes
|
|
427
|
+
nothing. Re-running after success re-validates and no-ops. The final tag, GitHub Release, and
|
|
428
|
+
evidence-bundle emit/attach are separate operations (routing.md step 5); report.mjs runs after
|
|
429
|
+
this transition on the released path.`
|
|
430
|
+
|
|
431
|
+
export function main(argv, deps = {}) {
|
|
432
|
+
const args = [...argv]
|
|
433
|
+
const options = {
|
|
434
|
+
manifestPath: null,
|
|
435
|
+
now: null,
|
|
436
|
+
adapterCompletionArtifact: null,
|
|
437
|
+
adapterComponentHead: null,
|
|
438
|
+
adapterCompletionSha256: null,
|
|
439
|
+
}
|
|
440
|
+
const positional = []
|
|
441
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
442
|
+
const arg = args[i]
|
|
443
|
+
if (arg === '--help' || arg === '-h') {
|
|
444
|
+
console.log(USAGE)
|
|
445
|
+
return 0
|
|
446
|
+
} else if (arg === '--adapter-completion-artifact') {
|
|
447
|
+
const value = args[++i]
|
|
448
|
+
if (value === undefined || value.startsWith('--')) {
|
|
449
|
+
throw new Error('--adapter-completion-artifact needs a path value')
|
|
450
|
+
}
|
|
451
|
+
options.adapterCompletionArtifact = value
|
|
452
|
+
} else if (arg === '--adapter-component-head') {
|
|
453
|
+
const value = args[++i]
|
|
454
|
+
if (value === undefined || value.startsWith('--')) {
|
|
455
|
+
throw new Error('--adapter-component-head needs a SHA value')
|
|
456
|
+
}
|
|
457
|
+
options.adapterComponentHead = value
|
|
458
|
+
} else if (arg === '--adapter-completion-sha256') {
|
|
459
|
+
const value = args[++i]
|
|
460
|
+
if (value === undefined || value.startsWith('--')) {
|
|
461
|
+
throw new Error('--adapter-completion-sha256 needs a SHA-256 value')
|
|
462
|
+
}
|
|
463
|
+
options.adapterCompletionSha256 = value
|
|
464
|
+
} else if (arg === '--now') {
|
|
465
|
+
const value = args[++i]
|
|
466
|
+
if (value === undefined || value.startsWith('--')) throw new Error('--now needs an ISO timestamp value')
|
|
467
|
+
options.now = value
|
|
468
|
+
} else if (arg.startsWith('--')) {
|
|
469
|
+
throw new Error(`unknown argument ${arg}\n\n${USAGE}`)
|
|
470
|
+
} else {
|
|
471
|
+
positional.push(arg)
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
if (positional.length !== 1) {
|
|
475
|
+
console.error(USAGE)
|
|
476
|
+
return 2
|
|
477
|
+
}
|
|
478
|
+
if (options.adapterCompletionArtifact == null || options.adapterComponentHead == null
|
|
479
|
+
|| options.adapterCompletionSha256 == null) {
|
|
480
|
+
console.error(USAGE)
|
|
481
|
+
return 2
|
|
482
|
+
}
|
|
483
|
+
const [manifestPath] = positional
|
|
484
|
+
const read = deps.readManifest || readManifest
|
|
485
|
+
const write = deps.writeManifest || writeManifest
|
|
486
|
+
let manifest
|
|
487
|
+
try {
|
|
488
|
+
manifest = read(manifestPath)
|
|
489
|
+
} catch (error) {
|
|
490
|
+
console.error(`finalize-release: manifest is not readable JSON: ${manifestPath} — ${error.message || error}`)
|
|
491
|
+
return 1
|
|
492
|
+
}
|
|
493
|
+
if (!isObject(manifest)) {
|
|
494
|
+
console.error(`finalize-release: manifest is not readable JSON: ${manifestPath}`)
|
|
495
|
+
return 1
|
|
496
|
+
}
|
|
497
|
+
try {
|
|
498
|
+
const result = finalizeRelease(manifest, {
|
|
499
|
+
now: options.now || new Date().toISOString(),
|
|
500
|
+
adapterCompletionArtifact: options.adapterCompletionArtifact,
|
|
501
|
+
adapterComponentHead: options.adapterComponentHead,
|
|
502
|
+
adapterCompletionSha256: options.adapterCompletionSha256,
|
|
503
|
+
})
|
|
504
|
+
if (!result.resumed) write(manifestPath, result.manifest)
|
|
505
|
+
console.log(JSON.stringify({
|
|
506
|
+
ok: true,
|
|
507
|
+
manifestPath,
|
|
508
|
+
step: result.manifest.step,
|
|
509
|
+
resumed: result.resumed,
|
|
510
|
+
finalizedAt: result.finalizedAt,
|
|
511
|
+
}, null, 2))
|
|
512
|
+
return 0
|
|
513
|
+
} catch (error) {
|
|
514
|
+
console.error(`finalize-release: ${error.message || error}`)
|
|
515
|
+
return 1
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
if (isMainModule(import.meta.url)) {
|
|
520
|
+
try {
|
|
521
|
+
process.exitCode = main(process.argv.slice(2))
|
|
522
|
+
} catch (error) {
|
|
523
|
+
console.error(`finalize-release: ${error.message || error}`)
|
|
524
|
+
process.exitCode = 1
|
|
525
|
+
}
|
|
526
|
+
}
|