@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,137 @@
|
|
|
1
|
+
# Steps 0-2: intake, assemble, promotion PR
|
|
2
|
+
|
|
3
|
+
From the `resolve-issues` handoff to an open promotion PR. Nothing is exposed in
|
|
4
|
+
this phase and no candidate exists yet. Next phase:
|
|
5
|
+
[candidate.md](candidate.md).
|
|
6
|
+
|
|
7
|
+
## Watching PR delivery
|
|
8
|
+
|
|
9
|
+
Sequential execution is not a reason to pause the release. A build, queue, deploy,
|
|
10
|
+
or post-deploy gate is watched by one launch of its background watcher, not a poll
|
|
11
|
+
loop that spends a turn per check. When it fires, the notification is a continuation
|
|
12
|
+
trigger: read and record the result, then carry straight on in the same turn. Stop
|
|
13
|
+
only for a decision: an authorization the envelope did not pre-approve, a terminal
|
|
14
|
+
verdict that hands back, or an ambiguous state nobody can describe. In pi session
|
|
15
|
+
`019faea6`, three of five human interventions were needless pauses after a watcher
|
|
16
|
+
had already reported success.
|
|
17
|
+
|
|
18
|
+
For a promotion PR or the metadata-sync PR opened by the verified-SHA flow, launch
|
|
19
|
+
[`watch-pr-delivery.mjs`](../../../cicd-pipeline/mergify/scripts/watch-pr-delivery.mjs)
|
|
20
|
+
from `<skillsRoot>/mergify/scripts/watch-pr-delivery.mjs`. It emits one terminal JSON
|
|
21
|
+
verdict on stdout, heartbeats on stderr, and bounds stalls rather than elapsed time.
|
|
22
|
+
It observes only: it never enqueues, merges, or takes merge authorization. Its
|
|
23
|
+
contract is [`watch-contract.md`](../../../cicd-pipeline/mergify/references/watch-contract.md).
|
|
24
|
+
Do not use the resolve-issues manifest-writing wrapper. Upstream PR delivery is a
|
|
25
|
+
finished input re-verified below, not re-watched; builds, deploys, and post-deploy
|
|
26
|
+
gates use their own watchers under the same one-launch rule.
|
|
27
|
+
|
|
28
|
+
### 0 — Intake: verify merge-readiness (principle 2)
|
|
29
|
+
Collect the PR set (from the resolve-issues handoff or as named) — for an **epic, that is one combined PR per component** (some may already be on `dev` if the upstream run merged them under `mergeShippable` — those need no re-collection, only re-verification that `dev`'s tip carries them); for a batch of independent issues, the N PRs. For each: fresh APPROVE on current HEAD, CI green on current HEAD, Review Contract present, no unresolved blocking findings, branch up to date with its base. For an epic's combined PR, also confirm its `integration-verdict.json` is `ALIVE` and its CI is green on the *current* HEAD (the combination was verified upstream — re-check it, don't inherit it). Anything failing re-check goes **back to `resolve-issues`** — this skill releases finished work; it does not finish it.
|
|
30
|
+
|
|
31
|
+
### 1 — Assemble the release (integration / release branch)
|
|
32
|
+
**N independently-implemented PRs become one combined, atomically-promotable release here** — the single combining point (principle 8). Five cases, and the last one is *not to run this step at all*:
|
|
33
|
+
|
|
34
|
+
- **Already combined** (a `resolve-issues` **epic** handoff): each component is one integration-verified PR whose branch *is* an assembled release slice. **Do not re-assemble** a component — re-verify its tip (principle 2). A single-component epic goes straight to step 2 with that branch; a multi-component epic assembles the components in dependency order (step 1), each already internally verified. (Where the integration branch auto-deploys, or a component was merged under `mergeShippable`, it lands on `dev` like any other and the next case applies.)
|
|
35
|
+
- **An integration branch exists** (the GitFlow `dev` the features targeted): merge any not-yet-integrated PRs into it in dependency order, then **pin the candidate** (principle 11) — a specific SHA, normally the post-merge tip once the integration environment's post-deploy smoke recorded **ALIVE for that exact SHA**, that **descends from the last release's back-merge commit**. Cut `release/<version>` at it and **record `release.candidateSha`** in the manifest; step 2 promotes that branch, not `dev`. A wanted SHA with no recorded verdict gets one deliberately (wait for the recorder, or deploy it to a no-traffic revision and smoke it) — never promote on "dev looked fine."
|
|
36
|
+
- **One branch does both jobs** (**GitHub Flow**: `main` is the integration branch *and* the production source): the PRs already merged to `main`, so cut `release/<version>` at a validated SHA on `main` and cut the version there (step 1b) — but understand what changes about the guarantee. The release branch **merges back into `main`**, and if `main` moved while you were releasing, principle 3 still applies — **rebase the release branch onto `main`'s tip and re-run step 2** rather than merge-resolving. The branch is one cut commit, so this is cheap. But rebasing means the **shipped SHA now carries commits that never earned a staging verdict**, so here the SHA-keyed verdict is a **pre-filter, not the release gate** — the gate is the step-4a candidate validation, which exercises the *exact shipped SHA* at 0% traffic before any user reaches it. Two more mechanical consequences: **merge before you tag** (`version.mjs` refuses a SHA that is not an ancestor of the production branch, so the rc cannot be minted on an unmerged release branch), and **there is no back-merge in step 5** — the cut commit reached `main` by being merged there. Trigger set: `cloud-build` → GitHub Flow variant. **This is the generic GitHub Flow path.** When pre-flight selected [`verified-sha-github-flow`](verified-sha-github-flow.md#candidate-selection-and-the-production-floor), that mode is authoritative: pin the eligible `main` SHA directly, never rebase it onto a moving tip, and follow its attempt and finalization order.
|
|
37
|
+
- **No integration branch at all** (PRs target prod directly, or are independent feature branches): **assemble `release/<slug>`** off the production base and merge the PRs into *it* in dependency order.
|
|
38
|
+
- **A merge queue already did it** (`deliveryMode: "queue"`, read from the handoff at [pre-flight item 2](preflight.md#pre-flight-gate-confirm-the-authorization-envelope-mandatory)): **do not assemble — there is nothing here to combine.** Every unit reached trunk through the queue with a `mergedSha` proved contained in the freshly-fetched `origin/<targetBranch>`, so the combining this step exists to perform already happened, upstream, one batch at a time. **Queue mechanics are `resolve-issues`' contract, not this skill's** — enqueue eligibility, the stall-bounded watch, the dequeue routing table and the rule that *a merge tool's return code, a queue acknowledgement and an auto-merge flag are all requests; only ancestry is a result* live in [references/delivery.md](../../resolve-issues/references/delivery.md) (`<skillsRoot>/resolve-issues/references/delivery.md`). Do not restate them here, and do not run them: by the time this lane starts, delivery is a finished fact and the only queue artifact it consumes is the merged SHA. What this step does instead is **select**, and the selection rule is the [eligible trunk SHA](verified-sha-github-flow.md#terms) — a commit that is on trunk *and* carries a green **Trunk Integration Verdict for that exact SHA**. Branch greenness is not eligibility, a verdict for a neighbouring SHA is not eligibility, and the current tip is not eligibility just for being the tip. **Record the selection as `release.candidateSha`** exactly as the integration-branch case does — it is the same field and the same job, and it is the only thing that tells a resumed session which of trunk's commits this release is about.
|
|
39
|
+
|
|
40
|
+
**The SHA the queue tested is, by construction, never the SHA that landed.** A queue validates a temporary **batch candidate** — this PR merged with the base, and usually with the batch's other PRs — and then the merge method writes a *different* commit onto trunk. The batch's green therefore describes a tree that exists at no ref you can release. **That costs this lane nothing, because it never inherits it**: eligibility is a verdict observed *after* the merge, on the commit that actually shipped, so the question "does the tested SHA's evidence transfer to the landed SHA?" is never asked. Naming the trade rather than assuming it away: preserving the tested SHA instead — fast-forward batching — buys evidence portability at the price of a repository-ruleset bypass, and the banked contract this rule comes from rejected that boundary deliberately. Fresh validation of what shipped is cheaper than a bypass, and it is the only reading under which a green batch is *not* a release-grade artifact. **A trunk commit with no post-merge verdict is ineligible however green its batch was** — get it one (wait for the recorder, or deploy it to a no-traffic revision and smoke it) exactly as the integration-branch case does; never promote on "the batch was green."
|
|
41
|
+
|
|
42
|
+
**Three principles change shape here, and none of them relaxes:**
|
|
43
|
+
- **[Principle 3](../SKILL.md#non-negotiable-principles) (promotions fast-forward)** — there is no promotion PR to fast-forward, because there is nothing to promote *from*. A promotion PR would itself be enqueued, and the queue would produce a *third* trunk commit — the pin destroyed by the act of promoting it. The rule's failure mode (a merge resolution into prod silently reverting hotfixes) cannot occur when this lane merges nothing; what survives is the ancestry the eligible-SHA selection already establishes.
|
|
44
|
+
- **[Principle 8](../SKILL.md#non-negotiable-principles) (combine into one promotion)** — one combined promotion becomes **one selected trunk SHA**. The guarantee is untouched, and this is exactly where it bites: a queue that landed six units is still released as *one* commit, one candidate, one rc. Releasing each merged unit separately would be the N-promotions failure with the assembly step removed.
|
|
45
|
+
- **[Principle 11](../SKILL.md#non-negotiable-principles) (a promotion promotes a pinned candidate SHA)** — the pin becomes a **trunk-integration verdict on an exact trunk SHA** rather than a release branch cut at a verdict-carrying integration tip. It does **not** demote to a pre-filter the way generic GitHub Flow's does: nothing is rebased, so the SHA that earned the verdict is the SHA that ships, and step 4a's candidate validation gates a commit the pin already covers. The GitFlow consequences are simply absent — no `dev`, so no step-5 back-merge and no descent constraint.
|
|
46
|
+
|
|
47
|
+
**And the version cut cannot follow the pin.** Step 1b commits the version identity onto an assembled branch *before* the promotion; with no branch and no promotion, a cut is a change to trunk, which is a queued PR, which is a new trunk SHA that is not the one selected. That is the ordering [`verified-sha-github-flow`](verified-sha-github-flow.md#final-claim-and-metadata-ordering) exists to solve, and this topology does **not** select it implicitly — pre-flight item 2 records whether the human did. Absent that selection, where the version identity lands is settled **before** the SHA is pinned, not discovered at step 1b.
|
|
48
|
+
|
|
49
|
+
Either way — except the merge-queue case, where this lane merges nothing and both bullets are answered upstream (see [row 2a](regression-checklist.md)):
|
|
50
|
+
- **Merge in dependency order, watching CI between merges** — incremental so a break is *attributed* to its merge. A textual conflict is resolved here; one needing a real code decision **bounces back to `resolve-issues`**.
|
|
51
|
+
- **The combined tree is a new artifact — verify it, don't inherit it (principle 2).** Each PR was reviewed individually; their union was not. Run **integration CI on the tip** *and* a **`review-pr` pass on the net combined diff vs the production base**, hunting cross-PR interactions no single review saw: a module two PRs both edited, conflicting symbols, migration ordering across PRs, a contract one PR changed and another consumed. A red build or blocking finding **stops the queue**.
|
|
52
|
+
|
|
53
|
+
**Bound the combined-review input, never the review.** Before the step-1 `review-pr` pass on a
|
|
54
|
+
large combined diff, fetch the named target branch and pin both sides of the range, then run
|
|
55
|
+
[`scripts/review-packet.mjs`](../scripts/review-packet.mjs):
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
git fetch --no-tags origin <target-branch>
|
|
59
|
+
target_ref=refs/remotes/origin/<target-branch>
|
|
60
|
+
target_head=$(git rev-parse --verify "$target_ref^{commit}")
|
|
61
|
+
head=$(git rev-parse --verify '<pinned-tip>^{commit}')
|
|
62
|
+
base=$(git merge-base "$target_head" "$head")
|
|
63
|
+
node <resolve-release>/scripts/review-packet.mjs collect \
|
|
64
|
+
--base "$base" --head "$head" --head-ref <release-ref> \
|
|
65
|
+
--target-ref "$target_ref" --target-head "$target_head" \
|
|
66
|
+
--target-repository github.com/<owner>/<repo> \
|
|
67
|
+
--evidence <per-pr-evidence.json> --ci <exact-head-ci.json>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The explicit target is part of the packet's authority, not a convenience flag. The command
|
|
71
|
+
requires a fully-qualified remote-tracking branch, binds it to the expected normalized repository
|
|
72
|
+
identity, rechecks target/head drift before output, computes the authoritative merge-base from the
|
|
73
|
+
pinned target head and exact review head, and requires `--base` to equal it. An older ancestor,
|
|
74
|
+
implicit `main`, symbolic remote `HEAD`, unavailable or stale target ref, or unrelated repository
|
|
75
|
+
fails closed before any stat, name-status, high-risk, per-PR or hunk evidence can be accepted.
|
|
76
|
+
|
|
77
|
+
The public CLI grammar is schema-defined. Every option accepts either `--key <value>` or
|
|
78
|
+
`--key=<value>`, but a separate token beginning with ASCII `-` is ambiguous and is never
|
|
79
|
+
consumed as data. Repository-relative evidence and CI filenames that legitimately begin with
|
|
80
|
+
`-` therefore use the equals form, for example `--evidence=-evidence.json` and
|
|
81
|
+
`--ci=-ci.json`. Those file operands may otherwise contain spaces or Unicode. Absolute paths,
|
|
82
|
+
Windows drive-absolute paths, traversal (`..`), dot or empty segments, and control characters
|
|
83
|
+
fail during argument validation, before either file is read or any Git command runs.
|
|
84
|
+
|
|
85
|
+
`--base`, `--head`, and `--target-head` are full 40-character lowercase commit SHAs.
|
|
86
|
+
`--target-ref` is a fully-qualified, explicit, non-symbolic `refs/remotes/<remote>/<branch>`;
|
|
87
|
+
`--target-repository` is normalized `host/owner/repository` identity. Optional `--head-ref` is
|
|
88
|
+
either a full lowercase SHA or an exact fully-qualified `refs/heads/...`, `refs/tags/...`, or
|
|
89
|
+
non-symbolic `refs/remotes/...` revision. Omitting it disables only named-head movement
|
|
90
|
+
detection; exact head, target repository/ref/SHA, merge-base, and exact-head CI checks remain.
|
|
91
|
+
`--max-bytes` is a decimal integer from 1 through 16,777,216. Unknown, duplicate, malformed,
|
|
92
|
+
or empty options, Unicode/single-dash option spellings, extra positionals, and unsupported
|
|
93
|
+
commands fail before I/O or Git; diagnostics identify the schema field without echoing supplied
|
|
94
|
+
command, option-name, positional, or value payloads. `--help` is generated from the same schema.
|
|
95
|
+
|
|
96
|
+
The JSON carries the exact range and target identity, short diff stat, name-status entries, every
|
|
97
|
+
high-risk path group (including rename/copy preimages), relevant constituent-PR evidence, and CI at
|
|
98
|
+
the exact head. It emits hunk coordinates rather than patch lines and records every omitted
|
|
99
|
+
ordinary name-status row under `truncation`; it fails if required metadata or any high-risk path
|
|
100
|
+
cannot fit the declared byte bound. Give that packet to the independent reviewer, who must inspect
|
|
101
|
+
the targeted hunks in the actual tree, check cross-PR interactions, and return one bounded complete
|
|
102
|
+
verdict. A packet is navigation evidence, not a replacement for source inspection and never
|
|
103
|
+
authorization to accept target or head drift.
|
|
104
|
+
|
|
105
|
+
The release branch's tip is the **single source** for the promotion in step 2. Two explicit exceptions, for the same reason: the opt-in `verified-sha-github-flow` mode, whose selected SHA is already on `main`, so [the pinned SHA itself is the source](verified-sha-github-flow.md#version-confirmation-and-the-immutable-pin), with no release branch or promotion merge — and the **merge-queue topology**, where the selected eligible trunk SHA is the source on the same terms.
|
|
106
|
+
|
|
107
|
+
### 1b — Cut the version and CHANGELOG (production promotions only)
|
|
108
|
+
**Skip this step for a `dev`/staging release** — it is unversioned, identified by its SHA (principle 10). Also skip it when [`verified-sha-github-flow`](verified-sha-github-flow.md#final-claim-and-metadata-ordering) is selected: changing CHANGELOG or package metadata before deployment would change the verified SHA, so that mode synchronizes metadata after the final release claim. `version.mjs` enforces the dev/staging case itself: `plan`, `changelog` and `tag` exit **3** ("not a production promotion"), a normal outcome to branch on rather than an error to work around. Exit 3 has that one meaning only ([exit codes](versioning.md#exit-codes)).
|
|
109
|
+
|
|
110
|
+
Commit the version identity **onto the assembled release branch, before opening the promotion PR** — never onto the production branch afterward ([principle 3](../SKILL.md#non-negotiable-principles): a post-merge commit on `main` leaves the base ahead of the head, breaking the *next* promotion's fast-forward). Cutting here also makes the shipped SHA the exact SHA whose CHANGELOG says it shipped.
|
|
111
|
+
|
|
112
|
+
Inspect a machine-readable plan before writing it; use `--format markdown` instead when the review artifact should be the complete planned file:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
node <resolve-release>/scripts/version.mjs changelog \
|
|
116
|
+
--version <confirmed> --date <YYYY-MM-DD> --changes <prs.json> \
|
|
117
|
+
--repo <owner/name> --target-branch <target> --production-branch <prod> \
|
|
118
|
+
--dry-run --format json > changelog-plan.json
|
|
119
|
+
node -e 'const p=require("./changelog-plan.json"); if (p.noop) console.error("CHANGELOG already cut")'
|
|
120
|
+
|
|
121
|
+
node <resolve-release>/scripts/version.mjs changelog \
|
|
122
|
+
--version <confirmed> --date <YYYY-MM-DD> --changes <prs.json> \
|
|
123
|
+
--repo <owner/name> --target-branch <target> --production-branch <prod>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The cut **carries the `## [Unreleased]` body into the new version heading**, merged with entries derived from the PRs' titles, leaving `Unreleased` empty for the next cycle — so hand-written notes no PR title captured survive. In the *same commit*, bump any file carrying a version (`package.json`, `pyproject.toml`, `VERSION`, chart/image tags). Do not tag here; that is step 3 or step 5, per the confirmed mode.
|
|
127
|
+
|
|
128
|
+
**Record the cut commit** in the run manifest (`release.cutCommit`) as soon as it lands — the `step` enum has no value between `merge` and `promote`, so `cutCommit` is what tells a resumed session that 1b is done, and a non-null `cutCommit` means **skip this step**. It must be recorded, not re-derived: re-running `changelog` is a harmless no-op (exit 0), but the hand-applied version-file bumps beside it have no such guard — a resume that repeats them double-bumps.
|
|
129
|
+
|
|
130
|
+
### 2 — Promotion PR (skill: review-pr, promotion mode)
|
|
131
|
+
Open (or update) the promotion PR from the assembled release branch into the production branch. **Skip this generic promotion-PR step in [`verified-sha-github-flow`](verified-sha-github-flow.md#candidate-selection-and-the-production-floor): the selected SHA is already an eligible commit on `main`, and its ancestry, version confirmation, and single-flight gates authorize the attempt tag.** **Skip it under the merge-queue topology too, for the reason step 1 gives — a promotion PR would be enqueued and would land a different SHA.** What is *not* skipped there is the release-readiness checklist: run it against the selected trunk SHA, with the fast-forward row recorded as `n/a — no promotion merge under a merge queue` rather than left blank. A blank row is not a cheaper `n/a`; it withholds the green the `auto-when-green` gate reads. Otherwise, this is the highest-stakes review in the pipeline — it gates production — so run the real skill, not a remembered checklist: read `<skillsRoot>/review-pr/SKILL.md` (the **absolute** skills directory this skill was loaded from — `<repo>/skills/engineering` in the skills repo, the resolved `~/.claude/skills` when globally installed; a relative `skills/engineering/...` path silently fails in a consumer repo) and apply its [Promotion PRs](../../review-pr/SKILL.md#promotion-prs-eg-dev--main) mode. A from-memory "fast-forward plus some release checks" silently drops items that mode enforces.
|
|
132
|
+
|
|
133
|
+
Concretely: **`fastForwardable` must be true** (else back-merge first, principle 3), then the **release-readiness checklist** — migrations/backfills ordered and reversible, secrets/config present in prod, flag defaults sane, version/changelog (the step-1b cut commit is that row's evidence; on a dev release the row is `n/a — unversioned`), rollback plan, and **candidate validation** — green CI **and** the recorded ALIVE smoke verdict on the exact promoted SHA (principle 11; SHA-keyed — a verdict from any other SHA is not evidence). This is release-readiness, **not** a re-review of the code: each PR was reviewed individually and the integration in step 1.
|
|
134
|
+
|
|
135
|
+
**Proof-of-execution:** record the `fastForwardable` boolean and the completed checklist — every row decided, none blank — as the promotion PR's verdict artifact. A prose "looks good to promote" is not it. These are exactly the inputs the `auto-when-green` gate ([principle 9](../SKILL.md#non-negotiable-principles)) reads, so a blank row doesn't merely leave the checklist incomplete: it withholds the green that would let the promotion run unattended.
|
|
136
|
+
|
|
137
|
+
**A row nobody could decide is not a decided row** ([principle 13](../SKILL.md#non-negotiable-principles)). The check whose secret was missing, whose staging service was down, whose migration dry-run would not start — that row reads exactly like a blank one here, and for the same reason: no result was produced, so no green was earned. It is emphatically **not** the `n/a` shape, which says *this row does not apply under this topology* and carries a checkable reason; a row that applies and could not be decided says *the release is not ready to be judged*. Do not resolve it by widening the `n/a`. Restore the check's ability to produce a result and re-run it, or the promotion stops here — while the merge lane that carries the repair keeps running.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# `auto-when-green` — the conditional promotion envelope
|
|
2
|
+
|
|
3
|
+
The `auto-when-green` mode (selected by the human at the [pre-flight gate](preflight.md#pre-flight-gate-confirm-the-authorization-envelope-mandatory), item 3) lets the promotion run **unattended** — but only behind a real gate, never on CI color alone. It shifts the human's authorization from *per-promotion* to *the policy confirmed at pre-flight*; it makes the gate **mechanical**, it never removes it. "CI was green, ship it" without the conditions below is the exact failure this mode exists to forbid.
|
|
4
|
+
|
|
5
|
+
**The gate is a script, not a reading.** Each condition below is decidable from an artifact the release lane already records, and an agent evaluating them from prose — at an unattended production traffic shift with no human present — is the worst possible place for a judgment call. Run [`scripts/green-gate.mjs`](../scripts/green-gate.mjs); it compares each condition against its artifact, **fails closed** on any missing or malformed input, and names the specific failing condition. `green-gate.mjs --help` is the authoritative synopsis. This doc is the **doctrine behind each condition** — the judgment `--help` does not encode — not a second copy of the checks.
|
|
6
|
+
|
|
7
|
+
## Two authorization points, two subcommands
|
|
8
|
+
|
|
9
|
+
Under [principle 12](../SKILL.md#non-negotiable-principles) the release crosses two outward-facing gates, and `auto-when-green` gates both — so the script has two subcommands, run at the two points:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
green-gate.mjs promote --manifest <run-manifest.json>
|
|
13
|
+
green-gate.mjs shift --manifest <run-manifest.json> --verdict <smoke-verdict.json>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Each exits **0 = fire** (proceed unattended), **1 = hold** (fall back to a per-step human confirm, with the failing condition named), **2 = usage / unreadable-input error**. On any `hold`, **fall back to the human** — that is the whole safety property; do not route around it.
|
|
17
|
+
|
|
18
|
+
Selecting `auto-when-green` covers both points. Choosing it for the promotion but wanting a human on the shift is a legitimate configuration — say so explicitly at pre-flight item 3, because the default reading is that it covers both, and run only `promote` unattended.
|
|
19
|
+
|
|
20
|
+
## The promotion gate (`green-gate.mjs promote`) — all of (a)–(d) must hold
|
|
21
|
+
|
|
22
|
+
The promotion + rc mint (step 3) fires automatically **only while every one** of these holds. `green-gate.mjs --help` names the manifest field for (a) and describes (b)–(d) in prose (it names a field again only for the carve-out major bump); the fields those prose conditions read live in the bullets below, alongside the doctrine `--help` doesn't carry — why each condition is what it is:
|
|
23
|
+
|
|
24
|
+
- **(a)/(b) — liveness on the *exact* promoted SHA, not CI color.** Fast-forwardability (`release.fastForwardable === true`, else back-merge first, principle 3), and green gates — PR-lane, integration CI, and (where the integration environment records one, principle 11) the SHA-keyed smoke verdict (`release.integrationSmoke`, keyed to `release.candidateSha`) — all keyed to the promoted SHA. A verdict from any other SHA, or a missing one, is **not** green: "CI passed" on a different tree is precisely the trap unattended promotion must reject. An environment with **no** such recorder declares that explicitly — `release.integrationSmoke: "not-recorded"` — rather than leaving it inferable from a blank field.
|
|
25
|
+
- **(c) — a collision is a human's call, never an auto-bump.** Release-readiness fully decided — every row PASS with a locator, **no blank/TODO row**, `coverage-checklist.mjs validate`'s bar (a row that needs human judgment is, by definition, not green) — including the [step-1b version/changelog cut](assembly.md#1b--cut-the-version-and-changelog-production-promotions-only) committed (`release.cutCommit`) with **no collision on the final tag** (`release.versionPlan.collision === false`; an existing `-rc.*` tag is not a collision). An unresolved collision is a human decision, never an automatic bump past it.
|
|
26
|
+
On HOLD, `green-gate.mjs promote` reports **every** failing readiness row in one
|
|
27
|
+
`diagnostics.readiness` array with row/index, evidence value (secret-shaped values redacted),
|
|
28
|
+
- **(d) — artifact-checked, not re-asserted.** The integration-review's blocking-finding count is read from the manifest (`release.blockingFindings === 0`), so the gate never re-litigates the review's verdict at fire time.
|
|
29
|
+
|
|
30
|
+
A blank checklist row is not merely incomplete: it withholds the green that would let the promotion proceed unattended. **So does a row that could not be decided** — the check whose credential was missing, whose service was unavailable, whose fixture had drifted ([principle 13](../SKILL.md#non-negotiable-principles): an applicable gate that cannot run is a failure, not an absence). It reaches the gate as the same withheld green a blank row does, and it must: the gate has no way to tell "nobody filled this in" from "nobody could", and neither one is a result. The condition is over **decided** rows — PASS with a checkable locator, or the `n/a` shape a topology's inapplicability earns — and an undecidable row is neither, however good the locator it cites for the attempt.
|
|
31
|
+
|
|
32
|
+
### Carve-outs that force the human even when (a)–(d) hold
|
|
33
|
+
|
|
34
|
+
`promote` also refuses to auto-fire — even on a full green — when any of these mechanically-checkable overrides trips:
|
|
35
|
+
|
|
36
|
+
- **Major version bump** — a major bump is a breaking change reaching consumers who already pinned the version, irreversible for them whatever the rollback does. `version.mjs plan` names the bump (`release.versionPlan.bump === "major"`), so this is checkable, not a judgment call.
|
|
37
|
+
- **Rollback not armed** — the policy must be **auto-rollback, not page-first** (`envelope.rollbackPolicy`), and the candidate revision must be deletable without confirmation (`envelope.candidateDeletableWithoutConfirm`). The validate→expose→verify→rollback loop (steps 4–5) is the only thing that makes an unattended promotion safe, so without it `auto-when-green` is **not available**.
|
|
38
|
+
- **Irreversible content** — a destructive or non-reversible migration/backfill, a secret/config change that cannot be verified before deploy, or a service's first-ever deploy each override the automatic path. The gate requires a positive `envelope.reversibilityConfirmed` to clear this carve-out and holds on its absence (the safe default: force the human), rather than silently passing an unverifiable claim.
|
|
39
|
+
|
|
40
|
+
## The traffic-shift gate (`green-gate.mjs shift`) — condition (e)
|
|
41
|
+
|
|
42
|
+
The traffic shift (step 4b) is the moment users are exposed. It is separately authorized (principle 1), and under this mode it fires automatically on one further condition:
|
|
43
|
+
|
|
44
|
+
- **(e) the candidate's step-4a verdict is ALIVE on that exact candidate** — produced by a smoke run under the read-only `prod-preview` profile against the candidate URL (the verdict's `noMutations` flag is derived from that read-only profile), **and still fresh**.
|
|
45
|
+
|
|
46
|
+
**(e) must be checked against the artifact, not asserted** — this is the crux the whole gate exists for. `smoke-verdict.json` records the `target` it ran against and whether `noMutations` was set, so the gate is: `verdict === "ALIVE"` **and** `target === candidateUrl` **and** `noMutations === true`. Without comparing the recorded target against `release.candidateUrl`, "on that exact candidate" is a claim the agent makes about its own prior work — an unfalsifiable input gating the one production traffic shift that happens with no human present, which is the worst possible place for one. A **DEAD**, **missing**, or wrong-`target` verdict is not green, and an **EMPTY** verdict is specifically not green: it means the run selected zero paths and proved nothing, which is the failure mode most likely to look like success. A **CANNOT-RUN** or **BLOCKED** verdict is also not green: it means the gate itself could not run (e.g. a secret env var held the secret *name* string instead of the secret *value*, so every request got 401/403) — the gate, not the candidate, is the defect. [Step 4a](exposure.md#4--validate-the-candidate-then-expose-it-skill-smoke) routes that case as its own state (the **gate** is the defect there, not the candidate, so nothing about it licenses deleting a revision); what this gate contributes is only that it never reads a missing answer as a satisfied one.
|
|
47
|
+
|
|
48
|
+
**Evidence expires — the target comparison bounds *which*, not *when*.** A verdict names the candidate it validated; it says nothing about how long ago. That gap matters because step 4b's shape is validate-at-0% → **wait for an authorization** → shift, and nothing bounds the wait: a human confirm can arrive hours later, and an unattended gate fires whenever it is evaluated. Meanwhile the coordinates drift — a newer build takes the `candidate` tag, the incumbent stops holding all the traffic, a second release starts — and an old ALIVE describes a world that no longer exists while looking exactly like a current one. So the envelope declares a **freshness window** (`envelope.verdictFreshnessMinutes`, pre-flight item 3; conservative default in `green-gate.mjs --help`) and the gate compares the verdict's recorded generation time against it.
|
|
49
|
+
|
|
50
|
+
Two properties are deliberate. First, **absence is not freshness**: a verdict carrying no timestamp cannot be aged, so it can never be shown to be current, and the gate holds rather than assuming the best — the same fail-closed reading every other condition takes on a missing input. Second, **expiry routes backward, not forward**: a stale verdict is not a failing candidate and must not be read as one. The candidate is usually fine; what aged out is the *evidence*. The response is to re-run step 4a and mint a current verdict — one smoke run — never to shift on the old one and never to delete the candidate. The whole point of the window is that re-validating is cheap and exposure on unre-checked coordinates is not.
|
|
51
|
+
|
|
52
|
+
The gate is only half of it: freshness is one of three assertions in step 4b's **post-authorization re-check fence**, alongside candidate identity and the live traffic split, which the gate cannot see from artifacts. See [SKILL.md step 4b](exposure.md#4--validate-the-candidate-then-expose-it-skill-smoke).
|
|
53
|
+
|
|
54
|
+
## Where each condition reads from
|
|
55
|
+
|
|
56
|
+
The script's inputs are exactly the artifacts the release lane already records — the run manifest (`envelope` + `release`, via `resolve-issues`'s [`run-state.mjs`](../../resolve-issues/references/run-state.md)), the recorded `version.mjs plan` output, and `smoke-verdict.json` from the `smoke` skill. Run `green-gate.mjs --help` for (a)'s field, the carve-out bump, and the full exit-code contract — the per-condition fields for (b)–(d) are named in the bullets above; the script is the source of truth, this doc is the why.
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Step 3: promote, then build the candidate (no exposure yet)
|
|
2
|
+
|
|
3
|
+
The promotion lands and the pipeline builds a candidate revision at **0% traffic**.
|
|
4
|
+
Still nothing exposed. Previous: [assembly.md](assembly.md) · next:
|
|
5
|
+
[exposure.md](exposure.md).
|
|
6
|
+
|
|
7
|
+
### 3 — Promote, then build the candidate (no exposure yet)
|
|
8
|
+
In [`verified-sha-github-flow`](verified-sha-github-flow.md#version-confirmation-and-the-immutable-pin), skip the generic merge below: create the next immutable rc tag directly on the selected eligible `main` SHA only after its human-confirmed SemVer and single-flight proof. **Under the merge-queue topology, skip it too and for the same reason** — the units are already on trunk, so `SHIPPED` is the eligible trunk SHA step 1 selected, not a post-merge tip, and the rc tag is minted directly on it. The authorization is unchanged: no merge to confirm does not mean no promotion to authorize, since minting the rc is what fires the production build. Otherwise, on the promotion authorization — the human's per-step confirm, or, under `auto-when-green`, automatically once the [principle 9](../SKILL.md#non-negotiable-principles) gate holds against step 2's artifacts (any miss or irreversible-content carve-out falls back to a confirm) — **take the lease first, then merge the promotion as a fast-forward, mechanically.**
|
|
9
|
+
|
|
10
|
+
**Open the Release Attempt Lease on that authorization — before anything else in this step** ([principle 7](../SKILL.md#non-negotiable-principles)). The authorization is the moment a release becomes in-flight, and it is the one moment **every trigger shape** shares, so the lease opens here on all four rather than at an rc mint three of them never perform:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
node <resolve-issues>/scripts/run-state.mjs check-release-lease <run-slug> # 0 free · 1 held · 2 ambiguous · 3 no-manifest
|
|
14
|
+
# on free — mint the attempt id, then open the attempt under it. No rc tag exists yet on ANY
|
|
15
|
+
# shape; the mint records one later.
|
|
16
|
+
ATTEMPT_ID=$(node <resolve-release>/scripts/evidence-bundle.mjs next-attempt-id \
|
|
17
|
+
--version <version> --dir <durable-evidence-dir>) # -> v0.5.0-attempt.1
|
|
18
|
+
node <resolve-issues>/scripts/run-state.mjs merge <run-slug> \
|
|
19
|
+
"{\"step\":\"promote\",\"release\":{\"attempt\":{\"id\":\"$ATTEMPT_ID\",\"state\":\"open\"}}}"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Only **exit 0 (free)** authorizes the promotion. **Held, ambiguous, and no-manifest all refuse and fail closed** — never wait, never supersede, never delete or move another attempt's tag. A held lease means a prior attempt is `open` or `blocked-manual`: resolve it (verify, roll back, revoke, or clear the block) before starting another release. Ambiguous or absent means the state cannot be read (a candidate on record with no attempt, a `step` past the mint with no attempt, a reaped manifest): **re-establish the real state from artifacts** — the rc tags on the remote, the live traffic split — before proceeding, rather than reading absence as freedom.
|
|
23
|
+
|
|
24
|
+
The **attempt id** is *this attempt's* identity, not the release's — **`<version>-attempt.<n>`**, on all four trigger shapes. It keys the durable evidence bundle at step 5, so it is chosen once, here, and never rewritten; a generic writer that tries to re-key, erase, or **reopen a spent id** is refused.
|
|
25
|
+
|
|
26
|
+
**Why the ordinal, and why it is minted rather than typed.** The version alone is *not* unique per attempt: a retry deliberately re-cuts the same version, and both failure tiers reuse the same terminal state — so attempt 1 and its retry would key the same bundle, and immutability (working exactly as designed) would refuse the second, **losing that failure's durable record**. `next-attempt-id` reads the ordinal off the bundles already written, and the id it returns is legal by construction rather than by your remembering the rules. Composing one by hand still works, and `run-state.mjs` refuses it if it cannot key a file (`v2.0.0#2` cannot) or if it reuses a closed attempt's id — but the mint is the path this step points at.
|
|
27
|
+
|
|
28
|
+
**What the mint guarantees, and what guards the rest.** A bundle exists only after a *terminal exit*, so reading the durable dir makes the minted id free of collision with every attempt that has **closed** — and only that. Two routes are outside it, and both are covered above rather than by the mint: an attempt still **in flight** has emitted no bundle and would not shift the ordinal, which is exactly why the lease check comes **first** and why only `free` authorizes the mint (in-flight reads `held`, a reaped manifest reads `unknown`); and two sessions minting **concurrently** before either emits is the advisory-not-atomic gap tracked in **#509**. The order in the block above is therefore load-bearing, not stylistic — minting before the lease answers `free` reintroduces the collision the ordinal exists to prevent. The mint also **fails closed on a directory it cannot read** (exit 2, nothing on stdout): only a dir that does not exist yet means "no attempt has emitted", because the first emit creates it. An unreadable one — a permissions change, a `--dir` typo landing on a file — is *reading absence as freedom*, the same error this step refuses two paragraphs down, and it would hand back an ordinal that is already spent.
|
|
29
|
+
|
|
30
|
+
**Not the rc tag, on any shape** — including the rc shape, which is what pre-#599 releases used. The lease now opens at the authorization, *before* the mint, so there is no rc tag in existence at the moment this id is written. Bundles written under the old convention are untouched: they key off their `rcTag` and keep their exact filenames.
|
|
31
|
+
|
|
32
|
+
**The accepted cost of opening here, stated so it is not later discovered as a bug:** a session that **crashes between the authorization and the mint** now leaves the lease reading **held**, not free. That is the correct reading — an authorized-but-unfinished promotion *is* in flight — and it is what a resumed session should see. Resolve it the way any held lease is resolved.
|
|
33
|
+
|
|
34
|
+
**This check is advisory, not atomic** — it is a check-then-act read, and making the take safe under two truly concurrent releases is tracked in **#509**; here single-flight rests on this precondition plus principle 8's one-combined-promotion rule.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
git checkout <prod> && git merge --ff-only <release-branch> && git push origin <prod>
|
|
38
|
+
SHIPPED=$(git rev-parse <prod>) # the post-merge tip — this is <shipped> everywhere below
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`--ff-only` is the point, not a preference. `gh pr merge --merge` and the green button both create a **merge commit**, and every downstream guarantee is written assuming they do not: the shipped SHA would stop being `release.candidateSha`, the image would build from a commit that never carried a verdict, and step 2's recorded `fastForwardable: true` would describe a merge that was not performed as one. All three fail silently. **Assert `SHIPPED == release.candidateSha`** wherever the promotion was expected to fast-forward (both GitFlow and, after the rebase, GitHub Flow) — if they differ, a merge commit was created and the release must stop here.
|
|
42
|
+
|
|
43
|
+
Merging is not deploying: under [principle 12](../SKILL.md#non-negotiable-principles) the production pipeline fires on an rc tag, not on the push to `<prod>`.
|
|
44
|
+
|
|
45
|
+
**Prove trigger-owned topology before the RC circuit breaker and mint.** The production build may be a Cloud Build `TAG_NAME` trigger, while a controller-submitted `gcloud builds submit` is only an alternate path. Run the preflight probe's repository-specific, read-only topology command and persist its complete JSON output:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
node <resolve-release>/scripts/preflight-probes.mjs check \
|
|
49
|
+
--repo <owner/name> --target-branch <prod-branch> \
|
|
50
|
+
--exposure-gate-probe '<repo exposure capability check>' \
|
|
51
|
+
--topology-probe '<read trigger config and emit the representative RC substitution record>' \
|
|
52
|
+
--json > preflight-probes.json
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The `rc-tag-topology` result must be ready and record `prodBuildTrigger: "tag-trigger"`, `versionSource: "TAG_NAME"`, and `candidateTagSource: "TAG_NAME"`; its representative substitution record proves `TAG_NAME -> _DEPLOYED_VERSION` (final SemVer) and `TAG_NAME -> _CANDIDATE_TAG` (the RC candidate tag). `version.mjs rc --topology-probe preflight-probes.json` rechecks this artifact at the mutation boundary and refuses before creating or pushing any RC otherwise. Do not substitute a controller-provided `_DEPLOYED_VERSION`: it proves only the controller path, not the production trigger. A dev-published source-SHA image is informational, **not** RC candidate evidence; candidate evidence requires the immutable RC tag, the build it triggered, and the candidate revision/tag binding for that RC.
|
|
56
|
+
|
|
57
|
+
**Run the rc circuit breaker before the mint — before EVERY immutable rc, including a `--retry-from` retry** ([principle 15](../SKILL.md#non-negotiable-principles); #962). The mint fires a production build, and a version whose prior attempts kept failing under the lane's own tooling is the loop that minted Forex v0.1.12 nine RCs: each attempt's fail-closed behavior was correct, and nothing stopped the loop. The breaker reads the durable evidence bundles for the proposed version, counts prior **non-released** attempts whose normalized failure owner is `release-tooling` or `gate-environment` (#961's taxonomy, reused — never re-derive it by hand), and holds when the count reaches the threshold:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
node <resolve-release>/scripts/rc-circuit-breaker.mjs check \
|
|
61
|
+
--dir <durable-evidence-dir> --version <v> > rc-breaker.json
|
|
62
|
+
# exit 0 proceed / authorized-continue · 1 hold-for-release-design-review · 2 cannot evaluate
|
|
63
|
+
node <resolve-issues>/scripts/run-state.mjs merge <run-slug> \
|
|
64
|
+
"{\"release\":{\"circuitBreaker\":<the decision JSON from rc-breaker.json>}}"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The decision JSON is recorded at `release.circuitBreaker` on EVERY outcome — proceed included — because the post-mortem reports whether the breaker fired and how it was resolved, and an unrecorded check is a check nobody can prove ran. The threshold defaults to **2** and is configurable (`--threshold`, or `$RC_CIRCUIT_BREAKER_THRESHOLD`; flag beats env beats default, and the decision records the source). Application, configuration and platform failures are counted and reported but never consume this threshold — the breaker exists for the lane's own machinery, and an artifact defect already has its own route. The check is **read-only**: a hold deletes nothing, moves no immutable rc tag, and burns no version. **Exit 2 is a refusal, not a proceed** — an unreadable durable dir or an unparseable bundle means the history cannot be established, and a gate that cannot read the history does not get to mint.
|
|
68
|
+
|
|
69
|
+
**On `hold-for-release-design-review`, stop for a human.** Continuing is an explicit, recorded authorization — a structured JSON record (`version`, `againstCount`, `authorizedBy`, `authorizedAt`, `rationale` naming why another RC is preferable to simplifying, removing, or externally repairing the gate; schema in [references/postmortem.md](postmortem.md)) — passed via `--authorization <file>`. The record binds to the current count: one authorization covers exactly one continuation, and a further qualifying failure re-holds. There is deliberately **no env var or flag that skips the check** — a silent bypass is the failure this gate exists to end.
|
|
70
|
+
|
|
71
|
+
**Mint the rc tag on the merged promotion SHA** — this is what triggers the build, on the `rc-tag` shape only. (**Skip the mint on the other three shapes**, pre-flight item 5: under `branch-push` the merge you just did is the trigger, so go straight to watching the build; under `final-tag` the version tag at step 5 is; under `no-deploy` there is no build to fire and the rest of this step is a decided `n/a`. The rc exists only to give a tag-triggered pipeline something to fire on that is not the version tag.) **The mint records its tag onto the attempt that is already open** — it does not open one, and skipping it never leaves the lease untaken:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
RC_TAG=$(node <resolve-release>/scripts/version.mjs rc --version <v> --sha <shipped> \
|
|
75
|
+
--target-branch <prod> --production-branch <prod> --topology-probe preflight-probes.json --push)
|
|
76
|
+
node <resolve-issues>/scripts/run-state.mjs merge <run-slug> \
|
|
77
|
+
"{\"release\":{\"attempt\":{\"rcTag\":\"$RC_TAG\"}}}" # onto the OPEN attempt
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
It prints the tag it minted (`v<v>-rc.1`, then `-rc.2` on a later attempt at a different SHA). Re-running it on the **same** SHA is a resume-safe no-op that prints the existing tag rather than firing a second build — so a resumed session cannot spam rc tags. A stalled build is re-run from the platform, not by minting another tag. Recording the tag is idempotent for the same reason; **moving** it is refused (principle 10 — an attempt records its rc tag once).
|
|
81
|
+
|
|
82
|
+
**Watch candidate delivery with one bounded launch**, keyed by the immutable RC tag and selected SHA:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
node <resolve-release>/scripts/watch-candidate-delivery.mjs \
|
|
86
|
+
--rc-tag <rc-tag> --sha "$SHIPPED" --service <svc> --region <r>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The watcher writes one heartbeat per poll to stderr and exactly one terminal structured verdict to stdout. Its progress and terminal states are `not-triggered`, `building`, `ready-at-0`, `failed`, `timed-out`, and `ambiguous`. Build receipts, when readable, supply progress and classified build failure, but they are optional evidence: Cloud Run is authoritative for success when it exposes exactly one candidate keyed to the selected SHA at 0%, reports that revision Ready, and its health endpoint attests the SHA/revision/digest tuple. Therefore a candidate already Ready terminates promptly even when the build-list response omits substitutions or other filtered fields; the watcher never uses unsupported server-side fields to decide identity.
|
|
90
|
+
|
|
91
|
+
This watcher **observes only**. It never mints or moves an RC tag, deploys or deletes a revision, shifts traffic, writes the run manifest, or closes the Release Attempt Lease. A `failed`, `timed-out`, or `ambiguous` verdict is evidence for the main loop to route and record; it is not authorization for the watcher to recover or finalize anything. On `ready-at-0`, record its candidate identity and continue immediately to validation. Mechanics remain the platform skill's (`cloud-build` → Post-deploy smoke gate), while this step confirms the contracted output: a candidate revision at **0% traffic** under its **SHA-keyed tag**, with its **revision name** and **candidate URL**. A build that reports success while traffic already moved is a **pipeline defect**, not a release to continue.
|
|
92
|
+
|
|
93
|
+
**A terminal pre-candidate failure closes through one command, before the candidate lane returns.** When the delivery evidence proves this attempt produced no candidate revision (for example, a failed RC-triggered build), construct the failed-pre-promotion bundle with its attempt id, SHA, build identity and verified disposition, then run:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
node <resolve-release>/scripts/close-attempt.mjs <run-manifest.json> \
|
|
97
|
+
--bundle release-evidence.json --dir <durable-evidence-dir> \
|
|
98
|
+
--state failed-pre-promotion \
|
|
99
|
+
--evidence "build failed before candidate creation; incumbent remained at 100%"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`close-attempt.mjs` validates the bundle and release-attempt transition, emits the top-level immutable bundle, and only then writes the terminal close. The emitted record stamps `failurePhase: "pre-candidate"`, so `version-postmortem.mjs` and `rc-circuit-breaker.mjs` discover it without a manual follow-up emit. A `timed-out` or `ambiguous` verdict is not this path until the main loop has proved no candidate exists; unknown platform state continues to hold the lease.
|
|
103
|
+
|
|
104
|
+
**Take delivery with a comparison, not a lookup — discovery fails closed (principle 12).** Three reads in order, all read-only against the candidate (principle 4: it is wired to the real production database, so nothing here may be anything but a GET), and the middle one is yours because a platform read is the release lane's job, not a script's:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# Prefer JSON: Cloud Run omits percent on zero-traffic tagged rows. The shared parser reconstructs every
|
|
108
|
+
# omitted row as 0 only when the complete observation has valid explicit percentages totaling
|
|
109
|
+
# 100%; partial, overfull, invalid, or otherwise ambiguous observations still refuse.
|
|
110
|
+
gcloud run services describe <svc> --region <r> --format=json > candidate-service.json
|
|
111
|
+
node <resolve-release>/scripts/candidate-identity.mjs locate \
|
|
112
|
+
--sha $SHIPPED --traffic candidate-service.json \
|
|
113
|
+
[--candidate-tag-prefix <repo-prefix>] > candidate-locate.json # exit 1 = REFUSED, stop
|
|
114
|
+
CAND_URL=$(node -p 'JSON.parse(require("fs").readFileSync("candidate-locate.json","utf8")).evidence.candidate.url')
|
|
115
|
+
curl -fsS "$CAND_URL/health" > candidate-health.json
|
|
116
|
+
node <resolve-release>/scripts/candidate-identity.mjs attest \
|
|
117
|
+
--sha $SHIPPED --locate candidate-locate.json --health candidate-health.json \
|
|
118
|
+
> candidate-identity.json # exit 1 = REFUSED, stop
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Where pre-flight item 3 declared no attesting endpoint**, the third read is the candidate revision's *configured* image digest instead of its health tuple, and it is passed as the declaration's substitute — `attest` refuses with `e:no-fallback-digest` without it, because that digest is the entire remaining check at step 4b:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
CAND_DIGEST=$(gcloud run revisions describe <candidate-revision> --region <r> \
|
|
125
|
+
--format='value(status.imageDigest)')
|
|
126
|
+
node <resolve-release>/scripts/candidate-identity.mjs attest \
|
|
127
|
+
--sha $SHIPPED --locate candidate-locate.json \
|
|
128
|
+
--declare-no-attestation "<reason from pre-flight item 3>" \
|
|
129
|
+
--candidate-image-digest "$CAND_DIGEST" > candidate-identity.json # exit 1 = REFUSED, stop
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`--candidate-tag-prefix` defaults to `candidate-`. Supply it when the repository's reviewed
|
|
133
|
+
pipeline contract uses another SHA-keyed namespace such as `a-`; the accepted locate artifact
|
|
134
|
+
records the prefix, and `attest` plus post-shift `serving` revalidate the tag against that recorded
|
|
135
|
+
namespace. It changes only the namespace, never the exact-SHA, single-entry, 0%-traffic, or
|
|
136
|
+
attestation requirements.
|
|
137
|
+
|
|
138
|
+
A refusal is the release **stopping**, not a diagnostic to weigh: `e:candidate-absent` (the pipeline did not publish this SHA's candidate), `e:ambiguous-candidate`, `e:incomplete-traffic-entry` (revision, URL and percent would have come from different deployments), `e:candidate-traffic-moved` (the delivery above, mechanized — the candidate is not at 0%, so the pipeline shifted traffic and this is a pipeline defect, not a release to continue), `e:sha-mismatch` / `e:revision-mismatch` (the running process is not what the coordinates claim), `e:locate-sha-mismatch` (the `locate` artifact you passed describes a **different commit** — a stale `candidate-locate.json` from an earlier rc, which a resumed session is exactly how you get), and **`e:second-candidate-in-flight`** — the case that used to be a warning to notice. That last one *is* principle 7 violated, and it is now the script's exit code rather than the agent's recollection: one of the two candidates was about to be validated under the other's identity. Route it like any other pre-exposure stop — nothing was exposed, nothing needs deleting yet, and the second release is resolved before this one resumes. `--help` names every id and its route (`candidate-identity.mjs --help`); the declared-weakening flags come from [pre-flight item 3](preflight.md#pre-flight-gate-confirm-the-authorization-envelope-mandatory).
|
|
139
|
+
|
|
140
|
+
**Pre-locate hygiene (#819).** Before running `locate`, clean traffic tags left by previous releases. A successful promotion can leave a `<repo-prefix><sha>` tag (by default `candidate-<sha>`) on the revision that is now serving 100% (the incumbent), or an orphan 0% revision from a release that already closed. Those tags are not a true second release, but they still make `locate` refuse. Run the hygiene check against the same traffic capture and pass the repository's reviewed prefix to both hygiene and `locate`:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
node <resolve-issues>/scripts/run-state.mjs check-release-lease-scoped \
|
|
144
|
+
<run-slug> --repo <owner/name> [--service <svc>] > product-leases.json
|
|
145
|
+
node <resolve-release>/scripts/candidate-hygiene.mjs locate \
|
|
146
|
+
--sha $SHIPPED --traffic candidate-service.json \
|
|
147
|
+
--lease-report product-leases.json \
|
|
148
|
+
--terminal-evidence prior-release-evidence.json \
|
|
149
|
+
--candidate-tag-prefix <repo-prefix> > candidate-hygiene.json
|
|
150
|
+
# If decision is "clean", run the emitted gcloud command and re-capture traffic before locate:
|
|
151
|
+
gcloud run services update-traffic <svc> --region <r> --remove-tags <stale-tags>
|
|
152
|
+
# If decision is "refuse", e:second-candidate-in-flight is still a hard stop.
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
`candidate-hygiene.mjs` and `candidate-identity.mjs` consume one shared parser and normalization. A foreign tag in the selected namespace is cleanable only when the full capture proves it has **0% traffic**, canonical terminal evidence (a resolve-release manifest or immutable evidence bundle) matches its exact tag and revision, and `check-release-lease-scoped` returns a valid empty report for the same product. Nonzero or ambiguous traffic, missing/nonterminal/mismatched evidence, and held or unknown matching leases still refuse with `e:second-candidate-in-flight`. Without the lease report after the other proofs, the script reports `needs-lease-report` so the operator does not guess. The release lane may pre-authorize this hygiene in the envelope ([pre-flight item 3](preflight.md#pre-flight-gate-confirm-the-authorization-envelope-mandatory)); otherwise it stops for human confirm before executing the cleanup command.
|
|
156
|
+
|
|
157
|
+
**Record `release.candidateIdentity` — the identity, not just the coordinates.** `attest`'s output is the field verbatim: `sourceSha`, `revision`, `imageDigest`, `imageDigestSource`, plus the `url`/`tag` it was read from and the `guarantee`/`weakenings` it was established under. **Every one of those fields is populated in every branch that accepts** — `imageDigestSource` is `attestation` under the full guarantee and `candidate-revision-spec` under the declared weakening, which is what tells a later reader (and step 4b) which comparison they are getting without inferring it from which fields happen to be set. **A populated record is not a verified one, though.** `attest` re-derives the coordinates it copies out of the `locate` artifact, but it finds what to re-derive by matching the record against that artifact, so a field the record carries under a *different* name from the one it was read under is copied across without a second look. Read the fields you are about to act on — row 4a's identity question is a check you run, not one this script has already run for you. `candidateRevision`/`candidateUrl` say *where* the candidate is; the tuple says *what it is*, and it is what step 4b's serving confirmation re-reads after the shift instead of re-deriving identity from an image string.
|
|
158
|
+
|
|
159
|
+
**Record `release.candidateRevision`, `release.candidateUrl`, and `release.previousRevision` in the manifest as soon as the build lands** — and **copy the first two out of `release.candidateIdentity` verbatim, never re-read them from the platform.** A second read is a second observation and can name a different deployment, which puts the fence in step 4b (assertion 2 reads `candidateUrl` and `candidateIdentity.tag` as if they were the same reading) in the position of comparing two artifacts nothing cross-checked. One reading, copied, keeps them one artifact. `previousRevision` is the exception and is read from the platform: it is the revision serving 100% right now, which `candidateIdentity` says nothing about — read, not assumed. This is load-bearing, not bookkeeping: between here and the step-4 traffic shift, production carries a revision no user reaches, and if the session dies now, only the manifest says it exists. `previousRevision` is what step 4b's re-check fence and step 5's failure paths compare the live traffic split against — without it recorded *before* the shift, "the incumbent still holds 100%" is a claim with nothing to check it against. A resumed run reads those fields and either completes the validation or deletes the revision — an orphan 0% revision left on prod indefinitely is the failure mode this step is designed to prevent.
|
|
160
|
+
|
|
161
|
+
**On resume, observe before you act — never infer exposure from the manifest alone.** The manifest write can lag the outward action by one crash, so `step` tells you what was *started*, not what landed. Any resume at `deploy`/`validate`/`expose` begins by reading the **live traffic split** from the platform and comparing it against `candidateRevision`. That reading, not the manifest, decides the branch: candidate at 0% → continue validating; candidate at 100% → you are past exposure, go to `verify`; anything in between → a partial shift (see step 4b). Choosing a branch without this is how a resumed session deletes the revision currently serving production.
|
|
162
|
+
|
|
163
|
+
**Where pre-flight item 6 declared durable processing, that resume reading is two reads, not one** — the traffic split *and* the processing state (is scheduling enabled? is admission open?). Under the [step-4b sequence](exposure.md#4--validate-the-candidate-then-expose-it-skill-smoke) the pause precedes the shift, so "candidate at 0%" no longer identifies one state: it is what a resume finds both when validation never finished *and* when a crash landed **between the pause and the shift**, with production serving the incumbent and its processing stopped. The split alone cannot tell those apart, and taking the first reading is a silent outage — the release lane paused production and then walked away from it. Read the processing state, and if it cannot be read, that is the ambiguous state ([principle 14](../SKILL.md#non-negotiable-principles)), not a branch to pick.
|
|
164
|
+
|
|
165
|
+
**The revision name is still the durable handle.** A tag — even a SHA-keyed one — is a pointer the platform owns, so deletion and every retry target the **revision name**, which names one immutable artifact. What the SHA-keyed tag changed is the *discovery*, not the handle: under a shared `candidate` tag the URL meant "the newest candidate" and a disagreement was something to notice, whereas now a rival release cannot take this SHA's tag and shows up as `e:second-candidate-in-flight` above.
|
|
166
|
+
|
|
167
|
+
A failed **build** never produced a candidate: route to `cloud-debug`, fix forward or abandon. Prod was never touched, no rollback, and — because no final tag exists — **no version is burned**; the retry reuses the version at the next rc.
|