@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,131 @@
|
|
|
1
|
+
// Shared read-only validator for exact-head CI artifacts. The mutating adapter lives
|
|
2
|
+
// in resolve-issues; consumers import this contract so review and integration use the
|
|
3
|
+
// same fail-closed definition of valid evidence.
|
|
4
|
+
import { locallyReproducedJobs, validatePublishOnlyEvidence, validateRequiredCheckExecution, validateRequiredCheckPlan } from './required-check-plan.mjs'
|
|
5
|
+
|
|
6
|
+
export const EXACT_HEAD_ARTIFACT_VERSION = 2
|
|
7
|
+
export const EXACT_HEAD_COLLECTION_MODES = Object.freeze(['choreographed', 'observed-existing-run'])
|
|
8
|
+
|
|
9
|
+
function isObject(value) {
|
|
10
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function stable(value) {
|
|
14
|
+
if (Array.isArray(value)) return value.map(stable)
|
|
15
|
+
if (!isObject(value)) return value
|
|
16
|
+
return Object.fromEntries(Object.keys(value).sort().map((key) => [key, stable(value[key])]))
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function sameExactHeadState(left, right) {
|
|
20
|
+
return JSON.stringify(stable(left)) === JSON.stringify(stable(right))
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function providerWorkflowIdentityMatches(expected, identity) {
|
|
24
|
+
if (typeof expected !== 'string' || !expected.trim() || !isObject(identity)) return false
|
|
25
|
+
const aliases = [identity.id, identity.name, identity.path]
|
|
26
|
+
.filter((value) => (typeof value === 'string' && value.trim()) || typeof value === 'number')
|
|
27
|
+
.map(String)
|
|
28
|
+
if (typeof identity.path === 'string') aliases.push(identity.path.split('/').at(-1))
|
|
29
|
+
return aliases.includes(expected)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function validateProviderWorkflowIdentity(identity) {
|
|
33
|
+
const errors = []
|
|
34
|
+
if (!isObject(identity)) return ['artifact.workflow.provider must preserve provider-issued workflow identity']
|
|
35
|
+
if ((typeof identity.id !== 'string' && typeof identity.id !== 'number') || !String(identity.id).trim()) errors.push('artifact.workflow.provider.id must identify the provider workflow')
|
|
36
|
+
if (typeof identity.name !== 'string' || !identity.name.trim()) errors.push('artifact.workflow.provider.name must preserve the provider workflow name')
|
|
37
|
+
if (identity.path !== undefined && (typeof identity.path !== 'string' || !identity.path.trim())) errors.push('artifact.workflow.provider.path must be a non-empty string when present')
|
|
38
|
+
return errors
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function classifyRawJobs(jobs, policy) {
|
|
42
|
+
const errors = []
|
|
43
|
+
if (!Array.isArray(jobs)) return { outcome: 'failure', errors: ['artifact.jobs must be an array'] }
|
|
44
|
+
if (!isObject(policy) || !Array.isArray(policy.requiredJobs) || !Array.isArray(policy.allowedSkippedJobs)) {
|
|
45
|
+
return { outcome: 'failure', errors: ['artifact.policy must preserve requiredJobs and allowedSkippedJobs arrays'] }
|
|
46
|
+
}
|
|
47
|
+
const byName = new Map()
|
|
48
|
+
for (const [index, job] of jobs.entries()) {
|
|
49
|
+
if (!isObject(job) || typeof job.name !== 'string' || !Object.hasOwn(job, 'conclusion')) {
|
|
50
|
+
errors.push(`artifact.jobs[${index}] must preserve name and conclusion`)
|
|
51
|
+
continue
|
|
52
|
+
}
|
|
53
|
+
if (byName.has(job.name)) errors.push(`artifact.jobs has duplicate/ambiguous evidence for ${job.name}`)
|
|
54
|
+
else byName.set(job.name, job)
|
|
55
|
+
}
|
|
56
|
+
const missing = policy.requiredJobs.filter((name) => !byName.has(name))
|
|
57
|
+
const notSuccessful = policy.requiredJobs.filter((name) => byName.has(name) && byName.get(name).conclusion !== 'success')
|
|
58
|
+
const allowedSkipped = new Set(policy.allowedSkippedJobs)
|
|
59
|
+
const disallowedSkipped = jobs.filter((job) => job?.conclusion === 'skipped' && !allowedSkipped.has(job.name)).map((job) => job.name)
|
|
60
|
+
const failed = jobs.filter((job) => job && !['success', 'skipped'].includes(job.conclusion)).map((job) => `${job.name} (${job.conclusion ?? 'pending'})`)
|
|
61
|
+
if (missing.length) errors.push(`required jobs missing: ${missing.join(', ')}`)
|
|
62
|
+
if (notSuccessful.length) errors.push(`required jobs not successful: ${notSuccessful.join(', ')}`)
|
|
63
|
+
if (disallowedSkipped.length) errors.push(`jobs skipped without policy allowance: ${disallowedSkipped.join(', ')}`)
|
|
64
|
+
if (failed.length) errors.push(`raw jobs contain failure states: ${failed.join(', ')}`)
|
|
65
|
+
let outcome = 'success'
|
|
66
|
+
if (jobs.length === 0) outcome = 'zero-job-workflow-failure'
|
|
67
|
+
else if (jobs.some((job) => job?.conclusion === 'cancelled')) outcome = 'cancelled'
|
|
68
|
+
else if (errors.length) outcome = 'failure'
|
|
69
|
+
else if (jobs.some((job) => job?.conclusion === 'skipped')) outcome = 'success-with-skipped'
|
|
70
|
+
return { outcome, errors }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function validateExactHeadArtifact(artifact, { expectedHeadSha } = {}) {
|
|
74
|
+
const errors = []
|
|
75
|
+
if (!isObject(artifact)) return { ok: false, errors: ['exact-head artifact must be an object'] }
|
|
76
|
+
if (artifact.version !== EXACT_HEAD_ARTIFACT_VERSION) errors.push(`unsupported exact-head artifact version ${artifact.version}`)
|
|
77
|
+
if (artifact.kind !== 'exact-head-ci') errors.push('exact-head artifact kind must be "exact-head-ci"')
|
|
78
|
+
if (!EXACT_HEAD_COLLECTION_MODES.includes(artifact.collectionMode)) errors.push(`artifact.collectionMode must be one of ${EXACT_HEAD_COLLECTION_MODES.join('|')}`)
|
|
79
|
+
if (typeof artifact.repository !== 'string' || !/^[^/\s]+\/[^/\s]+$/.test(artifact.repository)) errors.push('artifact.repository must be owner/name')
|
|
80
|
+
if (!Number.isInteger(artifact.pr) || artifact.pr < 1) errors.push('artifact.pr must be a positive integer')
|
|
81
|
+
if (typeof artifact.headSha !== 'string' || !/^[0-9a-f]{40}$/i.test(artifact.headSha)) errors.push('artifact.headSha must be a full 40-character commit SHA')
|
|
82
|
+
if (expectedHeadSha && artifact.headSha !== expectedHeadSha) errors.push(`exact-head artifact is STALE: stamped ${artifact.headSha}, expected ${expectedHeadSha}`)
|
|
83
|
+
const plan = validateRequiredCheckPlan(artifact.requiredCheckPlan, { expectedHeadSha: artifact.headSha })
|
|
84
|
+
errors.push(...plan.errors)
|
|
85
|
+
if (artifact.requiredCheckPlan?.repository !== artifact.repository) errors.push('requiredCheckPlan repository does not match artifact.repository')
|
|
86
|
+
const execution = validateRequiredCheckExecution(artifact.requiredCheckPlan, artifact.jobs, { expectedHeadSha: artifact.headSha })
|
|
87
|
+
errors.push(...execution.errors.filter((error) => !plan.errors.includes(error)))
|
|
88
|
+
const publishOnly = validatePublishOnlyEvidence(artifact.requiredCheckPlan, artifact.publishOnlyEvidence, { expectedHeadSha: artifact.headSha, expectedRepository: artifact.repository, expectedPr: artifact.pr })
|
|
89
|
+
errors.push(...publishOnly.errors)
|
|
90
|
+
if (!isObject(artifact.workflow) || typeof artifact.workflow.id !== 'string' || !artifact.workflow.id.trim()) errors.push('artifact.workflow.id must identify the requested provider workflow')
|
|
91
|
+
errors.push(...validateProviderWorkflowIdentity(artifact.workflow?.provider))
|
|
92
|
+
if (isObject(artifact.workflow?.provider) && !providerWorkflowIdentityMatches(artifact.workflow?.id, artifact.workflow.provider)) errors.push('provider workflow identity does not match artifact.workflow.id')
|
|
93
|
+
if (typeof artifact.workflow?.runId !== 'string' && typeof artifact.workflow?.runId !== 'number') errors.push('artifact.workflow.runId must identify the provider run')
|
|
94
|
+
if (artifact.workflow?.headSha !== artifact.headSha) errors.push('workflow headSha does not match artifact headSha')
|
|
95
|
+
if (!Array.isArray(artifact.jobs)) errors.push('artifact.jobs must be an array')
|
|
96
|
+
else for (const [index, job] of artifact.jobs.entries()) {
|
|
97
|
+
if (!isObject(job) || typeof job.name !== 'string' || !Object.hasOwn(job, 'conclusion')) errors.push(`artifact.jobs[${index}] must preserve name and conclusion`)
|
|
98
|
+
}
|
|
99
|
+
const expectedPolicy = {
|
|
100
|
+
requiredJobs: locallyReproducedJobs(artifact.requiredCheckPlan),
|
|
101
|
+
allowedSkippedJobs: artifact.requiredCheckPlan?.allowedSkippedJobs,
|
|
102
|
+
}
|
|
103
|
+
if (!sameExactHeadState(artifact.policy, expectedPolicy)) errors.push('artifact.policy must be derived exactly from requiredCheckPlan')
|
|
104
|
+
const raw = classifyRawJobs(artifact.jobs, artifact.policy)
|
|
105
|
+
errors.push(...raw.errors)
|
|
106
|
+
if (artifact.summary?.outcome !== raw.outcome) errors.push(`summary outcome "${artifact.summary?.outcome ?? 'missing'}" contradicts raw jobs outcome "${raw.outcome}"`)
|
|
107
|
+
|
|
108
|
+
if (artifact.collectionMode === 'choreographed') {
|
|
109
|
+
if (!Array.isArray(artifact.choreography) || artifact.choreography.length === 0) errors.push('choreographed artifacts must preserve the mutation choreography')
|
|
110
|
+
if (Object.hasOwn(artifact, 'observation')) errors.push('choreographed artifacts cannot claim observed-existing-run provenance')
|
|
111
|
+
if (artifact.restoration?.proved !== true) errors.push('live restoration was not proved')
|
|
112
|
+
if (!sameExactHeadState(artifact.snapshot, artifact.restoration?.observed)) errors.push('restoration observation does not equal the original snapshot')
|
|
113
|
+
}
|
|
114
|
+
if (artifact.collectionMode === 'observed-existing-run') {
|
|
115
|
+
if (Object.hasOwn(artifact, 'choreography')) errors.push('observed-existing-run artifacts cannot claim mutation choreography')
|
|
116
|
+
if (Object.hasOwn(artifact, 'restoration')) errors.push('observed-existing-run artifacts cannot contain a restoration claim')
|
|
117
|
+
if (Object.hasOwn(artifact, 'snapshot')) errors.push('observed-existing-run artifacts must use before/after observation provenance, not a choreography snapshot')
|
|
118
|
+
if (!isObject(artifact.observation) || !isObject(artifact.observation.before) || !isObject(artifact.observation.after)) {
|
|
119
|
+
errors.push('observed-existing-run artifacts require live PR observations before and after provider evidence acquisition')
|
|
120
|
+
} else {
|
|
121
|
+
if (artifact.observation.before.headSha !== artifact.headSha) errors.push('observed-existing-run before observation does not match artifact.headSha')
|
|
122
|
+
if (artifact.observation.after.headSha !== artifact.headSha) errors.push('observed-existing-run after observation does not match artifact.headSha')
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (!Array.isArray(artifact.errors)) errors.push('artifact.errors must be an array')
|
|
126
|
+
else if (artifact.errors.length) errors.push('passing exact-head artifacts cannot preserve collection errors')
|
|
127
|
+
if (artifact.status !== 'pass') errors.push(`exact-head status is "${artifact.status ?? 'missing'}", needs "pass"`)
|
|
128
|
+
if (!['success', 'success-with-skipped'].includes(artifact.summary?.outcome)) errors.push(`exact-head outcome is "${artifact.summary?.outcome ?? 'missing'}", not a passing outcome`)
|
|
129
|
+
if (!['success', 'success-with-skipped'].includes(raw.outcome)) errors.push(`raw jobs outcome "${raw.outcome}" is not passing`)
|
|
130
|
+
return { ok: errors.length === 0, errors }
|
|
131
|
+
}
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
// head-check-set — the FULL observed state of one commit, not the named subset of it.
|
|
2
|
+
//
|
|
3
|
+
// A required-check plan answers "are the checks I named green?". #1081 is what that
|
|
4
|
+
// question cannot see. Two release workflows were rejected by GitHub at validation
|
|
5
|
+
// time; every push produced two runs that completed in the same second having executed
|
|
6
|
+
// ZERO jobs, named after the file path instead of the declared `name:`. That was
|
|
7
|
+
// visible for seven hours across fourteen commits, and the unit still earned an
|
|
8
|
+
// independent PASS and a merge-ready mark, because the one check anybody read
|
|
9
|
+
// (`skill-script-tests`) was green — and it genuinely was.
|
|
10
|
+
//
|
|
11
|
+
// THE SUBTLETY THAT DEFEATS THE OBVIOUS FIX. "Read every check-run instead of the
|
|
12
|
+
// named ones" would ALSO have missed #1081. A workflow GitHub cannot parse produces no
|
|
13
|
+
// check-run at all — not a red one, none. The real observation at that SHA:
|
|
14
|
+
//
|
|
15
|
+
// GET /commits/ee5dd91a…/check-runs → 4 entries, every one success/neutral/skipped
|
|
16
|
+
// GET /commits/ee5dd91a…/status → state "pending", zero statuses
|
|
17
|
+
// GET /actions/runs?head_sha=ee5dd91a… → 3 runs, TWO of them conclusion "failure"
|
|
18
|
+
// with name == path and zero jobs
|
|
19
|
+
//
|
|
20
|
+
// So the full state of a commit is the union of three channels — check-runs, legacy
|
|
21
|
+
// commit statuses, and Actions runs — and the third is the only one where a startup
|
|
22
|
+
// failure lives. This module classifies that union. Both halves are load-bearing: the
|
|
23
|
+
// union is what makes undeclared red visible, and the zero-job run is what makes the
|
|
24
|
+
// #1081 class NAMEABLE rather than a number in a count.
|
|
25
|
+
//
|
|
26
|
+
// WHAT THIS DELIBERATELY DOES NOT DO. It does not gate. Whether a red check nobody
|
|
27
|
+
// declared required should block a merge is a policy decision belonging to the human
|
|
28
|
+
// and to `.mergify.yml`, and a reporting script that quietly changed what merges would
|
|
29
|
+
// be a worse defect than the one it fixes. The partition names `undeclaredRed`
|
|
30
|
+
// separately from `requiredRed` precisely so a caller can act on the difference; the
|
|
31
|
+
// only outcome this module calls failing is the one the existing contract already
|
|
32
|
+
// blocks on — a REQUIRED check red or absent.
|
|
33
|
+
|
|
34
|
+
export const HEAD_CHECK_SET_VERSION = 1
|
|
35
|
+
export const HEAD_CHECK_SET_KIND = 'head-check-set'
|
|
36
|
+
export const OBSERVATION_SOURCES = Object.freeze(['check-run', 'commit-status', 'actions-run'])
|
|
37
|
+
export const OBSERVATION_STATES = Object.freeze(['green', 'red', 'neutral', 'pending'])
|
|
38
|
+
|
|
39
|
+
// Why `cancelled` counts as red: a cancelled required check is not a passed one, and
|
|
40
|
+
// the surrounding contract has said "skipped is never success" since epic-892. Neutral
|
|
41
|
+
// and skipped stay `neutral` — they are not passes either, but they are not evidence of
|
|
42
|
+
// breakage, and conflating the two is how a report gets ignored.
|
|
43
|
+
const RED_CONCLUSIONS = new Set(['failure', 'timed_out', 'action_required', 'startup_failure', 'stale', 'cancelled'])
|
|
44
|
+
const NEUTRAL_CONCLUSIONS = new Set(['skipped', 'neutral'])
|
|
45
|
+
const COMPLETED = new Set(['completed'])
|
|
46
|
+
|
|
47
|
+
// A run GitHub could not parse is reported with its file path where its name belongs,
|
|
48
|
+
// because there is no parsed `name:` to report. Corroborating, never sufficient: a
|
|
49
|
+
// workflow may legitimately declare no `name:`, in which case GitHub also uses the path.
|
|
50
|
+
const WORKFLOW_PATH = /^\.github\/workflows\/[^/]+\.ya?ml$/
|
|
51
|
+
|
|
52
|
+
const isObject = (value) => value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
53
|
+
const nonEmpty = (value) => typeof value === 'string' && value.trim().length > 0
|
|
54
|
+
const SHA = /^[0-9a-f]{40}$/i
|
|
55
|
+
const lower = (value) => (value == null ? null : String(value).toLowerCase())
|
|
56
|
+
|
|
57
|
+
function stateOf(status, conclusion) {
|
|
58
|
+
const normalizedStatus = lower(status)
|
|
59
|
+
const normalizedConclusion = lower(conclusion)
|
|
60
|
+
if (normalizedStatus !== null && !COMPLETED.has(normalizedStatus)) return 'pending'
|
|
61
|
+
if (normalizedConclusion === null) return 'pending'
|
|
62
|
+
if (normalizedConclusion === 'success') return 'green'
|
|
63
|
+
if (NEUTRAL_CONCLUSIONS.has(normalizedConclusion)) return 'neutral'
|
|
64
|
+
if (RED_CONCLUSIONS.has(normalizedConclusion)) return 'red'
|
|
65
|
+
// Deny-by-default on a conclusion this module has never seen: an unknown conclusion
|
|
66
|
+
// read as green is exactly the failure this file exists to remove.
|
|
67
|
+
return 'red'
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function commitStatusState(state) {
|
|
71
|
+
const normalized = lower(state)
|
|
72
|
+
if (normalized === 'success') return 'green'
|
|
73
|
+
if (normalized === 'pending') return 'pending'
|
|
74
|
+
return 'red'
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Every check name the plan declares required, in any phase. */
|
|
78
|
+
export function requiredCheckNames(plan) {
|
|
79
|
+
const fromChecks = (plan?.checks || [])
|
|
80
|
+
.filter((check) => check?.admission === 'required' && nonEmpty(check?.name))
|
|
81
|
+
.map((check) => check.name)
|
|
82
|
+
const fromDiscovery = (plan?.discovery?.requiredChecks || []).filter(nonEmpty)
|
|
83
|
+
return [...new Set([...fromChecks, ...fromDiscovery])].sort()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function checkRunObservation(run) {
|
|
87
|
+
return {
|
|
88
|
+
source: 'check-run',
|
|
89
|
+
name: String(run?.name ?? ''),
|
|
90
|
+
state: stateOf(run?.status, run?.conclusion),
|
|
91
|
+
status: run?.status ?? null,
|
|
92
|
+
conclusion: run?.conclusion ?? null,
|
|
93
|
+
app: run?.app?.slug ?? null,
|
|
94
|
+
checkSuiteId: run?.check_suite?.id ?? null,
|
|
95
|
+
url: run?.html_url ?? null,
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function commitStatusObservation(status) {
|
|
100
|
+
return {
|
|
101
|
+
source: 'commit-status',
|
|
102
|
+
name: String(status?.context ?? ''),
|
|
103
|
+
state: commitStatusState(status?.state),
|
|
104
|
+
status: 'completed',
|
|
105
|
+
conclusion: status?.state ?? null,
|
|
106
|
+
app: status?.creator?.login ?? null,
|
|
107
|
+
checkSuiteId: null,
|
|
108
|
+
url: status?.target_url ?? null,
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* An Actions run, plus the two facts that make #1081 nameable.
|
|
114
|
+
*
|
|
115
|
+
* `jobCount` is supplied by the caller because it costs one extra API call per run and
|
|
116
|
+
* this module is pure. A run that has completed with `jobCount === 0` executed nothing:
|
|
117
|
+
* that is the startup-failure signature, and it is the whole reason the Actions channel
|
|
118
|
+
* is read at all. `null` means the caller could not determine it — reported as unknown,
|
|
119
|
+
* never as zero, because "we did not look" and "there were none" must not render alike.
|
|
120
|
+
*/
|
|
121
|
+
function actionsRunObservation(run) {
|
|
122
|
+
const name = String(run?.name ?? '')
|
|
123
|
+
const path = run?.path ?? null
|
|
124
|
+
const jobCount = Number.isInteger(run?.jobCount) ? run.jobCount : null
|
|
125
|
+
const state = stateOf(run?.status, run?.conclusion)
|
|
126
|
+
const completed = COMPLETED.has(lower(run?.status) ?? '')
|
|
127
|
+
return {
|
|
128
|
+
source: 'actions-run',
|
|
129
|
+
name,
|
|
130
|
+
state,
|
|
131
|
+
status: run?.status ?? null,
|
|
132
|
+
conclusion: run?.conclusion ?? null,
|
|
133
|
+
app: 'github-actions',
|
|
134
|
+
checkSuiteId: run?.check_suite_id ?? null,
|
|
135
|
+
url: run?.html_url ?? null,
|
|
136
|
+
runId: run?.id ?? null,
|
|
137
|
+
path,
|
|
138
|
+
event: run?.event ?? null,
|
|
139
|
+
jobCount,
|
|
140
|
+
// Recorded, never classified on. The obvious reading of "is this run attached to the
|
|
141
|
+
// pull request?" is the API's `pull_requests` array, and it is not usable: at
|
|
142
|
+
// ee5dd91a the perfectly healthy `pull_request`-event `Skill gates` run also carries
|
|
143
|
+
// `pull_requests: []`. Deriving an attachment boolean from it would report every
|
|
144
|
+
// healthy run as unattached. `event` is what actually distinguishes an out-of-band
|
|
145
|
+
// dispatch, and it is what `absenceReason` uses.
|
|
146
|
+
pullRequests: Array.isArray(run?.pull_requests) ? run.pull_requests.map((entry) => entry?.number ?? null) : [],
|
|
147
|
+
nameEqualsPath: nonEmpty(path) && name === path,
|
|
148
|
+
looksLikeWorkflowPath: WORKFLOW_PATH.test(name),
|
|
149
|
+
zeroJobs: completed && jobCount === 0,
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Raw Actions runs (each optionally carrying an integer `jobCount`) as observations. */
|
|
154
|
+
export function observeActionsRuns(runs) {
|
|
155
|
+
return (Array.isArray(runs) ? runs : []).map(actionsRunObservation)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* The #1081 signature: a run that COMPLETED, executed zero jobs, and did not succeed.
|
|
160
|
+
*
|
|
161
|
+
* Kept distinct from "a red run" on purpose. A red run with jobs failed while doing its
|
|
162
|
+
* work and its jobs say why; a red run with no jobs never started, and nothing else in
|
|
163
|
+
* the system says so — no check-run exists to be red. The name-equals-path corroboration
|
|
164
|
+
* is reported alongside rather than required, because a workflow with no declared `name:`
|
|
165
|
+
* produces the same coincidence honestly.
|
|
166
|
+
*/
|
|
167
|
+
export function startupFailures(observations) {
|
|
168
|
+
return observations
|
|
169
|
+
.filter((entry) => entry.source === 'actions-run' && entry.zeroJobs && entry.state !== 'green')
|
|
170
|
+
.map((entry) => ({
|
|
171
|
+
runId: entry.runId,
|
|
172
|
+
name: entry.name,
|
|
173
|
+
path: entry.path,
|
|
174
|
+
event: entry.event,
|
|
175
|
+
conclusion: entry.conclusion,
|
|
176
|
+
state: entry.state,
|
|
177
|
+
nameEqualsPath: entry.nameEqualsPath,
|
|
178
|
+
looksLikeWorkflowPath: entry.looksLikeWorkflowPath,
|
|
179
|
+
url: entry.url,
|
|
180
|
+
}))
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Why a required check produced no observation at all. This is #1058's distinction and
|
|
185
|
+
* it falls out of the same union: the three answers call for opposite responses, and the
|
|
186
|
+
* single string they share today ("no workflow run found") reads as *still queued* for
|
|
187
|
+
* all three.
|
|
188
|
+
*/
|
|
189
|
+
export function ciAbsence(observations) {
|
|
190
|
+
const actionsRuns = (observations || []).filter((entry) => entry.source === 'actions-run')
|
|
191
|
+
return absenceReason(observations || [], actionsRuns)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function absenceReason(observations, actionsRuns) {
|
|
195
|
+
if (observations.some((entry) => entry.state === 'pending')) {
|
|
196
|
+
return {
|
|
197
|
+
reason: 'pending',
|
|
198
|
+
detail: 'a run for this SHA has not finished; waiting is correct and dispatching another would waste a run',
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (observations.length === 0) {
|
|
202
|
+
return {
|
|
203
|
+
reason: 'no-run-anywhere',
|
|
204
|
+
detail: 'no check-run, commit status or Actions run exists for this SHA — the triggering event was most likely never delivered; waiting is futile, a workflow_dispatch or a close/reopen at the same head is the intervention',
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
// #1058's third state: a run exists at this SHA, but it got there by hand. The check
|
|
208
|
+
// it produced is honest exact-head evidence and satisfies no merge-queue condition,
|
|
209
|
+
// and the situation is actionable NOW rather than something to wait out.
|
|
210
|
+
const dispatched = actionsRuns.filter((entry) => entry.event === 'workflow_dispatch')
|
|
211
|
+
if (dispatched.length && dispatched.length === actionsRuns.length) {
|
|
212
|
+
return {
|
|
213
|
+
reason: 'dispatched-out-of-band',
|
|
214
|
+
detail: `every Actions run at this SHA was started by workflow_dispatch (${dispatched.map((entry) => entry.runId).join(', ')}) — the event that would have created this check was never delivered; the dispatched run is honest exact-head evidence but satisfies no merge-queue check condition, so delivery still needs the event`,
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
reason: 'absent-though-runs-completed',
|
|
219
|
+
detail: 'runs for this SHA have completed without ever producing this check — inspect the workflow trigger and path filters, and the startupFailures channel: a workflow GitHub refuses to parse creates a run and produces no check-run at all',
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Classify the full observed state of one head SHA.
|
|
225
|
+
*
|
|
226
|
+
* @param {object} input
|
|
227
|
+
* @param {string} input.repository owner/name
|
|
228
|
+
* @param {string} input.headSha the 40-character commit being described
|
|
229
|
+
* @param {object[]} [input.checkRuns] raw `GET /commits/{sha}/check-runs` entries
|
|
230
|
+
* @param {object[]} [input.statuses] raw `GET /commits/{sha}/status` `.statuses` entries
|
|
231
|
+
* @param {object[]} [input.actionsRuns] raw `GET /actions/runs?head_sha=` entries, each
|
|
232
|
+
* optionally carrying an integer `jobCount`
|
|
233
|
+
* @param {object|null} [input.plan] a required-check plan; OPTIONAL — with none, nothing
|
|
234
|
+
* is required and this degrades to a pure observation
|
|
235
|
+
* report rather than calling every red undeclared
|
|
236
|
+
* @param {string} [input.collectedAt]
|
|
237
|
+
*/
|
|
238
|
+
export function classifyHeadCheckSet({
|
|
239
|
+
repository,
|
|
240
|
+
headSha,
|
|
241
|
+
checkRuns = [],
|
|
242
|
+
statuses = [],
|
|
243
|
+
actionsRuns = [],
|
|
244
|
+
plan = null,
|
|
245
|
+
collectedAt = new Date().toISOString(),
|
|
246
|
+
}) {
|
|
247
|
+
const observations = [
|
|
248
|
+
...checkRuns.map(checkRunObservation),
|
|
249
|
+
...statuses.map(commitStatusObservation),
|
|
250
|
+
...actionsRuns.map(actionsRunObservation),
|
|
251
|
+
]
|
|
252
|
+
const required = requiredCheckNames(plan)
|
|
253
|
+
const requiredSet = new Set(required)
|
|
254
|
+
for (const entry of observations) entry.required = requiredSet.has(entry.name)
|
|
255
|
+
|
|
256
|
+
const observedNames = new Set(observations.map((entry) => entry.name))
|
|
257
|
+
const actionsObservations = observations.filter((entry) => entry.source === 'actions-run')
|
|
258
|
+
|
|
259
|
+
const red = observations.filter((entry) => entry.state === 'red')
|
|
260
|
+
const partition = {
|
|
261
|
+
requiredRed: red.filter((entry) => entry.required),
|
|
262
|
+
// The #1081 case. Informational by contract — see this file's header — but never silent.
|
|
263
|
+
undeclaredRed: red.filter((entry) => !entry.required),
|
|
264
|
+
neverRan: required
|
|
265
|
+
.filter((name) => !observedNames.has(name))
|
|
266
|
+
.map((name) => ({ name, ...absenceReason(observations, actionsObservations) })),
|
|
267
|
+
pending: observations.filter((entry) => entry.state === 'pending'),
|
|
268
|
+
neutral: observations.filter((entry) => entry.state === 'neutral'),
|
|
269
|
+
green: observations.filter((entry) => entry.state === 'green'),
|
|
270
|
+
}
|
|
271
|
+
const signatures = { startupFailures: startupFailures(observations) }
|
|
272
|
+
|
|
273
|
+
// Outcome ordering is the caller's decision order, and `green-with-undeclared-red` is
|
|
274
|
+
// deliberately its own value rather than folded into either neighbour: it is the state
|
|
275
|
+
// #1081 was in for seven hours, and it must be neither "failure" (it does not block)
|
|
276
|
+
// nor "green" (it is not).
|
|
277
|
+
let outcome = 'green'
|
|
278
|
+
if (partition.requiredRed.length) outcome = 'required-red'
|
|
279
|
+
else if (partition.neverRan.length) outcome = 'required-missing'
|
|
280
|
+
else if (partition.pending.length) outcome = 'pending'
|
|
281
|
+
else if (partition.undeclaredRed.length || signatures.startupFailures.length) outcome = 'green-with-undeclared-red'
|
|
282
|
+
|
|
283
|
+
return {
|
|
284
|
+
version: HEAD_CHECK_SET_VERSION,
|
|
285
|
+
kind: HEAD_CHECK_SET_KIND,
|
|
286
|
+
repository,
|
|
287
|
+
headSha,
|
|
288
|
+
collectedAt,
|
|
289
|
+
planned: { required, planPresent: plan != null },
|
|
290
|
+
observations,
|
|
291
|
+
signatures,
|
|
292
|
+
partition,
|
|
293
|
+
summary: {
|
|
294
|
+
outcome,
|
|
295
|
+
counts: {
|
|
296
|
+
observations: observations.length,
|
|
297
|
+
checkRuns: checkRuns.length,
|
|
298
|
+
commitStatuses: statuses.length,
|
|
299
|
+
actionsRuns: actionsRuns.length,
|
|
300
|
+
requiredRed: partition.requiredRed.length,
|
|
301
|
+
undeclaredRed: partition.undeclaredRed.length,
|
|
302
|
+
neverRan: partition.neverRan.length,
|
|
303
|
+
pending: partition.pending.length,
|
|
304
|
+
startupFailures: signatures.startupFailures.length,
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* True when the REQUIRED set — and only the required set — is unsatisfied.
|
|
312
|
+
*
|
|
313
|
+
* This is the whole of the blocking judgement, isolated here so that the one place a
|
|
314
|
+
* caller could turn undeclared red into a merge decision is a single named function
|
|
315
|
+
* rather than an exit-code expression buried in a CLI.
|
|
316
|
+
*/
|
|
317
|
+
export function blocksOnRequiredSet(artifact) {
|
|
318
|
+
return (artifact?.partition?.requiredRed?.length ?? 0) > 0
|
|
319
|
+
|| (artifact?.partition?.neverRan?.length ?? 0) > 0
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export function validateHeadCheckSetArtifact(artifact, { expectedHeadSha, label = 'headCheckSet' } = {}) {
|
|
323
|
+
const errors = []
|
|
324
|
+
if (!isObject(artifact)) return { ok: false, errors: [`${label} must be an object`] }
|
|
325
|
+
if (artifact.version !== HEAD_CHECK_SET_VERSION) errors.push(`${label}.version must be ${HEAD_CHECK_SET_VERSION}`)
|
|
326
|
+
if (artifact.kind !== HEAD_CHECK_SET_KIND) errors.push(`${label}.kind must be "${HEAD_CHECK_SET_KIND}"`)
|
|
327
|
+
if (!nonEmpty(artifact.repository) || !/^[^/\s]+\/[^/\s]+$/.test(artifact.repository)) errors.push(`${label}.repository must be owner/name`)
|
|
328
|
+
if (!nonEmpty(artifact.headSha) || !SHA.test(artifact.headSha)) errors.push(`${label}.headSha must be a full 40-character commit SHA`)
|
|
329
|
+
else if (expectedHeadSha && artifact.headSha !== expectedHeadSha) errors.push(`${label} is STALE: stamped ${artifact.headSha}, expected ${expectedHeadSha}`)
|
|
330
|
+
if (!Array.isArray(artifact.observations)) errors.push(`${label}.observations must be an array`)
|
|
331
|
+
else {
|
|
332
|
+
for (const [index, entry] of artifact.observations.entries()) {
|
|
333
|
+
const at = `${label}.observations[${index}]`
|
|
334
|
+
if (!isObject(entry)) { errors.push(`${at} must be an object`); continue }
|
|
335
|
+
if (!OBSERVATION_SOURCES.includes(entry.source)) errors.push(`${at}.source must be ${OBSERVATION_SOURCES.join('|')}`)
|
|
336
|
+
if (!nonEmpty(entry.name)) errors.push(`${at}.name must be a non-empty string`)
|
|
337
|
+
if (!OBSERVATION_STATES.includes(entry.state)) errors.push(`${at}.state must be ${OBSERVATION_STATES.join('|')}`)
|
|
338
|
+
if (typeof entry.required !== 'boolean') errors.push(`${at}.required must be boolean`)
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
if (!isObject(artifact.partition)) errors.push(`${label}.partition must be an object`)
|
|
342
|
+
else {
|
|
343
|
+
for (const key of ['requiredRed', 'undeclaredRed', 'neverRan', 'pending', 'neutral', 'green']) {
|
|
344
|
+
if (!Array.isArray(artifact.partition[key])) errors.push(`${label}.partition.${key} must be an array`)
|
|
345
|
+
}
|
|
346
|
+
for (const [index, entry] of (artifact.partition.neverRan || []).entries()) {
|
|
347
|
+
const at = `${label}.partition.neverRan[${index}]`
|
|
348
|
+
if (!isObject(entry) || !nonEmpty(entry.name)) errors.push(`${at}.name must be a non-empty string`)
|
|
349
|
+
// The reason is the entire point of the row: an absence without one reads as
|
|
350
|
+
// "still queued" for all three of #1058's cases.
|
|
351
|
+
else if (!nonEmpty(entry.reason) || !nonEmpty(entry.detail)) errors.push(`${at} requires an actionable reason and detail`)
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if (!isObject(artifact.signatures) || !Array.isArray(artifact.signatures.startupFailures)) errors.push(`${label}.signatures.startupFailures must be an array`)
|
|
355
|
+
if (!isObject(artifact.summary) || !nonEmpty(artifact.summary.outcome)) errors.push(`${label}.summary.outcome must be a non-empty string`)
|
|
356
|
+
return { ok: errors.length === 0, errors }
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function describe(entry) {
|
|
360
|
+
const detail = entry.source === 'actions-run'
|
|
361
|
+
? ` — run ${entry.runId ?? '?'}, event ${entry.event ?? '?'}, ${entry.jobCount === null ? 'job count unknown' : `${entry.jobCount} job(s)`}`
|
|
362
|
+
: ''
|
|
363
|
+
return `${entry.name} [${entry.source}] ${entry.conclusion ?? entry.status ?? 'pending'}${detail}`
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export function renderHeadCheckSet(artifact) {
|
|
367
|
+
const lines = [
|
|
368
|
+
'<!-- generated by head-check-set.mjs — do not hand-edit; re-run collect -->',
|
|
369
|
+
`**Full check set at \`${artifact.headSha}\`** (${artifact.repository}, collected ${artifact.collectedAt})`,
|
|
370
|
+
'',
|
|
371
|
+
`- Outcome: **${artifact.summary?.outcome}** — ${artifact.summary?.counts?.observations ?? 0} observation(s) across `
|
|
372
|
+
+ `${artifact.summary?.counts?.checkRuns ?? 0} check-run(s), ${artifact.summary?.counts?.commitStatuses ?? 0} commit status(es), `
|
|
373
|
+
+ `${artifact.summary?.counts?.actionsRuns ?? 0} Actions run(s)`,
|
|
374
|
+
`- Required by the plan: ${artifact.planned?.planPresent ? (artifact.planned.required.join(', ') || 'none') : 'NO PLAN SUPPLIED — nothing is classified required, so this is an observation report only'}`,
|
|
375
|
+
]
|
|
376
|
+
const section = (title, rows) => {
|
|
377
|
+
if (!rows.length) return
|
|
378
|
+
lines.push(`- ${title}:`)
|
|
379
|
+
for (const row of rows) lines.push(` - ${row}`)
|
|
380
|
+
}
|
|
381
|
+
section('REQUIRED and red (blocking under the existing contract)', artifact.partition?.requiredRed?.map(describe) ?? [])
|
|
382
|
+
section(
|
|
383
|
+
'NOT required and red (informational — surfaced, never gated here; whether it should block belongs to `.mergify.yml` and the human)',
|
|
384
|
+
artifact.partition?.undeclaredRed?.map(describe) ?? [],
|
|
385
|
+
)
|
|
386
|
+
section(
|
|
387
|
+
'Required but never ran at all',
|
|
388
|
+
artifact.partition?.neverRan?.map((row) => `${row.name}: ${row.reason} — ${row.detail}`) ?? [],
|
|
389
|
+
)
|
|
390
|
+
section(
|
|
391
|
+
'Workflow runs that completed having executed ZERO jobs (the #1081 startup-failure signature)',
|
|
392
|
+
artifact.signatures?.startupFailures?.map((row) =>
|
|
393
|
+
`run ${row.runId} \`${row.name}\` (${row.conclusion}) executed no jobs`
|
|
394
|
+
+ `${row.nameEqualsPath ? ' and its name equals its file path — corroborating a workflow GitHub refused to parse' : ''}`) ?? [],
|
|
395
|
+
)
|
|
396
|
+
section('Pending', artifact.partition?.pending?.map(describe) ?? [])
|
|
397
|
+
return lines.join('\n')
|
|
398
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// The one ESM main-module guard every skill script in this repo uses.
|
|
2
|
+
//
|
|
3
|
+
// Not a style preference — the hand-written versions of this three-line check had two opposite
|
|
4
|
+
// silent failure modes in production, and both shipped:
|
|
5
|
+
//
|
|
6
|
+
// #718 `import.meta.url === pathToFileURL(process.argv[1]).href`
|
|
7
|
+
// Node resolves `import.meta.url` to the REAL path; `argv[1]` keeps whatever the caller
|
|
8
|
+
// typed. A skills install is a symlink farm (`~/.claude/skills/<skill>` → the checkout),
|
|
9
|
+
// so through the install path the two never match: the CLI does not run and node exits 0.
|
|
10
|
+
// `coverage-ledger.mjs` shipped this and recorded a false pass in the wild
|
|
11
|
+
// (a consumer product repo: epic #352, unit #379, PR #384) on a gate whose only job is to
|
|
12
|
+
// say no. Fails OPEN, invisibly — "passed" and "never ran" are byte-identical evidence.
|
|
13
|
+
//
|
|
14
|
+
// #446 `fs.realpathSync(process.argv[1]) === …`, unguarded.
|
|
15
|
+
// `node -` sets `argv[1]` to the literal string `"-"` — truthy, so the usual
|
|
16
|
+
// `process.argv[1] &&` prefix does not save it — and realpath throws ENOENT at MODULE
|
|
17
|
+
// LOAD, before a line of the module's own code runs. The module cannot be imported by
|
|
18
|
+
// anything that does not launch it as a file. Fails CLOSED, at load.
|
|
19
|
+
//
|
|
20
|
+
// So the contract has exactly two obligations, one per failure mode:
|
|
21
|
+
// 1. realpath BOTH sides before comparing, so a symlinked invocation is still the main module;
|
|
22
|
+
// 2. degrade to `false` — "not the main module" — for anything that does not resolve, never throw.
|
|
23
|
+
//
|
|
24
|
+
// WHY IT IS SHARED RATHER THAN COPIED. PR #278 fixed twelve copies of failure mode #718 by hand and
|
|
25
|
+
// mechanized nothing. Five weeks later `mergify/scripts/audit.mjs` was written with the pre-#278
|
|
26
|
+
// form, and `coverage-ledger.mjs` survived the whole pass because it spelled the same bug a third
|
|
27
|
+
// way that #278's grep did not match. One module plus `main-guard-hygiene.test.mjs` is what stops
|
|
28
|
+
// the next copy: there is now no second place for this logic to drift to, and adding one fails CI.
|
|
29
|
+
//
|
|
30
|
+
// WHERE IT LIVES, AND WHY HERE. Native Skills CLI installation flattens selected packages into
|
|
31
|
+
// sibling directories under `~/.agents/skills`, while this repository nests them under
|
|
32
|
+
// `skills/engineering`. A Workflow consumer at `<skill>/scripts` therefore imports
|
|
33
|
+
// `../../engineering-runtime/scripts/main-module.mjs`: the same specifier reaches this package in
|
|
34
|
+
// both layouts. Claude Code launches through a per-skill symlink into the canonical store; node's
|
|
35
|
+
// realpath-aware module resolution and this predicate preserve that path as well.
|
|
36
|
+
//
|
|
37
|
+
// `engineering-runtime` owns the helper as an installable support package rather than a user
|
|
38
|
+
// capability. `resolve-issues/scripts/sandbox-selftest.mjs` reconstructs the same sibling layout and
|
|
39
|
+
// links this package beside the skill under test. Every consumer imports this package directly.
|
|
40
|
+
import fs from 'node:fs';
|
|
41
|
+
import { fileURLToPath } from 'node:url';
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Is the module identified by `importMetaUrl` the script node was launched with?
|
|
45
|
+
*
|
|
46
|
+
* Symlink-transparent, and total: any input that does not resolve to a real file answers `false`
|
|
47
|
+
* instead of throwing, so importing a CLI module is always safe.
|
|
48
|
+
*
|
|
49
|
+
* @param {string} importMetaUrl the caller's own `import.meta.url`
|
|
50
|
+
* @returns {boolean}
|
|
51
|
+
*/
|
|
52
|
+
export function isMainModule(importMetaUrl) {
|
|
53
|
+
const entry = process.argv[1];
|
|
54
|
+
if (typeof entry !== 'string' || entry === '') return false; // imported, or an embedding harness
|
|
55
|
+
|
|
56
|
+
let entryPath;
|
|
57
|
+
try {
|
|
58
|
+
entryPath = fs.realpathSync(entry);
|
|
59
|
+
} catch {
|
|
60
|
+
return false; // #446: `node -` gives "-"; some harnesses give a path that is not on disk
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let selfPath;
|
|
64
|
+
try {
|
|
65
|
+
selfPath = fileURLToPath(importMetaUrl);
|
|
66
|
+
} catch {
|
|
67
|
+
return false; // a data:/http: module has no path to compare, so it is never the launched script
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
// #718: the launched side is resolved above, so this side must be too. Under Node's default
|
|
71
|
+
// resolution `import.meta.url` is already real and this is a no-op; under `--preserve-symlinks`
|
|
72
|
+
// it is the half that makes the comparison hold.
|
|
73
|
+
selfPath = fs.realpathSync(selfPath);
|
|
74
|
+
} catch {
|
|
75
|
+
// The module is executing, so it existed a moment ago. Compare what we have rather than
|
|
76
|
+
// claiming "not main" — a vanished file should not silently disable a running CLI.
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return entryPath === selfPath;
|
|
80
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { DEFAULT_SCHEMA, load } from "js-yaml"
|
|
2
|
+
|
|
3
|
+
export function parseMergifyYaml(yaml) {
|
|
4
|
+
try {
|
|
5
|
+
const value = load(String(yaml || ""), { schema: DEFAULT_SCHEMA, json: false })
|
|
6
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Mergify YAML root must be a mapping")
|
|
7
|
+
return value
|
|
8
|
+
} catch (error) {
|
|
9
|
+
throw new Error(`Mergify YAML structural parse failed closed: ${String(error.message || error).trim()}`)
|
|
10
|
+
}
|
|
11
|
+
}
|