@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,721 @@
|
|
|
1
|
+
// combine-and-verify — the MECHANIZED epic integration gate. It assembles the
|
|
2
|
+
// cluster (creates an integration branch off the target, merges each unit's
|
|
3
|
+
// branch in dependency order, captures conflicts) and reduces the gate results
|
|
4
|
+
// into a single integration verdict whose status is ALIVE only when the combined
|
|
5
|
+
// tree is assembled clean AND its CI, integrated review, and smoke all came back
|
|
6
|
+
// green.
|
|
7
|
+
//
|
|
8
|
+
// The smoke gate has ONE honest exception (#393). A component in a repo with no
|
|
9
|
+
// bootable surface / no curated golden-path manifest genuinely cannot run smoke —
|
|
10
|
+
// this skills repo is the case in point (no root package.json, no app to boot). For
|
|
11
|
+
// that, and ONLY that, smoke reaches its pass by an explicit, reasoned
|
|
12
|
+
// `not-applicable`: the orchestrator records `gates.smoke = 'not-applicable'` AND a
|
|
13
|
+
// top-level `smokeNotApplicableReason: '<why>'`. This is the SAME honesty pattern as
|
|
14
|
+
// green-gate's N/A: an N/A-with-reason is NOT a pass fabricated to get green. The
|
|
15
|
+
// guardrails are sharp — an N/A with no reason fails closed (treated like a missing
|
|
16
|
+
// gate); a MISSING smoke field is still DEAD (missing ≠ N/A, never a silent default);
|
|
17
|
+
// `pending`/`DEAD`/`EMPTY` still fail; and a repo that CAN boot still owes a real
|
|
18
|
+
// ALIVE. Fabricating `smoke: 'ALIVE'` where the app never ran is still forbidden —
|
|
19
|
+
// the reasoned N/A is the alternative to that fabrication, and `verdict`/`mergeable`
|
|
20
|
+
// surface it in the handoff so a merge-without-smoke is recorded, never buried.
|
|
21
|
+
//
|
|
22
|
+
// Why this exists as a script and not prose: the integration gate is a
|
|
23
|
+
// load-bearing, late-in-a-long-run step. Described in prose it degrades to a
|
|
24
|
+
// lookalike or gets skipped ("no cluster"). Mechanized, it cannot be forgotten —
|
|
25
|
+
// and its `integration-verdict.json` is the forcing artifact a merge/promote
|
|
26
|
+
// step refuses to proceed without. "No artifact, or status DEAD" = the gate did
|
|
27
|
+
// not pass = do not merge.
|
|
28
|
+
//
|
|
29
|
+
// node combine-and-verify.mjs validate --units units.json
|
|
30
|
+
// node combine-and-verify.mjs assemble --units units.json --target dev --branch integ/epic-42 --out integration-verdict.json
|
|
31
|
+
// node combine-and-verify.mjs verdict --in integration-verdict.json # exit 0 = ALIVE, 1 = DEAD
|
|
32
|
+
// node combine-and-verify.mjs mergeable --in integration-verdict.json --manifest run-manifest.json --component <id> --pr <ref> [--deps id:ref,…]
|
|
33
|
+
// # step-7 gate: exit 0 = safe to merge, 1 = no. Authorization is READ from the
|
|
34
|
+
// # manifest's mergeShippable (never a flag); ancestry checks fetch + use origin/<target>.
|
|
35
|
+
//
|
|
36
|
+
// units.json is the cluster roster (from the run manifest): an array of
|
|
37
|
+
// { id, branch, dependsOn?: [ids] }. The CI / review / smoke results are filled
|
|
38
|
+
// into the verdict's `gates` by the agent steps that run those skills; this
|
|
39
|
+
// script owns the decidable shell — merge order, the git assembly, and the
|
|
40
|
+
// gate arithmetic — never the judgment (review) or the driving (smoke).
|
|
41
|
+
|
|
42
|
+
import fs from 'node:fs'
|
|
43
|
+
import { execFileSync } from 'node:child_process'
|
|
44
|
+
import { isMainModule } from '../../engineering-runtime/scripts/main-module.mjs'
|
|
45
|
+
import { validateExactHeadArtifact } from '../../engineering-runtime/scripts/exact-head-artifact.mjs'
|
|
46
|
+
import { checksForPhase, validateQueueExecutionArtifact } from '../../engineering-runtime/scripts/required-check-plan.mjs'
|
|
47
|
+
import { targetBaseDrift, validateComponentCandidates } from './component-candidate.mjs'
|
|
48
|
+
import { completionBlockers } from './unit-lifecycle.mjs'
|
|
49
|
+
|
|
50
|
+
// ---- pure, unit-testable core (no git, no network) ----
|
|
51
|
+
|
|
52
|
+
// The gates that must each be green for the combined tree to be ALIVE, and the
|
|
53
|
+
// single value each must hold. Anything else — including the `pending` the
|
|
54
|
+
// assemble step writes — keeps the verdict DEAD. That is the forcing function:
|
|
55
|
+
// the artifact stays DEAD until the real sub-results are recorded.
|
|
56
|
+
//
|
|
57
|
+
// `smoke` has one extra pass value beyond `ALIVE`: an explicit, reasoned
|
|
58
|
+
// `not-applicable` (see the file header and `SMOKE_NOT_APPLICABLE` /
|
|
59
|
+
// `computeIntegrationVerdict`). ci and review have NO such exception — only smoke
|
|
60
|
+
// gains it, because only smoke can be genuinely unrunnable (no bootable surface).
|
|
61
|
+
export const GATE_PASS = { ci: 'green', review: 'PASS', smoke: 'ALIVE' }
|
|
62
|
+
|
|
63
|
+
// The third, explicitly-reasoned smoke state (#393): NOT a pass on its own — it
|
|
64
|
+
// passes ONLY when accompanied by a top-level `smokeNotApplicableReason` on the
|
|
65
|
+
// verdict. Absent the reason it fails closed, exactly like a missing gate.
|
|
66
|
+
export const SMOKE_NOT_APPLICABLE = 'not-applicable'
|
|
67
|
+
|
|
68
|
+
// Structural validation of the cluster roster. Decidable, zero-judgment.
|
|
69
|
+
export function validateUnits(units) {
|
|
70
|
+
const errors = []
|
|
71
|
+
if (!Array.isArray(units) || units.length === 0) {
|
|
72
|
+
return { ok: false, errors: ['units must be a non-empty array — an integration gate over no units proves nothing'] }
|
|
73
|
+
}
|
|
74
|
+
const ids = new Set()
|
|
75
|
+
for (const [i, u] of units.entries()) {
|
|
76
|
+
const at = `units[${i}]${u && u.id ? ` (${u.id})` : ''}`
|
|
77
|
+
if (!u || typeof u !== 'object') { errors.push(`${at}: not an object`); continue }
|
|
78
|
+
if (!u.id) errors.push(`${at}: missing id`)
|
|
79
|
+
else if (ids.has(u.id)) errors.push(`${at}: duplicate id "${u.id}"`)
|
|
80
|
+
else ids.add(u.id)
|
|
81
|
+
if (!u.branch) errors.push(`${at}: missing branch`)
|
|
82
|
+
}
|
|
83
|
+
for (const u of units) {
|
|
84
|
+
for (const dep of u.dependsOn || []) {
|
|
85
|
+
if (!ids.has(dep)) errors.push(`unit "${u.id}": dependsOn unknown unit "${dep}"`)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return { ok: errors.length === 0, errors }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Dependency-order the units (topological sort), preserving input order among
|
|
92
|
+
// independent units so merges are stable and break-attribution is clean. Throws
|
|
93
|
+
// on a cycle — an unorderable cluster is a finding, not a silent arbitrary order.
|
|
94
|
+
export function mergeOrder(units) {
|
|
95
|
+
const byId = new Map(units.map((u) => [u.id, u]))
|
|
96
|
+
const state = new Map() // id -> 0 unvisited | 1 in-progress | 2 done
|
|
97
|
+
const order = []
|
|
98
|
+
const visit = (id, stack) => {
|
|
99
|
+
if (state.get(id) === 2) return
|
|
100
|
+
if (state.get(id) === 1) throw new Error(`dependency cycle: ${[...stack, id].join(' → ')}`)
|
|
101
|
+
state.set(id, 1)
|
|
102
|
+
for (const dep of byId.get(id).dependsOn || []) {
|
|
103
|
+
if (byId.has(dep)) visit(dep, [...stack, id])
|
|
104
|
+
}
|
|
105
|
+
state.set(id, 2)
|
|
106
|
+
order.push(id)
|
|
107
|
+
}
|
|
108
|
+
for (const u of units) visit(u.id, [])
|
|
109
|
+
return order
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function consumeQueueExecutionArtifact(verdict = {}, artifact) {
|
|
113
|
+
const plan = verdict.requiredCheckPlan ?? verdict.exactHeadEvidence?.requiredCheckPlan ?? null
|
|
114
|
+
const validation = validateQueueExecutionArtifact(plan, artifact, {
|
|
115
|
+
expectedRepository: plan?.repository,
|
|
116
|
+
expectedPr: verdict.combinedPr ?? undefined,
|
|
117
|
+
expectedPrHeadSha: verdict.integrationHeadSha,
|
|
118
|
+
})
|
|
119
|
+
return {
|
|
120
|
+
...verdict,
|
|
121
|
+
gates: { ...(verdict.gates || {}), queueExecution: validation.ok ? 'pass' : 'fail' },
|
|
122
|
+
queueExecutionEvidence: validation.ok ? artifact : { errors: validation.errors },
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function consumeExactHeadArtifact(verdict = {}, artifact) {
|
|
127
|
+
const expectedHeadSha = verdict.integrationHeadSha
|
|
128
|
+
const validation = expectedHeadSha
|
|
129
|
+
? validateExactHeadArtifact(artifact, { expectedHeadSha })
|
|
130
|
+
: { ok: false, errors: ['integrationHeadSha is not recorded — cannot bind exact-head evidence'] }
|
|
131
|
+
const provenance = artifact?.collectionMode === 'choreographed'
|
|
132
|
+
? { kind: 'choreographed', restorationProved: artifact?.restoration?.proved === true }
|
|
133
|
+
: artifact?.collectionMode === 'observed-existing-run'
|
|
134
|
+
? {
|
|
135
|
+
kind: 'observed-existing-run',
|
|
136
|
+
headObservedBeforeAndAfter: artifact?.observation?.before?.headSha === artifact?.headSha
|
|
137
|
+
&& artifact?.observation?.after?.headSha === artifact?.headSha,
|
|
138
|
+
workflowRunId: artifact?.workflow?.runId ?? null,
|
|
139
|
+
}
|
|
140
|
+
: { kind: null }
|
|
141
|
+
return {
|
|
142
|
+
...verdict,
|
|
143
|
+
gates: { ...(verdict.gates || {}), exactHead: validation.ok ? 'pass' : 'fail' },
|
|
144
|
+
requiredCheckPlan: artifact?.requiredCheckPlan ?? null,
|
|
145
|
+
exactHeadEvidence: {
|
|
146
|
+
collectionMode: artifact?.collectionMode ?? null,
|
|
147
|
+
headSha: artifact?.headSha ?? null,
|
|
148
|
+
outcome: artifact?.summary?.outcome ?? null,
|
|
149
|
+
jobs: Array.isArray(artifact?.jobs)
|
|
150
|
+
? artifact.jobs.map(({ id, name, status, conclusion }) => ({ id, name, status: status ?? null, conclusion }))
|
|
151
|
+
: [],
|
|
152
|
+
provenance,
|
|
153
|
+
errors: validation.errors,
|
|
154
|
+
},
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Reduce the assembled result + the gate results into the integration verdict.
|
|
159
|
+
// ALIVE iff: assembled with no conflicts AND every gate holds its pass value.
|
|
160
|
+
// A `pending` / missing / wrong gate value keeps it DEAD, with the reason named.
|
|
161
|
+
//
|
|
162
|
+
// `smoke` alone has a third pass state — an explicit, reasoned `not-applicable`
|
|
163
|
+
// (#393): for a repo with no bootable surface, `gates.smoke = 'not-applicable'`
|
|
164
|
+
// PLUS a non-empty top-level `smokeNotApplicableReason` reaches ALIVE. Fail-closed
|
|
165
|
+
// guardrails: an N/A with no reason is DEAD (unreasoned, treated like a missing
|
|
166
|
+
// gate); a MISSING smoke field is DEAD (missing ≠ N/A); `pending`/`DEAD`/`EMPTY`
|
|
167
|
+
// stay DEAD; `ALIVE` is still the real pass a bootable repo owes. ci and review get
|
|
168
|
+
// no exception — the special-case is scoped to smoke.
|
|
169
|
+
export function computeIntegrationVerdict({ assembled, conflicts = [], gates = {}, smokeNotApplicableReason, integrationHeadSha, requiredCheckPlan } = {}) {
|
|
170
|
+
const reasons = []
|
|
171
|
+
if (!assembled) reasons.push('assembly did not complete')
|
|
172
|
+
if (conflicts.length) reasons.push(`merge conflict(s): ${conflicts.map((c) => c.unit).join(', ')} — route each back to the owning unit (do not hand-resolve)`)
|
|
173
|
+
if (integrationHeadSha && gates.exactHead !== 'pass') {
|
|
174
|
+
reasons.push(`gate "exactHead" is ${gates.exactHead == null ? 'not recorded' : `"${gates.exactHead}"`}, needs "pass" from a validated exact-head artifact`)
|
|
175
|
+
}
|
|
176
|
+
for (const [gate, want] of Object.entries(GATE_PASS)) {
|
|
177
|
+
const got = gates[gate]
|
|
178
|
+
if (gate === 'smoke') {
|
|
179
|
+
if (got === want) continue // real ALIVE — the pass a bootable repo owes
|
|
180
|
+
if (got === SMOKE_NOT_APPLICABLE) {
|
|
181
|
+
const reason = typeof smokeNotApplicableReason === 'string' && smokeNotApplicableReason.trim()
|
|
182
|
+
if (reason) continue // honest N/A: passes, surfaced in the handoff
|
|
183
|
+
reasons.push(`gate "smoke" is "${SMOKE_NOT_APPLICABLE}" but no smokeNotApplicableReason was recorded — an unreasoned N/A fails closed, exactly like a missing gate`)
|
|
184
|
+
continue
|
|
185
|
+
}
|
|
186
|
+
// missing / pending / DEAD / EMPTY / any other value: still DEAD (missing ≠ N/A).
|
|
187
|
+
reasons.push(`gate "smoke" is ${got == null ? 'not recorded' : `"${got}"`}, needs "${want}" (or "${SMOKE_NOT_APPLICABLE}" with a smokeNotApplicableReason for a repo with no bootable surface)`)
|
|
188
|
+
continue
|
|
189
|
+
}
|
|
190
|
+
if (got !== want) reasons.push(`gate "${gate}" is ${got == null ? 'not recorded' : `"${got}"`}, needs "${want}"`)
|
|
191
|
+
}
|
|
192
|
+
return { status: reasons.length === 0 ? 'ALIVE' : 'DEAD', ok: reasons.length === 0, reasons }
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// The handoff note that surfaces an N/A smoke decision so a merge-without-smoke is
|
|
196
|
+
// recorded, not buried (#393's core ask). Returns null when smoke actually ran
|
|
197
|
+
// (ALIVE) or is not N/A — nothing to surface. When smoke is `not-applicable`, echoes
|
|
198
|
+
// the reason (or names the missing reason). Consumed by the `verdict` and `mergeable`
|
|
199
|
+
// CLI output; pure + exported so it is unit-testable.
|
|
200
|
+
export function smokeHandoffNote(verdict = {}) {
|
|
201
|
+
const gates = verdict.gates || {}
|
|
202
|
+
if (gates.smoke !== SMOKE_NOT_APPLICABLE) return null
|
|
203
|
+
const reason = typeof verdict.smokeNotApplicableReason === 'string' && verdict.smokeNotApplicableReason.trim()
|
|
204
|
+
return reason
|
|
205
|
+
? `smoke: not-applicable — ${reason} (no ALIVE was fabricated; fabricating one is still forbidden)`
|
|
206
|
+
: 'smoke: not-applicable but no smokeNotApplicableReason recorded — unreasoned N/A, treated as a missing gate'
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// The decidable preconditions for merging an independently-shippable component
|
|
210
|
+
// into the target branch (integration gate step 7). This is the sibling of
|
|
211
|
+
// computeIntegrationVerdict: like `verdict` it only *decides and refuses*, it
|
|
212
|
+
// does not act. The difference from the prose it replaces is that every fact is
|
|
213
|
+
// gathered live (gh/git) by the CLI below and **fails closed** — an ungatherable
|
|
214
|
+
// fact reads as not-satisfied, never satisfied — so the check verifies real state
|
|
215
|
+
// rather than the orchestrator's belief. It deliberately does NOT perform the
|
|
216
|
+
// merge: pushing to a protected branch is repo-policy-shaped *driving* the
|
|
217
|
+
// orchestrator owns (the same boundary that keeps smoke's driving out of here),
|
|
218
|
+
// and a merge has no mandatory manifest chokepoint to make a gate unbypassable
|
|
219
|
+
// the way the round circuit-breaker's transition commands are. What this buys is
|
|
220
|
+
// a live, decidable go/no-go — run it, gate on exit 0, then `gh pr merge`.
|
|
221
|
+
export function publishedPrHead(view) {
|
|
222
|
+
return typeof view?.headRefOid === 'string' && /^[0-9a-f]{7,64}$/i.test(view.headRefOid)
|
|
223
|
+
? view.headRefOid
|
|
224
|
+
: null
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export function candidateAuthorization(manifest, componentId, verdict, liveHeadSha) {
|
|
228
|
+
const reasons = []
|
|
229
|
+
if (typeof componentId !== 'string' || !componentId.trim()) {
|
|
230
|
+
reasons.push('component id is required — unit PASS is preliminary evidence and cannot select the final authorization boundary')
|
|
231
|
+
return { authorized: false, reasons }
|
|
232
|
+
}
|
|
233
|
+
const component = Array.isArray(manifest?.components)
|
|
234
|
+
? manifest.components.find((entry) => entry?.id === componentId)
|
|
235
|
+
: null
|
|
236
|
+
if (!component) {
|
|
237
|
+
reasons.push(`component ${componentId} is not recorded in the run manifest`)
|
|
238
|
+
return { authorized: false, reasons }
|
|
239
|
+
}
|
|
240
|
+
const validationErrors = validateComponentCandidates(manifest)
|
|
241
|
+
if (validationErrors.length) reasons.push(`component candidate state is invalid: ${validationErrors[0]}`)
|
|
242
|
+
const candidate = component.candidate
|
|
243
|
+
const frozen = candidate?.frozen
|
|
244
|
+
const terminal = Array.isArray(candidate?.authorizations) ? candidate.authorizations.at(-1) : null
|
|
245
|
+
if (candidate?.state !== 'authorized') reasons.push(`component candidate is ${candidate?.state ?? 'not recorded'}, needs "authorized"`)
|
|
246
|
+
if (!frozen?.headSha) reasons.push('component candidate has no frozen HEAD')
|
|
247
|
+
if (!terminal || terminal.generation !== candidate?.generation || terminal.headSha !== frozen?.headSha) {
|
|
248
|
+
reasons.push('component candidate has no terminal authorization for its current frozen generation')
|
|
249
|
+
}
|
|
250
|
+
if (component.authorizationBoundary?.kind !== 'integration' || component.authorizationBoundary?.value !== frozen?.headSha) {
|
|
251
|
+
reasons.push('component authorization boundary is not the frozen component HEAD')
|
|
252
|
+
}
|
|
253
|
+
const recomputed = verdict?.requiredCheckPlan ? computeIntegrationVerdict(verdict) : { ok: verdict?.status === 'ALIVE', reasons: [] }
|
|
254
|
+
if (verdict?.status !== 'ALIVE' || !recomputed.ok) reasons.push(`integration verdict is ${verdict?.status == null ? 'not recorded' : `"${verdict.status}"`}, needs recomputed "ALIVE" (${recomputed.reasons.join('; ') || 'stored status mismatch'})`)
|
|
255
|
+
if (verdict?.integrationBranch !== frozen?.branch) reasons.push('integration verdict branch does not match the frozen candidate branch')
|
|
256
|
+
if (liveHeadSha !== frozen?.headSha) reasons.push(`published PR HEAD ${liveHeadSha ?? 'could not be resolved'} does not equal frozen candidate HEAD ${frozen?.headSha ?? 'not recorded'}`)
|
|
257
|
+
return { authorized: reasons.length === 0, reasons }
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export function mergePreconditions({ authorized, candidateAuthorized, verdictStatus, ciGreen, unmergedDeps = [], completionBlockedOn = [], fastForwardable, targetDrifted = false, targetDriftReasons = [] }) {
|
|
261
|
+
const reasons = []
|
|
262
|
+
if (authorized !== true) reasons.push('mergeShippable was not authorized at pre-flight — this skill merges nothing without it (guarantee 4)')
|
|
263
|
+
if (candidateAuthorized !== true) reasons.push('the published PR HEAD is not the authorized frozen component HEAD — unit PASSes and stale component verdicts are preliminary evidence, not delivery authorization')
|
|
264
|
+
if (verdictStatus !== 'ALIVE') reasons.push(`integration verdict is ${verdictStatus == null ? 'not recorded' : `"${verdictStatus}"`}, needs "ALIVE"`)
|
|
265
|
+
if (ciGreen !== true) reasons.push("CI is not verified green on the PR's current HEAD")
|
|
266
|
+
if (unmergedDeps.length) reasons.push(`dependency component(s) not yet in the target: ${unmergedDeps.join(', ')} — a component merges only after everything it depends on`)
|
|
267
|
+
if (completionBlockedOn.length) reasons.push(`external/human completion dependency is still pending: ${completionBlockedOn.join(', ')}`)
|
|
268
|
+
if (fastForwardable !== true) reasons.push('the integration branch is not fast-forwardable onto the target — back-merge the target in and re-verify CI first, never force')
|
|
269
|
+
// #944 / epic-867: target trunk moved under a multi-unit component mid-integration.
|
|
270
|
+
if (targetDrifted === true) {
|
|
271
|
+
const detail = Array.isArray(targetDriftReasons) && targetDriftReasons.length
|
|
272
|
+
? targetDriftReasons.join('; ')
|
|
273
|
+
: 'target trunk moved since assemble without re-ALIVE'
|
|
274
|
+
reasons.push(detail)
|
|
275
|
+
}
|
|
276
|
+
return { ok: reasons.length === 0, reasons }
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Queue-head checks stay in the frozen required-check plan, but their synthetic
|
|
280
|
+
// commit cannot exist before admission. Eligibility proves the plan has a real
|
|
281
|
+
// provider observation path; completion consumes the resulting artifact.
|
|
282
|
+
export function queueObservationMapping(plan) {
|
|
283
|
+
const errors = []
|
|
284
|
+
for (const check of checksForPhase(plan, 'queue-head')) {
|
|
285
|
+
if (!check?.queueEvidence?.provider || !check?.queueEvidence?.acquisition) {
|
|
286
|
+
errors.push(`queue-head requirement ${check?.name ?? 'unknown'} has no provider observation path`)
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return { ok: errors.length === 0, errors }
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export function enqueueEligibility({ authorized, candidateAuthorized, verdict, ciGreen, unmergedDeps = [], completionBlockedOn = [], targetDrifted = false, targetDriftReasons = [] } = {}) {
|
|
293
|
+
// Source-to-live-target fast-forwardability is a direct-merge requirement. A batched
|
|
294
|
+
// queue deliberately accepts a source branch behind the live target and tests the
|
|
295
|
+
// synthetic current-base plus candidate tree. targetDrifted carries the queue-specific
|
|
296
|
+
// ancestry proof: the recorded assembly base must still be in live target history.
|
|
297
|
+
const preconditions = mergePreconditions({ authorized, candidateAuthorized, verdictStatus: verdict?.status, ciGreen, unmergedDeps, completionBlockedOn, fastForwardable: true, targetDrifted, targetDriftReasons })
|
|
298
|
+
const mapping = queueObservationMapping(verdict?.requiredCheckPlan)
|
|
299
|
+
return { ok: preconditions.ok && mapping.ok, reasons: [...preconditions.reasons, ...mapping.errors] }
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function deliveryCompletion(verdict = {}, { pr, mergedSha, ancestry } = {}) {
|
|
303
|
+
const reasons = []
|
|
304
|
+
if (ancestry !== true) reasons.push('merge commit ancestry is not proved in freshly fetched origin target')
|
|
305
|
+
if (typeof mergedSha !== 'string' || !/^[0-9a-f]{7,64}$/i.test(mergedSha)) reasons.push('mergedSha must be the provider-reported merge commit')
|
|
306
|
+
const plan = verdict.requiredCheckPlan
|
|
307
|
+
if (checksForPhase(plan, 'queue-head').length) {
|
|
308
|
+
const evidence = validateQueueExecutionArtifact(plan, verdict.queueExecutionEvidence, {
|
|
309
|
+
expectedRepository: plan?.repository,
|
|
310
|
+
expectedPr: pr,
|
|
311
|
+
expectedPrHeadSha: verdict.integrationHeadSha,
|
|
312
|
+
})
|
|
313
|
+
if (!evidence.ok) reasons.push(`queue-head delivery evidence is invalid: ${evidence.errors[0]}`)
|
|
314
|
+
}
|
|
315
|
+
return { ok: reasons.length === 0, reasons }
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export function unresolvedCompletionDependencies(manifest = {}, verdict = {}, pr = null) {
|
|
319
|
+
const units = Array.isArray(manifest.units) ? manifest.units.filter((unit) => unit && typeof unit === 'object') : []
|
|
320
|
+
const ids = new Set(Array.isArray(verdict.mergeOrder) ? verdict.mergeOrder : [])
|
|
321
|
+
if (!ids.size && Array.isArray(manifest.components)) {
|
|
322
|
+
const component = manifest.components.find((entry) => entry?.pr != null && String(entry.pr) === String(pr))
|
|
323
|
+
for (const unit of component?.units ?? []) ids.add(typeof unit === 'string' ? unit : unit?.id)
|
|
324
|
+
}
|
|
325
|
+
const selected = ids.size
|
|
326
|
+
? units.filter((unit) => ids.has(unit.id))
|
|
327
|
+
: units.filter((unit) => unit.pr != null && String(unit.pr) === String(pr))
|
|
328
|
+
return selected.flatMap((unit) => completionBlockers(manifest, unit).map((dependency) => `${unit.id}->${dependency}`))
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// ---- git/gh live facts (side-effecting; not unit-tested, like smoke's driving) ----
|
|
332
|
+
|
|
333
|
+
function git(args, opts = {}) {
|
|
334
|
+
return execFileSync('git', args, { encoding: 'utf8', ...opts }).trim()
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Every gather below FAILS CLOSED: a non-zero exit (not an ancestor, missing ref,
|
|
338
|
+
// no gh, unauthenticated, a pending or failing check) resolves to the unsafe
|
|
339
|
+
// answer, never the safe one — the merge decision must never default to "go".
|
|
340
|
+
// Pure half of the --pr check, so the rule can be tested without a repo or a network.
|
|
341
|
+
// `view` is `gh pr view --json state,mergeCommit,headRefOid`.
|
|
342
|
+
export function deliveredShaCheck(sha, view) {
|
|
343
|
+
const state = view?.state
|
|
344
|
+
const merge = view?.mergeCommit?.oid ?? null
|
|
345
|
+
const head = view?.headRefOid ?? null
|
|
346
|
+
if (state && state !== 'MERGED') {
|
|
347
|
+
return { ok: false, reason: `the PR is ${state}, not MERGED — "delivered" is a claim about a merge that has happened; a request is not a result` }
|
|
348
|
+
}
|
|
349
|
+
if (!merge) return { ok: true, note: 'the PR reports no mergeCommit — checking the --sha as given' }
|
|
350
|
+
if (sha === merge) return { ok: true }
|
|
351
|
+
if (head && sha === head) {
|
|
352
|
+
return {
|
|
353
|
+
ok: false,
|
|
354
|
+
reason: `--sha ${sha} is the PR's BRANCH HEAD; the merge landed as ${merge}. On a squash or rebase queue the head is never contained in the target, so this check would report a genuinely merged PR as undelivered — pass the merge commit (${merge})`,
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return { ok: false, reason: `--sha ${sha} is neither the PR's merge commit (${merge}) nor its head (${head ?? 'unknown'}) — record what actually landed, not a sha from elsewhere in the run` }
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
export function tryJson(cmd, args) {
|
|
361
|
+
try { return JSON.parse(execFileSync(cmd, args, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] })) }
|
|
362
|
+
catch { return null }
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export function isAncestor(ancestor, descendant) {
|
|
366
|
+
// Exit-code-only (no stdout to trim), so call execFileSync directly rather than
|
|
367
|
+
// through git()'s .trim() wrapper. stderr silenced: a missing ref is an expected
|
|
368
|
+
// fail-closed path, not an error to surface — `false` is the answer, git's
|
|
369
|
+
// diagnostic just noise.
|
|
370
|
+
try { execFileSync('git', ['merge-base', '--is-ancestor', ancestor, descendant], { stdio: 'ignore' }); return true }
|
|
371
|
+
catch { return false }
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Does a ref resolve? Used to decide whether origin/<target> exists before the
|
|
375
|
+
// ancestry checks compare against it — a non-existent ref would fail closed, but we
|
|
376
|
+
// want to distinguish "no remote configured" from "genuinely behind" and say which.
|
|
377
|
+
export function isRef(ref) {
|
|
378
|
+
try { execFileSync('git', ['rev-parse', '--verify', '--quiet', `${ref}^{commit}`], { stdio: 'ignore' }); return true }
|
|
379
|
+
catch { return false }
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function ciGreenFor(pr) {
|
|
383
|
+
// `gh pr checks` exits 0 only when every required check has passed (8 = still
|
|
384
|
+
// pending, 1 = a failure); any non-zero → not green.
|
|
385
|
+
try { execFileSync('gh', ['pr', 'checks', pr], { stdio: 'ignore' }); return true }
|
|
386
|
+
catch { return false }
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Assemble the live facts mergePreconditions needs. `deps` is [{ id, ref }] — the
|
|
390
|
+
// dependency components' tips, each of which must already be an ancestor of the
|
|
391
|
+
// target (i.e. merged) before this component may go. Caveat the caller owns: this
|
|
392
|
+
// verifies the deps it is GIVEN are merged, not that the list is complete — the
|
|
393
|
+
// component graph lives in the manifest, so the orchestrator must pass every
|
|
394
|
+
// dependency; a dep nobody declares is a dep this gate can't fail closed on.
|
|
395
|
+
//
|
|
396
|
+
// `targetRef` is the ref the ancestry checks run against — the CLI passes the
|
|
397
|
+
// freshly-fetched `origin/<target>`, NOT the bare local branch, because `mergeable`
|
|
398
|
+
// runs long after `assemble` (after CI/review/smoke) and a local branch that fell
|
|
399
|
+
// behind origin would let the "live" FF/deps check pass on stale data. `authorized`
|
|
400
|
+
// is a fact the CALLER must have read from durable state (the manifest's
|
|
401
|
+
// mergeShippable), never asserted — the CLI reads it from --manifest.
|
|
402
|
+
export function gatherMergeFacts(verdict, { authorized, pr, deps = [], targetRef }, ci = ciGreenFor) {
|
|
403
|
+
const target = targetRef || verdict.targetBranch
|
|
404
|
+
return {
|
|
405
|
+
authorized: authorized === true,
|
|
406
|
+
verdictStatus: verdict.status,
|
|
407
|
+
ciGreen: pr ? ci(pr) : false,
|
|
408
|
+
unmergedDeps: deps.filter((d) => !isAncestor(d.ref, target)).map((d) => d.id),
|
|
409
|
+
fastForwardable: isAncestor(target, verdict.integrationBranch),
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// `--target` may be the fetched remote head (`origin/dev` — the assemble base
|
|
414
|
+
// should be the latest head, not a stale local branch). The verdict's
|
|
415
|
+
// `targetBranch` field is consumed as a PR *base*, which must be the plain
|
|
416
|
+
// branch name — so strip the remote qualifier there, while the diff range
|
|
417
|
+
// keeps the exact ref the assembly actually ran against.
|
|
418
|
+
export function branchName(ref) {
|
|
419
|
+
return ref.replace(/^origin\//, '')
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Create the integration branch off `target` and merge each unit in dependency
|
|
423
|
+
// order. A merge that conflicts is aborted (branch returns to the last clean
|
|
424
|
+
// tip) and recorded — then the run continues so ALL conflicts surface in one
|
|
425
|
+
// pass. Returns { assembled, conflicts, mergeOrder, merged }.
|
|
426
|
+
export function assemble(units, target, branch) {
|
|
427
|
+
const order = mergeOrder(units)
|
|
428
|
+
const byId = new Map(units.map((u) => [u.id, u]))
|
|
429
|
+
git(['checkout', '-B', branch, target])
|
|
430
|
+
const conflicts = []
|
|
431
|
+
const merged = []
|
|
432
|
+
for (const id of order) {
|
|
433
|
+
const u = byId.get(id)
|
|
434
|
+
try {
|
|
435
|
+
git(['merge', '--no-ff', '--no-edit', u.branch])
|
|
436
|
+
merged.push(id)
|
|
437
|
+
} catch {
|
|
438
|
+
let files = []
|
|
439
|
+
try { files = git(['diff', '--name-only', '--diff-filter=U']).split('\n').filter(Boolean) } catch { /* ignore */ }
|
|
440
|
+
try { git(['merge', '--abort']) } catch { /* ignore */ }
|
|
441
|
+
conflicts.push({ unit: id, branch: u.branch, files })
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return { assembled: conflicts.length === 0, conflicts, mergeOrder: order, merged }
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// ---- CLI ----
|
|
448
|
+
|
|
449
|
+
function parseArgs(argv) {
|
|
450
|
+
const f = {}
|
|
451
|
+
for (let i = 0; i < argv.length; i++) {
|
|
452
|
+
const a = argv[i]
|
|
453
|
+
if (a === '--units') f.units = argv[++i]
|
|
454
|
+
else if (a === '--component') f.component = argv[++i]
|
|
455
|
+
else if (a === '--target') f.target = argv[++i]
|
|
456
|
+
else if (a === '--branch') f.branch = argv[++i]
|
|
457
|
+
else if (a === '--out') f.out = argv[++i]
|
|
458
|
+
else if (a === '--in') f.in = argv[++i]
|
|
459
|
+
else if (a === '--pr') f.pr = argv[++i]
|
|
460
|
+
else if (a === '--artifact') f.artifact = argv[++i]
|
|
461
|
+
else if (a === '--deps') f.deps = argv[++i]
|
|
462
|
+
else if (a === '--manifest') f.manifest = argv[++i]
|
|
463
|
+
else if (a === '--sha') f.sha = argv[++i]
|
|
464
|
+
else if (a === '--no-fetch') f.noFetch = true
|
|
465
|
+
else if (a === '--json') f.json = true
|
|
466
|
+
else if (!f.cmd) f.cmd = a
|
|
467
|
+
}
|
|
468
|
+
return f
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const readJson = (p) => JSON.parse(fs.readFileSync(p, 'utf8'))
|
|
472
|
+
|
|
473
|
+
// The synopsis the docs point `--help` at (references/integration-gate.md treats
|
|
474
|
+
// the script as the source of truth for flags/exit codes/merge order). Kept in
|
|
475
|
+
// sync with the four commands runCli dispatches.
|
|
476
|
+
const USAGE = `combine-and-verify — the mechanized epic integration gate.
|
|
477
|
+
Usage:
|
|
478
|
+
combine-and-verify validate --units <units.json>
|
|
479
|
+
combine-and-verify assemble --units <units.json> --target <branch> --branch <integ-branch> [--out <verdict.json>]
|
|
480
|
+
combine-and-verify record-exact-head --in <integration-verdict.json> --artifact <exact-head.json> [--out <verdict.json>]
|
|
481
|
+
combine-and-verify record-queue-execution --in <integration-verdict.json> --artifact <queue-execution.json> [--out <verdict.json>]
|
|
482
|
+
combine-and-verify verdict --in <integration-verdict.json> [--json] # exit 0 = ALIVE, 1 = DEAD
|
|
483
|
+
combine-and-verify mergeable --in <integration-verdict.json> --manifest <run-manifest.json> --component <id> --pr <ref> [--deps id:ref,…] [--no-fetch] # direct delivery only
|
|
484
|
+
combine-and-verify enqueue-eligible --in <integration-verdict.json> --manifest <run-manifest.json> --component <id> --pr <ref> [--deps id:ref,…] [--no-fetch]
|
|
485
|
+
combine-and-verify delivered --sha <merge-sha> --target <branch> [--pr <n>] [--no-fetch] # direct completion only
|
|
486
|
+
combine-and-verify queue-delivered --in <integration-verdict.json> --artifact <queue-execution.json> --sha <merge-sha> --target <branch> --pr <n> [--no-fetch]
|
|
487
|
+
# --pr checks the sha IS the PR's merge commit (squash/rebase queues never contain the head)
|
|
488
|
+
Authorization for mergeable is READ from the manifest: mergeShippable plus the named component's authorized frozen HEAD, never flags. See the file header for the full contract.`
|
|
489
|
+
|
|
490
|
+
export function runCli(argv) {
|
|
491
|
+
const f = parseArgs(argv)
|
|
492
|
+
if (f.cmd === 'help' || f.cmd === '--help' || f.cmd === '-h') { console.log(USAGE); return 0 }
|
|
493
|
+
if (!f.cmd) { console.error('usage: combine-and-verify <validate|assemble|verdict> [--units f] [--target b] [--branch b] [--in f] [--out f]'); return 2 }
|
|
494
|
+
|
|
495
|
+
if (f.cmd === 'validate') {
|
|
496
|
+
if (!f.units) { console.error('--units is required'); return 2 }
|
|
497
|
+
const { ok, errors } = validateUnits(readJson(f.units))
|
|
498
|
+
if (ok) console.log('combine-and-verify: ✓ units valid')
|
|
499
|
+
else { console.log('combine-and-verify: ✗ units invalid:'); errors.forEach((e) => console.log(` - ${e}`)) }
|
|
500
|
+
return ok ? 0 : 1
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if (f.cmd === 'assemble') {
|
|
504
|
+
if (!f.units || !f.target || !f.branch) { console.error('assemble needs --units, --target, --branch'); return 2 }
|
|
505
|
+
const units = readJson(f.units)
|
|
506
|
+
const { ok, errors } = validateUnits(units)
|
|
507
|
+
if (!ok) { console.error('combine-and-verify: units invalid:'); errors.forEach((e) => console.error(` - ${e}`)); return 1 }
|
|
508
|
+
const a = assemble(units, f.target, f.branch)
|
|
509
|
+
// The gates start `pending` — the agent records ci/review/smoke as it runs
|
|
510
|
+
// those skills against the assembled tip, then `verdict` computes the gate.
|
|
511
|
+
const verdict = {
|
|
512
|
+
targetBranch: branchName(f.target),
|
|
513
|
+
integrationBranch: f.branch,
|
|
514
|
+
integrationHeadSha: git(['rev-parse', f.branch]),
|
|
515
|
+
combinedDiffRange: `${f.target}...${f.branch}`,
|
|
516
|
+
mergeOrder: a.mergeOrder,
|
|
517
|
+
merged: a.merged,
|
|
518
|
+
assembled: a.assembled,
|
|
519
|
+
conflicts: a.conflicts,
|
|
520
|
+
gates: { ci: 'pending', review: 'pending', smoke: 'pending', exactHead: 'pending' },
|
|
521
|
+
exactHeadEvidence: null,
|
|
522
|
+
requiredCheckPlan: null,
|
|
523
|
+
queueExecutionEvidence: null,
|
|
524
|
+
// Filled ONLY when smoke genuinely cannot run — a repo with no bootable
|
|
525
|
+
// surface / no curated golden-path manifest (#393). Then the orchestrator sets
|
|
526
|
+
// `gates.smoke = 'not-applicable'` AND writes the reason here; both are required
|
|
527
|
+
// for the N/A to pass. Left null when smoke ran (records a real ALIVE/DEAD) —
|
|
528
|
+
// never a silent default for a missing smoke result.
|
|
529
|
+
smokeNotApplicableReason: null,
|
|
530
|
+
// Filled by the orchestrator after the verdict is ALIVE: the single
|
|
531
|
+
// combined PR published from this integration branch (gate step 6). The
|
|
532
|
+
// epic's handoff deliverable; null until published.
|
|
533
|
+
combinedPr: null,
|
|
534
|
+
}
|
|
535
|
+
const computed = computeIntegrationVerdict(verdict)
|
|
536
|
+
const out = { ...verdict, status: computed.status, reasons: computed.reasons }
|
|
537
|
+
const dest = f.out || 'integration-verdict.json'
|
|
538
|
+
fs.writeFileSync(dest, JSON.stringify(out, null, 2) + '\n')
|
|
539
|
+
console.log(`combine-and-verify: assembled ${a.merged.length}/${units.length} onto ${f.branch}${a.conflicts.length ? ` — ${a.conflicts.length} conflict(s)` : ''}; gates pending → wrote ${dest}`)
|
|
540
|
+
a.conflicts.forEach((c) => console.log(` ✗ conflict: ${c.unit} (${c.branch}) — ${c.files.join(', ') || 'see git'}`))
|
|
541
|
+
console.log(' next: record ci/review/smoke results into the gates, then `verdict --in ' + dest + '`')
|
|
542
|
+
return a.assembled ? 0 : 1
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
if (f.cmd === 'record-queue-execution') {
|
|
546
|
+
if (!f.in || !f.artifact) { console.error('record-queue-execution needs --in <integration-verdict.json> --artifact <queue-execution.json> [--out <verdict.json>]'); return 2 }
|
|
547
|
+
const consumed = consumeQueueExecutionArtifact(readJson(f.in), readJson(f.artifact))
|
|
548
|
+
const computed = computeIntegrationVerdict(consumed)
|
|
549
|
+
const out = { ...consumed, status: computed.status, reasons: computed.reasons }
|
|
550
|
+
fs.writeFileSync(f.out || f.in, JSON.stringify(out, null, 2) + '\n')
|
|
551
|
+
return consumed.gates.queueExecution === 'pass' ? 0 : 1
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
if (f.cmd === 'record-exact-head') {
|
|
555
|
+
if (!f.in || !f.artifact) { console.error('record-exact-head needs --in <integration-verdict.json> --artifact <exact-head.json> [--out <verdict.json>]'); return 2 }
|
|
556
|
+
const consumed = consumeExactHeadArtifact(readJson(f.in), readJson(f.artifact))
|
|
557
|
+
const computed = computeIntegrationVerdict(consumed)
|
|
558
|
+
const out = { ...consumed, status: computed.status, reasons: computed.reasons }
|
|
559
|
+
const destination = f.out || f.in
|
|
560
|
+
fs.writeFileSync(destination, JSON.stringify(out, null, 2) + '\n')
|
|
561
|
+
const accepted = consumed.gates.exactHead === 'pass'
|
|
562
|
+
if (accepted) console.log(`combine-and-verify: exact-head artifact accepted at ${consumed.integrationHeadSha}; integration remains ${computed.status}`)
|
|
563
|
+
else {
|
|
564
|
+
console.log('combine-and-verify: exact-head artifact rejected:')
|
|
565
|
+
consumed.exactHeadEvidence.errors.forEach((error) => console.log(` ✗ ${error}`))
|
|
566
|
+
}
|
|
567
|
+
return accepted ? 0 : 1
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if (f.cmd === 'verdict') {
|
|
571
|
+
if (!f.in) { console.error('--in <integration-verdict.json> is required'); return 2 }
|
|
572
|
+
const v = readJson(f.in)
|
|
573
|
+
const computed = computeIntegrationVerdict(v)
|
|
574
|
+
const out = { ...v, status: computed.status, reasons: computed.reasons }
|
|
575
|
+
if (f.out || f.in) fs.writeFileSync(f.out || f.in, JSON.stringify(out, null, 2) + '\n')
|
|
576
|
+
if (f.json) console.log(JSON.stringify(out, null, 2))
|
|
577
|
+
else {
|
|
578
|
+
console.log(`combine-and-verify: ${computed.ok ? '✅ ALIVE' : '❌ DEAD'} — combined tree ${v.combinedDiffRange}`)
|
|
579
|
+
const note = smokeHandoffNote(out)
|
|
580
|
+
if (note) console.log(` ⓘ ${note}`) // surface an N/A smoke decision, not bury it
|
|
581
|
+
computed.reasons.forEach((r) => console.log(` ✗ ${r}`))
|
|
582
|
+
}
|
|
583
|
+
return computed.ok ? 0 : 1
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
if (f.cmd === 'mergeable' || f.cmd === 'enqueue-eligible') {
|
|
587
|
+
if (!f.in || !f.manifest || !f.component || !f.pr) { console.error('mergeable needs --in <integration-verdict.json> --manifest <run-manifest.json> --component <id> --pr <ref> [--deps id:ref,…]'); return 2 }
|
|
588
|
+
const verdict = readJson(f.in)
|
|
589
|
+
const deps = (f.deps || '').split(',').map((s) => s.trim()).filter(Boolean).map((spec) => {
|
|
590
|
+
const [id, ref] = spec.split(':')
|
|
591
|
+
return { id, ref: ref || id }
|
|
592
|
+
})
|
|
593
|
+
// Authorization is READ from durable state, never asserted: mergeShippable in the
|
|
594
|
+
// run manifest is what pre-flight recorded (guarantee 4). Absent/unreadable
|
|
595
|
+
// manifest → not authorized (fail closed). A bare --authorized flag was the hole
|
|
596
|
+
// the review caught — it let a mistaken command satisfy the one precondition
|
|
597
|
+
// guarantee 4 exists to protect — so authorization has no CLI-flag source.
|
|
598
|
+
let authorized = false
|
|
599
|
+
let manifest = null
|
|
600
|
+
let deliveryMode = 'direct'
|
|
601
|
+
try {
|
|
602
|
+
manifest = readJson(f.manifest)
|
|
603
|
+
authorized = manifest.mergeShippable === true
|
|
604
|
+
deliveryMode = manifest.deliveryMode ?? 'direct'
|
|
605
|
+
} catch {
|
|
606
|
+
console.error(` ⚠ could not read --manifest ${f.manifest}; treating merge and candidate authorization as NOT satisfied`)
|
|
607
|
+
}
|
|
608
|
+
// Fetch so the ancestry checks run against the real remote head, not a local
|
|
609
|
+
// branch that fell behind during the (possibly hours-long) verify phase.
|
|
610
|
+
const target = branchName(verdict.targetBranch)
|
|
611
|
+
let targetRef = `origin/${target}`
|
|
612
|
+
if (!f.noFetch) {
|
|
613
|
+
try { execFileSync('git', ['fetch', 'origin', target], { stdio: 'ignore' }) }
|
|
614
|
+
catch { console.error(` ⚠ git fetch origin ${target} failed; FF/deps checks run against the last-known origin/${target}`) }
|
|
615
|
+
}
|
|
616
|
+
// If there is no origin/<target> at all (no remote), fall back to the local ref
|
|
617
|
+
// and say so rather than silently comparing against a ref that doesn't exist.
|
|
618
|
+
if (!isRef(targetRef)) { console.error(` ⚠ ${targetRef} does not exist; comparing against local ${target}`); targetRef = target }
|
|
619
|
+
const liveHeadSha = publishedPrHead(tryJson('gh', ['pr', 'view', String(f.pr), '--json', 'headRefOid']))
|
|
620
|
+
const candidate = candidateAuthorization(manifest, f.component, verdict, liveHeadSha)
|
|
621
|
+
const facts = gatherMergeFacts(verdict, { authorized, pr: f.pr, deps, targetRef })
|
|
622
|
+
facts.completionBlockedOn = unresolvedCompletionDependencies(manifest ?? {}, verdict, f.pr)
|
|
623
|
+
// #944: direct delivery owns target integration and therefore requires exact-tip
|
|
624
|
+
// freshness. A queue provider owns speculative integration: forward movement is safe
|
|
625
|
+
// when the recorded assembly base remains an ancestor of the live target. The frozen
|
|
626
|
+
// PR head stays immutable; queue-head CI proves compatibility with the current base.
|
|
627
|
+
let liveTargetSha = null
|
|
628
|
+
try { liveTargetSha = execFileSync('git', ['rev-parse', targetRef], { encoding: 'utf8' }).trim() } catch { /* fail open on tip read → no drift signal */ }
|
|
629
|
+
const componentRow = Array.isArray(manifest?.components)
|
|
630
|
+
? manifest.components.find((entry) => entry?.id === f.component)
|
|
631
|
+
: null
|
|
632
|
+
const assembleBaseIsAncestor = Boolean(componentRow?.baseShaAtAssemble && liveTargetSha)
|
|
633
|
+
&& isAncestor(componentRow.baseShaAtAssemble, liveTargetSha)
|
|
634
|
+
const drift = targetBaseDrift({
|
|
635
|
+
baseShaAtAssemble: componentRow?.baseShaAtAssemble,
|
|
636
|
+
baseShaAtEnqueue: componentRow?.baseShaAtEnqueue,
|
|
637
|
+
liveTargetSha,
|
|
638
|
+
deliveryMode,
|
|
639
|
+
assembleBaseIsAncestor,
|
|
640
|
+
})
|
|
641
|
+
const gate = f.cmd === 'enqueue-eligible' ? enqueueEligibility : mergePreconditions
|
|
642
|
+
const { ok, reasons } = gate({
|
|
643
|
+
...facts,
|
|
644
|
+
candidateAuthorized: candidate.authorized,
|
|
645
|
+
verdict,
|
|
646
|
+
targetDrifted: drift.drifted,
|
|
647
|
+
targetDriftReasons: drift.reasons,
|
|
648
|
+
})
|
|
649
|
+
reasons.push(...candidate.reasons.filter((reason) => !reasons.includes(reason)))
|
|
650
|
+
if (f.cmd === 'mergeable' && deliveryMode === 'queue') reasons.push('deliveryMode is queue — run enqueue-eligible, then request one provider enqueue; Mergify performs the merge')
|
|
651
|
+
if (f.cmd === 'enqueue-eligible' && deliveryMode !== 'queue') reasons.push(`deliveryMode is ${deliveryMode} — enqueue-eligible is only for queue delivery; run mergeable for direct delivery`)
|
|
652
|
+
// If this component reached ALIVE with smoke recorded not-applicable, surface that
|
|
653
|
+
// in the merge output so the merge-without-smoke is a recorded, visible decision
|
|
654
|
+
// (#393) — printed whether or not the merge is allowed, never buried.
|
|
655
|
+
const note = smokeHandoffNote(verdict)
|
|
656
|
+
if (note) console.log(` ⓘ ${note}`)
|
|
657
|
+
const label = f.cmd === 'enqueue-eligible' ? 'enqueue-eligible' : 'mergeable'
|
|
658
|
+
if (ok) console.log(`combine-and-verify: ✅ ${label} into ${target} — every precondition verified live against ${targetRef}`)
|
|
659
|
+
else { console.log(`combine-and-verify: ❌ NOT ${label}:`); reasons.forEach((r) => console.log(` ✗ ${r}`)) }
|
|
660
|
+
console.log(f.cmd === 'enqueue-eligible'
|
|
661
|
+
? ' (queue delivery: one authorized enqueue request only; Mergify performs the merge, never gh pr merge)'
|
|
662
|
+
: ' (direct delivery only: perform the merge with `gh pr merge`, never a forced push; production promotion stays resolve-release)')
|
|
663
|
+
return ok ? 0 : 1
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
if (f.cmd === 'queue-delivered') {
|
|
667
|
+
if (!f.in || !f.artifact || !f.sha || !f.target || !f.pr) { console.error('queue-delivered needs --in <integration-verdict.json> --artifact <queue-execution.json> --sha <merge-sha> --target <branch> --pr <n>'); return 2 }
|
|
668
|
+
const verdict = consumeQueueExecutionArtifact(readJson(f.in), readJson(f.artifact))
|
|
669
|
+
const delivered = runCli(['delivered', '--sha', f.sha, '--target', f.target, '--pr', f.pr, ...(f.noFetch ? ['--no-fetch'] : [])]) === 0
|
|
670
|
+
const completion = deliveryCompletion(verdict, { pr: Number(f.pr), mergedSha: f.sha, ancestry: delivered })
|
|
671
|
+
if (completion.ok) console.log('combine-and-verify: queue delivery complete — ancestry and synthetic queue evidence proved')
|
|
672
|
+
else { console.log('combine-and-verify: queue delivery evidence-blocked:'); completion.reasons.forEach((reason) => console.log(` ✗ ${reason}`)) }
|
|
673
|
+
return completion.ok ? 0 : 1
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// The POST-condition to `mergeable`'s pre-condition. `mergeable` proves the bar is met
|
|
677
|
+
// before the merge action; nothing proved the action landed. epic-986 performed its
|
|
678
|
+
// delivery action (`@mergifyio queue`), the PR did not merge, and the loop moved on —
|
|
679
|
+
// four units reported merge-ready with `mergedSha: null`. A merge tool's return code,
|
|
680
|
+
// a queue acknowledgement, and an auto-merge flag are all REQUESTS. Only ancestry in
|
|
681
|
+
// the freshly-fetched target branch is a result, so that is what this checks.
|
|
682
|
+
if (f.cmd === 'delivered') {
|
|
683
|
+
if (!f.sha || !f.target) { console.error('delivered needs --sha <merge-sha> --target <branch> [--pr <n>] [--no-fetch]'); return 2 }
|
|
684
|
+
// WHICH sha is checked is the whole correctness of this gate, and until now nothing
|
|
685
|
+
// enforced it. Ancestry of the PR's HEAD passes on a merge-commit queue (the head is
|
|
686
|
+
// an ancestor of the merge commit) and FAILS on a squash or rebase queue, where the
|
|
687
|
+
// queue writes new commits and the head lands nowhere. So the same honest caller gets
|
|
688
|
+
// "✅ delivered" on one repo and "❌ NOT delivered" for a genuinely merged PR on the
|
|
689
|
+
// next — and the loop hands back landed work. `--pr` closes it by taking the SHA from
|
|
690
|
+
// the PR's own merge record instead of trusting the caller's.
|
|
691
|
+
if (f.pr) {
|
|
692
|
+
const view = tryJson('gh', ['pr', 'view', String(f.pr), '--json', 'state,mergeCommit,headRefOid'])
|
|
693
|
+
if (!view) console.error(` ⚠ could not read PR #${f.pr} — falling back to the --sha given; verify it is the MERGE commit, not the branch head`)
|
|
694
|
+
else {
|
|
695
|
+
const verdict = deliveredShaCheck(f.sha, view)
|
|
696
|
+
if (!verdict.ok) { console.log(`combine-and-verify: ❌ ${verdict.reason}`); return 1 }
|
|
697
|
+
if (verdict.note) console.error(` ⚠ ${verdict.note}`)
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
const target = branchName(f.target)
|
|
701
|
+
let targetRef = `origin/${target}`
|
|
702
|
+
if (!f.noFetch) {
|
|
703
|
+
try { execFileSync('git', ['fetch', 'origin', target], { stdio: 'ignore' }) }
|
|
704
|
+
catch { console.error(` ⚠ git fetch origin ${target} failed; containment is checked against the last-known origin/${target}`) }
|
|
705
|
+
}
|
|
706
|
+
if (!isRef(targetRef)) { console.error(` ⚠ ${targetRef} does not exist; comparing against local ${target}`); targetRef = target }
|
|
707
|
+
const landed = isAncestor(f.sha, targetRef)
|
|
708
|
+
if (landed) console.log(`combine-and-verify: ✅ delivered — ${f.sha} is contained in ${targetRef}`)
|
|
709
|
+
else {
|
|
710
|
+
console.log(`combine-and-verify: ❌ NOT delivered — ${f.sha} is not contained in ${targetRef}`)
|
|
711
|
+
console.log(' The delivery action was performed but the commit did not land. Record the attempt with its')
|
|
712
|
+
console.log(' outcome (run-state record-delivery-attempt), not a terminal "merged" — a request is not a result.')
|
|
713
|
+
}
|
|
714
|
+
return landed ? 0 : 1
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
console.error(`unknown command: ${f.cmd}`)
|
|
718
|
+
return 2
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
if (isMainModule(import.meta.url)) process.exit(runCli(process.argv.slice(2)))
|