@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,485 @@
|
|
|
1
|
+
// The VERSION-LEVEL release post-mortem: what one version cost across every attempt it took (#961).
|
|
2
|
+
//
|
|
3
|
+
// WHY THIS EXISTS. The single-manifest post-mortem (postmortem.mjs) profiles ONE run's record for
|
|
4
|
+
// completeness. It cannot see the shape that made Forex v0.1.12 the incident it was: NINE attempts
|
|
5
|
+
// for one version, eight of them non-released, and the release lane's own tooling — never the
|
|
6
|
+
// application — as the repeated owner of the failures. Reconstructing that meant reading nine
|
|
7
|
+
// durable bundles by hand, which is exactly the work the durable record exists to make mechanical.
|
|
8
|
+
// So this pass aggregates every durable evidence bundle a version left behind and asks the
|
|
9
|
+
// cross-attempt questions: how many attempts, how each one ended, how far each one got, who owned
|
|
10
|
+
// the failures, and whether one owner kept repeating.
|
|
11
|
+
//
|
|
12
|
+
// WHAT IT READS. The durable evidence bundles (`evidence-bundle.mjs`, keyed
|
|
13
|
+
// `<version>-attempt.<n>--<terminal-state>.json`) — NOT the run manifest, and never a live service.
|
|
14
|
+
// The bundles are the immutable record (principle 10); this pass is their aggregate reader. It is
|
|
15
|
+
// deliberately tolerant of schema drift across attempts: real bundles were written by different
|
|
16
|
+
// releases months apart, so every signal below is read defensively and an absent signal is reported
|
|
17
|
+
// as absent, never invented.
|
|
18
|
+
//
|
|
19
|
+
// THE CLASSIFICATION SURFACE IS A CONTRACT (#962 consumes it). `FAILURE_OWNERS` and the derivation
|
|
20
|
+
// order in `classifyFailureOwner` are documented in references/postmortem.md and are STABLE:
|
|
21
|
+
// changing a name, the order, or a rule's meaning is a breaking change to a downstream contract.
|
|
22
|
+
// A bundle may DECLARE its owner (`failureOwner`, top-level or in `disposition`) — the declaration
|
|
23
|
+
// wins, because the human or mechanism closest to the failure knows more than any heuristic — and
|
|
24
|
+
// everything else is derived from the recorded verdicts by the ordered rules below.
|
|
25
|
+
//
|
|
26
|
+
// What this pass deliberately does NOT do (the issue's non-goals): it does not treat a rollback as
|
|
27
|
+
// a failure of the release process — a rollback that happened and was recorded is the machine
|
|
28
|
+
// working, reported as a fact in the counts — and it does not adjudicate its own findings. The
|
|
29
|
+
// repeated-owner finding says WHERE to look; what to change is still a human's call.
|
|
30
|
+
|
|
31
|
+
import fs from 'node:fs'
|
|
32
|
+
import path from 'node:path'
|
|
33
|
+
|
|
34
|
+
import { renderProfile, saveProfile } from '../../resolve-issues/scripts/postmortem.mjs'
|
|
35
|
+
import { ATTEMPT_TERMINAL_STATES } from '../../resolve-issues/scripts/run-state.mjs'
|
|
36
|
+
import { isMainModule } from '../../engineering-runtime/scripts/main-module.mjs'
|
|
37
|
+
|
|
38
|
+
// The normalized failure-owner classification, in its documented order. STABLE SURFACE (#962).
|
|
39
|
+
export const FAILURE_OWNERS = ['application', 'configuration', 'platform', 'release-tooling', 'gate-environment']
|
|
40
|
+
|
|
41
|
+
// WHERE the lane stopped, normalized the same way the owner is (#962). `pre-candidate` — the
|
|
42
|
+
// attempt ended before any candidate evidence exists (promotion/build/probe; also every failure
|
|
43
|
+
// on the `no-deploy` shape, which never has a candidate); `candidate-validation` — a candidate
|
|
44
|
+
// was judged at 0% and traffic never moved (step 4a); `post-exposure` — traffic moved and was
|
|
45
|
+
// restored (a rollback, by construction). Same stability rule as FAILURE_OWNERS: the
|
|
46
|
+
// rc-circuit-breaker records this per qualifying attempt.
|
|
47
|
+
export const FAILURE_PHASES = ['pre-candidate', 'candidate-validation', 'post-exposure']
|
|
48
|
+
|
|
49
|
+
const isObject = (v) => v != null && typeof v === 'object' && !Array.isArray(v)
|
|
50
|
+
const nonEmpty = (v) => typeof v === 'string' && v.trim() !== ''
|
|
51
|
+
|
|
52
|
+
// The states that END an attempt without shipping, where something — not someone — owns the
|
|
53
|
+
// outcome. `revoked` is an operator's deliberate abandonment and `blocked-manual` is a park, not
|
|
54
|
+
// an outcome; classifying either would manufacture a failure that did not happen.
|
|
55
|
+
const FAILURE_STATES = new Set(['failed-pre-promotion', 'rolled-back'])
|
|
56
|
+
|
|
57
|
+
// ---- classification ----
|
|
58
|
+
//
|
|
59
|
+
// Ordered, first match wins. The order is the contract: each rule is stated so that a LATER rule
|
|
60
|
+
// never re-attributes what an earlier one already explained.
|
|
61
|
+
//
|
|
62
|
+
// 1. DECLARED. `failureOwner` on the bundle or its disposition, naming one of the five. The
|
|
63
|
+
// stable extension point: a future mechanism (#962) writes the owner at close time and this
|
|
64
|
+
// pass reads it back verbatim.
|
|
65
|
+
// 2. gate-environment. The candidate verdict is CANNOT-RUN — an applicable gate that could not
|
|
66
|
+
// run (principle 13): absent credentials, an unavailable external service, a harness that
|
|
67
|
+
// would not start. The world stayed known and the question went unanswered; the owner is the
|
|
68
|
+
// gate's environment, not the candidate and not the lane.
|
|
69
|
+
// 3. application / configuration / platform. The live or candidate evidence judged the artifact
|
|
70
|
+
// UNHEALTHY. Attributed by the recorded reason: configuration-shaped evidence (secrets, env
|
|
71
|
+
// vars, config) → `configuration`; platform-shaped (quota, the hosting platform, deploy
|
|
72
|
+
// infrastructure) → `platform`; anything else unhealthy → `application` — the observed defect
|
|
73
|
+
// belongs to the artifact until the evidence says otherwise.
|
|
74
|
+
// 4. release-tooling (healthy). The live evidence shows the candidate HEALTHY — serving, ready,
|
|
75
|
+
// probes passing — and the attempt failed anyway. What failed is the lane's own machinery:
|
|
76
|
+
// the verifier, the normalization, an adapter, a gate's assumption about a valid platform
|
|
77
|
+
// representation. Forex 0.1.12's dominant owner.
|
|
78
|
+
// 5. release-tooling (unmeasured stop). The candidate verdict was ALIVE and the attempt still
|
|
79
|
+
// ended in a failure state — the lane discarded or rolled back a candidate its own gate
|
|
80
|
+
// judged alive, so the stopping assumption lives in the lane.
|
|
81
|
+
// 6. release-tooling (no evidence). No verdict evidence at all: the lane ended an attempt it
|
|
82
|
+
// never measured. The hole is in the lane's record, so the owner is the lane.
|
|
83
|
+
const CONFIG_SIGNAL = /config|secret|env\s?var|missing (key|token|credential)|api[- ]?key/i
|
|
84
|
+
const PLATFORM_SIGNAL = /quota|cloud run|platform|infrastructure|deploy(ment)? fail|provision/i
|
|
85
|
+
|
|
86
|
+
function evidenceText(bundle) {
|
|
87
|
+
return [bundle?.candidateVerdict, bundle?.liveVerdict, bundle?.disposition]
|
|
88
|
+
.filter(isObject)
|
|
89
|
+
.flatMap((o) => [o.verdict, o.runtimeStatus, o.reason, o.releaseLaneVerdict, o.action])
|
|
90
|
+
.filter(nonEmpty)
|
|
91
|
+
.join(' · ')
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function attributeUnhealthy(bundle) {
|
|
95
|
+
const text = evidenceText(bundle)
|
|
96
|
+
if (CONFIG_SIGNAL.test(text)) return 'configuration'
|
|
97
|
+
if (PLATFORM_SIGNAL.test(text)) return 'platform'
|
|
98
|
+
return 'application'
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const healthyLiveSignal = (lv) => isObject(lv) && (
|
|
102
|
+
(nonEmpty(lv.runtimeStatus) && /^healthy/i.test(lv.runtimeStatus))
|
|
103
|
+
|| lv.verdict === 'ALIVE'
|
|
104
|
+
|| (nonEmpty(lv.verdict) && /^probe-succeeded/i.test(lv.verdict))
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
const unhealthySignal = (v) => isObject(v) && (
|
|
108
|
+
v.verdict === 'DEAD'
|
|
109
|
+
|| (nonEmpty(v.runtimeStatus) && !/^healthy/i.test(v.runtimeStatus))
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
// Returns { owner, basis } for a failure terminal state, null otherwise. `basis` names the rule
|
|
113
|
+
// and the signals used, so a reader can disbelieve the classification against the bundle itself.
|
|
114
|
+
export function classifyFailureOwner(bundle) {
|
|
115
|
+
if (!isObject(bundle) || !FAILURE_STATES.has(bundle.terminalState)) return null
|
|
116
|
+
|
|
117
|
+
// 1. declared
|
|
118
|
+
const declared = [bundle.failureOwner, isObject(bundle.disposition) ? bundle.disposition.failureOwner : null]
|
|
119
|
+
.find((v) => FAILURE_OWNERS.includes(v))
|
|
120
|
+
if (declared) return { owner: declared, basis: 'declared in the bundle' }
|
|
121
|
+
|
|
122
|
+
const cv = isObject(bundle.candidateVerdict) ? bundle.candidateVerdict : null
|
|
123
|
+
const lv = isObject(bundle.liveVerdict) ? bundle.liveVerdict : null
|
|
124
|
+
|
|
125
|
+
// 2. gate-environment
|
|
126
|
+
if (cv?.verdict === 'CANNOT-RUN') {
|
|
127
|
+
return { owner: 'gate-environment', basis: 'candidate verdict CANNOT-RUN — an applicable gate that could not run (principle 13)' }
|
|
128
|
+
}
|
|
129
|
+
// 3. unhealthy evidence, attributed. Live evidence wins over the pre-exposure verdict: what
|
|
130
|
+
// production showed after the shift is the stronger observation (principle 5).
|
|
131
|
+
if (unhealthySignal(lv)) return { owner: attributeUnhealthy(bundle), basis: `live verdict unhealthy (${lv.verdict ?? lv.runtimeStatus})` }
|
|
132
|
+
if (unhealthySignal(cv)) return { owner: attributeUnhealthy(bundle), basis: 'candidate verdict DEAD' }
|
|
133
|
+
// 4. healthy live evidence, attempt failed anyway
|
|
134
|
+
if (healthyLiveSignal(lv)) {
|
|
135
|
+
return { owner: 'release-tooling', basis: `live evidence healthy (${lv.runtimeStatus ?? lv.verdict}) while the attempt ${bundle.terminalState} — the lane's own machinery stopped it` }
|
|
136
|
+
}
|
|
137
|
+
// 5. candidate judged alive, attempt failed
|
|
138
|
+
if (cv?.verdict === 'ALIVE') {
|
|
139
|
+
return { owner: 'release-tooling', basis: `candidate verdict ALIVE but the attempt ${bundle.terminalState}` }
|
|
140
|
+
}
|
|
141
|
+
// 6. no verdict evidence at all
|
|
142
|
+
return { owner: 'release-tooling', basis: 'no verdict evidence recorded — the lane ended an attempt it never measured' }
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// The phase a failure terminal state stopped at, derived from the bundle's own evidence — or
|
|
146
|
+
// the bundle's declared `failurePhase` when the author recorded one (declared wins, the same
|
|
147
|
+
// rule as the owner: the mechanism closest to the failure knows more than the derivation).
|
|
148
|
+
// Null for non-failure terminal states, exactly as the owner is.
|
|
149
|
+
export function classifyFailurePhase(bundle) {
|
|
150
|
+
if (!isObject(bundle) || !FAILURE_STATES.has(bundle.terminalState)) return null
|
|
151
|
+
if (FAILURE_PHASES.includes(bundle.failurePhase)) return bundle.failurePhase
|
|
152
|
+
if (bundle.terminalState === 'rolled-back') return 'post-exposure'
|
|
153
|
+
if (isObject(bundle.liveVerdict) || isObject(bundle.trafficTransition)) return 'post-exposure'
|
|
154
|
+
if (isObject(bundle.candidateVerdict) || isObject(bundle.candidateReceipt)) return 'candidate-validation'
|
|
155
|
+
return 'pre-candidate'
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ---- discovery ----
|
|
159
|
+
|
|
160
|
+
const ATTEMPT_KEY = /^(?<version>.+)-attempt\.(?<ordinal>\d+)$/
|
|
161
|
+
const RC_KEY = /^(?<version>.+)-rc\.(?<ordinal>\d+)$/
|
|
162
|
+
const normVersion = (v) => String(v ?? '').trim().replace(/^v/, '')
|
|
163
|
+
|
|
164
|
+
// The version a bundle file belongs to, and its ordering ordinal, parsed from the FILENAME — the
|
|
165
|
+
// filename is the immutable key (evidence-bundle.mjs), so a bundle whose content disagrees with
|
|
166
|
+
// its own key is discovered under the key, exactly as `path` would locate it. Returns null for
|
|
167
|
+
// anything that is not a bundle file (sidecars like `<id>-rollout.json` never match: the key and
|
|
168
|
+
// state are separated by `--`, and the state must be one of #432's terminal states).
|
|
169
|
+
function parseBundleFilename(entry) {
|
|
170
|
+
if (!entry.endsWith('.json')) return null
|
|
171
|
+
const stem = entry.slice(0, -'.json'.length)
|
|
172
|
+
const sep = stem.indexOf('--')
|
|
173
|
+
if (sep < 0) return null
|
|
174
|
+
const key = stem.slice(0, sep)
|
|
175
|
+
const state = stem.slice(sep + 2)
|
|
176
|
+
if (!ATTEMPT_TERMINAL_STATES.includes(state)) return null
|
|
177
|
+
const m = ATTEMPT_KEY.exec(key) ?? RC_KEY.exec(key)
|
|
178
|
+
if (!m) return null
|
|
179
|
+
return { key, state, version: m.groups.version, ordinal: Number(m.groups.ordinal) }
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Every durable bundle for one version, ordered by the immutable attempt id's ordinal —
|
|
183
|
+
// NUMERICALLY, so attempt.10 follows attempt.9 (a lexicographic sort silently reorders history).
|
|
184
|
+
// Legacy pre-#599 bundles key off the rc tag (`v<version>-rc.<n>`) and sort by the rc ordinal on
|
|
185
|
+
// the same scale. Version comparison normalizes the leading `v` off BOTH sides (evidence-bundle's
|
|
186
|
+
// own rule): a repo may spell its version `0.1.12` while its tags say `v0.1.12`.
|
|
187
|
+
//
|
|
188
|
+
// FAIL CLOSED, same reading as evidence-bundle.mjs: a dir that cannot be READ throws (EACCES,
|
|
189
|
+
// ENOTDIR, …) — only ENOENT is "nothing emitted yet". A bundle FILE that cannot be parsed is not
|
|
190
|
+
// skipped either: it comes back in `unreadable` so the profile can fault it — a durable record
|
|
191
|
+
// that cannot be read is a hole in exactly the history this pass exists to account for.
|
|
192
|
+
export function discoverVersionBundles(dir, version) {
|
|
193
|
+
if (!nonEmpty(dir)) throw new Error('discoverVersionBundles: a durable evidence dir is required')
|
|
194
|
+
if (!nonEmpty(version)) throw new Error('discoverVersionBundles: a version is required')
|
|
195
|
+
let entries
|
|
196
|
+
try {
|
|
197
|
+
entries = fs.readdirSync(dir)
|
|
198
|
+
} catch (e) {
|
|
199
|
+
if (e.code !== 'ENOENT') throw e
|
|
200
|
+
entries = []
|
|
201
|
+
}
|
|
202
|
+
const wanted = normVersion(version)
|
|
203
|
+
const attempts = []
|
|
204
|
+
const unreadable = []
|
|
205
|
+
for (const entry of entries) {
|
|
206
|
+
const parsed = parseBundleFilename(entry)
|
|
207
|
+
if (!parsed || normVersion(parsed.version) !== wanted) continue
|
|
208
|
+
const file = path.join(dir, entry)
|
|
209
|
+
let bundle = null
|
|
210
|
+
try {
|
|
211
|
+
bundle = JSON.parse(fs.readFileSync(file, 'utf8'))
|
|
212
|
+
} catch {
|
|
213
|
+
unreadable.push(`${entry} (unparseable)`)
|
|
214
|
+
continue
|
|
215
|
+
}
|
|
216
|
+
attempts.push({
|
|
217
|
+
attemptId: parsed.key,
|
|
218
|
+
ordinal: parsed.ordinal,
|
|
219
|
+
terminalState: parsed.state,
|
|
220
|
+
file: entry,
|
|
221
|
+
path: file,
|
|
222
|
+
bundle,
|
|
223
|
+
})
|
|
224
|
+
}
|
|
225
|
+
attempts.sort((a, b) => (a.ordinal - b.ordinal) || (a.attemptId < b.attemptId ? -1 : 1))
|
|
226
|
+
return { attempts, unreadable }
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ---- the per-attempt measurements ----
|
|
230
|
+
|
|
231
|
+
// The highest traffic percent the candidate is recorded reaching, from whichever artifact carried
|
|
232
|
+
// it: the staged transition (`stages[].to`), the atomic shape (`after.candidatePercent`), the
|
|
233
|
+
// live verdict's read, or the receipt's. `null` when nothing recorded an exposure — reported as
|
|
234
|
+
// absent, never zero-filled: 0% is a measurement (never exposed), null is no record.
|
|
235
|
+
function maxExposure(bundle) {
|
|
236
|
+
const candidates = []
|
|
237
|
+
const tt = isObject(bundle.trafficTransition) ? bundle.trafficTransition : null
|
|
238
|
+
if (tt) {
|
|
239
|
+
if (Array.isArray(tt.stages)) {
|
|
240
|
+
for (const s of tt.stages) if (isObject(s) && Number.isFinite(s.to)) candidates.push(s.to)
|
|
241
|
+
}
|
|
242
|
+
if (isObject(tt.after) && Number.isFinite(tt.after.candidatePercent)) candidates.push(tt.after.candidatePercent)
|
|
243
|
+
}
|
|
244
|
+
const lv = isObject(bundle.liveVerdict) ? bundle.liveVerdict : null
|
|
245
|
+
if (lv) {
|
|
246
|
+
if (Number.isFinite(lv.candidatePercent)) candidates.push(lv.candidatePercent)
|
|
247
|
+
if (isObject(lv.traffic) && Number.isFinite(lv.traffic.candidate)) candidates.push(lv.traffic.candidate)
|
|
248
|
+
}
|
|
249
|
+
const receipt = isObject(bundle.candidateReceipt) ? bundle.candidateReceipt : null
|
|
250
|
+
if (receipt && Number.isFinite(receipt.trafficPercent)) candidates.push(receipt.trafficPercent)
|
|
251
|
+
return candidates.length ? Math.max(...candidates) : null
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Every recorded traffic-affecting mutation: each observed shift stage (or the single before/after
|
|
255
|
+
// transition), plus the rollback restore — restoring the incumbent is a traffic mutation too, and
|
|
256
|
+
// counting only the shifts would understate what production went through by exactly the unwind.
|
|
257
|
+
function trafficMutations(bundle) {
|
|
258
|
+
let n = 0
|
|
259
|
+
const tt = isObject(bundle.trafficTransition) ? bundle.trafficTransition : null
|
|
260
|
+
if (tt) {
|
|
261
|
+
if (Array.isArray(tt.stages)) n += tt.stages.filter(isObject).length
|
|
262
|
+
else if (isObject(tt.before) && isObject(tt.after)) n += 1
|
|
263
|
+
}
|
|
264
|
+
if (bundle.terminalState === 'rolled-back' && isObject(bundle.disposition)) n += 1
|
|
265
|
+
return n
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Rolled back while the recorded evidence shows the candidate healthy/ready. Reported SEPARATELY
|
|
269
|
+
// from unhealthy rollbacks (the AC): a healthy candidate rolled back is the lane doubting itself,
|
|
270
|
+
// an unhealthy one is the lane working — conflating them is how "the tooling cried wolf six times"
|
|
271
|
+
// reads as "the release failed six times". The signal is POSITIVE health evidence in the live
|
|
272
|
+
// verdict (healthy/* runtime status, ALIVE, probe-succeeded) — a candidate merely judged ALIVE at
|
|
273
|
+
// 0% is not "healthy when rolled back", because exposure is where health became a question.
|
|
274
|
+
const healthyAtRollback = (a) => a.terminalState === 'rolled-back' && healthyLiveSignal(a.bundle.liveVerdict)
|
|
275
|
+
|
|
276
|
+
// ---- the profile ----
|
|
277
|
+
|
|
278
|
+
// Whether the version was REUSED or BURNED. Under both tag-after-verify shapes a failed attempt
|
|
279
|
+
// burns nothing (principle 10: the only tag carrying a promise is minted post-verification), so
|
|
280
|
+
// more than one attempt means the version was reused. The burn is the legacy `final-tag` shape:
|
|
281
|
+
// the pipeline fires on the final tag, so a failed attempt consumed the version claim and the
|
|
282
|
+
// retry takes the next patch (references/versioning.md). A burn the bundle itself recorded
|
|
283
|
+
// (`disposition.versionWithdrawn`) is honored on any shape.
|
|
284
|
+
function versionDisposition(attempts) {
|
|
285
|
+
const burned = attempts.some((a) => a.bundle.deployTrigger === 'final-tag'
|
|
286
|
+
|| (isObject(a.bundle.disposition) && a.bundle.disposition.versionWithdrawn === true))
|
|
287
|
+
if (burned) return 'burned'
|
|
288
|
+
return attempts.length > 1 ? 'reused' : 'single-attempt'
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export function versionPostmortem({ dir, version, runSlug = null } = {}) {
|
|
292
|
+
const { attempts, unreadable } = discoverVersionBundles(dir, version)
|
|
293
|
+
|
|
294
|
+
const rows = attempts.map((a) => {
|
|
295
|
+
const failure = classifyFailureOwner(a.bundle)
|
|
296
|
+
return {
|
|
297
|
+
attemptId: a.attemptId,
|
|
298
|
+
ordinal: a.ordinal,
|
|
299
|
+
terminalState: a.terminalState,
|
|
300
|
+
rcTag: nonEmpty(a.bundle.rcTag) ? a.bundle.rcTag : null,
|
|
301
|
+
maxExposure: maxExposure(a.bundle),
|
|
302
|
+
trafficMutations: trafficMutations(a.bundle),
|
|
303
|
+
failureOwner: failure?.owner ?? null,
|
|
304
|
+
failureBasis: failure?.basis ?? null,
|
|
305
|
+
failurePhase: classifyFailurePhase(a.bundle),
|
|
306
|
+
healthyAtRollback: healthyAtRollback(a),
|
|
307
|
+
file: a.file,
|
|
308
|
+
}
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
const terminalStates = Object.fromEntries(ATTEMPT_TERMINAL_STATES.map((s) => [s, 0]))
|
|
312
|
+
for (const r of rows) terminalStates[r.terminalState] += 1
|
|
313
|
+
|
|
314
|
+
const rcTags = new Set(rows.map((r) => r.rcTag).filter(Boolean))
|
|
315
|
+
const rolledBack = rows.filter((r) => r.terminalState === 'rolled-back')
|
|
316
|
+
const metrics = {
|
|
317
|
+
version: String(version),
|
|
318
|
+
attempts: rows.length,
|
|
319
|
+
rcCount: rcTags.size,
|
|
320
|
+
nonReleased: rows.length - terminalStates.released,
|
|
321
|
+
terminalStates,
|
|
322
|
+
rollbacks: rolledBack.length,
|
|
323
|
+
trafficMutations: rows.reduce((n, r) => n + r.trafficMutations, 0),
|
|
324
|
+
maxExposure: rows.reduce((m, r) => (r.maxExposure != null && r.maxExposure > m ? r.maxExposure : m), 0),
|
|
325
|
+
healthyAtRollback: rolledBack.filter((r) => r.healthyAtRollback).map((r) => r.attemptId),
|
|
326
|
+
healthyAtFullExposure: rolledBack.filter((r) => r.healthyAtRollback && r.maxExposure === 100).map((r) => r.attemptId),
|
|
327
|
+
otherAtRollback: rolledBack.filter((r) => !r.healthyAtRollback).map((r) => r.attemptId),
|
|
328
|
+
versionDisposition: versionDisposition(attempts),
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const findings = []
|
|
332
|
+
|
|
333
|
+
// A durable record that cannot be read is a hole in the account this pass exists to keep.
|
|
334
|
+
if (unreadable.length) {
|
|
335
|
+
findings.push({
|
|
336
|
+
id: 'unreadable-version-bundle',
|
|
337
|
+
severity: 'high',
|
|
338
|
+
costMinutes: null,
|
|
339
|
+
headline: `${unreadable.length} bundle(s) for ${version} could not be read`,
|
|
340
|
+
detail: 'The aggregate is computed from the durable bundles, and a bundle that does not parse is an attempt whose record is unreachable — the same completeness failure the single-manifest pass faults, one level up. The counts above exclude it.',
|
|
341
|
+
evidence: unreadable,
|
|
342
|
+
recommendation: 'Read the file by hand and repair or quarantine it. Do not delete it: a damaged durable record is still the only record of its attempt.',
|
|
343
|
+
})
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// The finding this pass exists for (#961): one failure owner repeating across attempts. Eight
|
|
347
|
+
// failed Forex attempts all owned by the release tooling is a systemic signal; the same signal
|
|
348
|
+
// spread across eight single-manifest post-mortems was eight files that each said nothing
|
|
349
|
+
// wrong. Threshold: two occurrences is worth a line, three or more is the dominant story.
|
|
350
|
+
const byOwner = new Map()
|
|
351
|
+
for (const r of rows.filter((x) => x.failureOwner)) {
|
|
352
|
+
if (!byOwner.has(r.failureOwner)) byOwner.set(r.failureOwner, [])
|
|
353
|
+
byOwner.get(r.failureOwner).push(r)
|
|
354
|
+
}
|
|
355
|
+
for (const [owner, owned] of [...byOwner.entries()].sort((a, b) => b[1].length - a[1].length)) {
|
|
356
|
+
if (owned.length < 2) continue
|
|
357
|
+
findings.push({
|
|
358
|
+
id: 'repeated-failure-owner',
|
|
359
|
+
severity: owned.length >= 3 ? 'high' : 'medium',
|
|
360
|
+
costMinutes: null,
|
|
361
|
+
headline: `\`${owner}\` owned ${owned.length} of ${rows.filter((r) => r.failureOwner).length} failed attempt(s) of ${version}`,
|
|
362
|
+
detail: 'One owner repeating across attempts of one version is a systemic signal, not N independent failures: the retry loop is re-entering the same defect with a new candidate each time, and each retry pays the full attempt cost. The classification is derived from the recorded verdicts (references/postmortem.md § failure ownership) — adjudicate the owner, then fix the owner, not the candidate.',
|
|
363
|
+
evidence: owned.map((r) => `${r.attemptId} (${r.terminalState}${r.maxExposure != null ? `, reached ${r.maxExposure}%` : ''}) — ${r.failureBasis} — ${r.file}`),
|
|
364
|
+
recommendation: 'Adjudicate each attempt against its bundle before acting: a mis-classified owner sends the fix to the wrong place, and the heuristic is documented so it can be argued with. Where the owner is `release-tooling`, the issue belongs against the release lane, not the application.',
|
|
365
|
+
})
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const healthyNote = metrics.healthyAtRollback.length
|
|
369
|
+
? ` · ${metrics.healthyAtRollback.length} rolled back while healthy/ready (${metrics.healthyAtFullExposure.length} at 100%)`
|
|
370
|
+
: ''
|
|
371
|
+
const summaryLine = rows.length === 0
|
|
372
|
+
? `no durable bundles discovered for ${version}`
|
|
373
|
+
: [
|
|
374
|
+
`${metrics.attempts} attempt(s)`,
|
|
375
|
+
`${metrics.rcCount} rc tag(s)`,
|
|
376
|
+
`${metrics.nonReleased} non-released`,
|
|
377
|
+
`${metrics.rollbacks} rolled back${healthyNote}`,
|
|
378
|
+
`${metrics.trafficMutations} traffic mutation(s)`,
|
|
379
|
+
`max exposure ${metrics.maxExposure}%`,
|
|
380
|
+
`version ${metrics.versionDisposition}`,
|
|
381
|
+
].join(' · ')
|
|
382
|
+
|
|
383
|
+
return {
|
|
384
|
+
kind: 'release-version',
|
|
385
|
+
runSlug: nonEmpty(runSlug) ? `${runSlug}@${version}` : `version-${version}`,
|
|
386
|
+
source: `the durable evidence bundles for ${version} under ${dir}`,
|
|
387
|
+
summaryLine,
|
|
388
|
+
metrics,
|
|
389
|
+
attempts: rows,
|
|
390
|
+
blindSpots: 'attempts that never emitted a bundle (a session that died before a terminal exit), bundles outside this evidence dir, and everything the bundles themselves cannot see — the aggregate is exactly as complete as the durable records it discovered',
|
|
391
|
+
findings,
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// The per-attempt table, rendered for the handoff and the CLI. The persisted .txt stays
|
|
396
|
+
// renderProfile's (the series format is shared with both other kinds); this table is what makes
|
|
397
|
+
// the aggregate readable at a glance where it is printed.
|
|
398
|
+
export function renderAttemptTable(p) {
|
|
399
|
+
if (!p.attempts?.length) return ''
|
|
400
|
+
const lines = p.attempts.map((a) => {
|
|
401
|
+
const exposure = a.maxExposure == null ? 'no exposure recorded' : `max exposure ${a.maxExposure}%`
|
|
402
|
+
const owner = a.failureOwner ? ` — owner: ${a.failureOwner}${a.failurePhase ? ` (${a.failurePhase})` : ''}` : ''
|
|
403
|
+
const healthy = a.healthyAtRollback ? ' [healthy/ready when rolled back]' : ''
|
|
404
|
+
return ` ${a.attemptId} ${a.terminalState} ${exposure}${a.trafficMutations ? ` ${a.trafficMutations} traffic mutation(s)` : ''}${owner}${healthy}`
|
|
405
|
+
})
|
|
406
|
+
return `${lines.join('\n')}\n`
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
export function renderVersionPostmortem(p) {
|
|
410
|
+
return `${renderAttemptTable(p)}${renderProfile(p)}`
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// The same series, the same renderer, the same persistence as the run-level passes — one more
|
|
414
|
+
// `kind` in the shared index, which is what lets the cross-run tooling read it without a new
|
|
415
|
+
// format. NEVER THROWS, on saveProfile's terms.
|
|
416
|
+
export function saveVersionPostmortem(p, env = process.env, now = new Date()) {
|
|
417
|
+
return saveProfile(p, env, now)
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// ---- CLI ----
|
|
421
|
+
|
|
422
|
+
const USAGE = `version-postmortem — the version-level release post-mortem (#961).
|
|
423
|
+
|
|
424
|
+
Aggregates every durable evidence bundle one version left behind (<version>-attempt.<n>--<state>.json,
|
|
425
|
+
legacy <version>-rc.<n> keys included), ordered by the immutable attempt id, and reports: attempt
|
|
426
|
+
and rc counts, terminal-state counts, maximum exposure per attempt, traffic mutations, rollback
|
|
427
|
+
count (healthy-at-rollback separately from the rest), reused-vs-burned, and a normalized failure
|
|
428
|
+
owner per failed attempt — ${FAILURE_OWNERS.join(' | ')} — with a finding when one owner repeats.
|
|
429
|
+
|
|
430
|
+
Usage:
|
|
431
|
+
version-postmortem --dir <durable-evidence-dir> --version <v> [--run-slug <slug>] [--save] [--issue]
|
|
432
|
+
|
|
433
|
+
--save persist the profile into the shared post-mortem series (kind: release-version)
|
|
434
|
+
--issue print {title, body} for filing against resolve-release
|
|
435
|
+
|
|
436
|
+
Doctrine: resolve-release/references/postmortem.md (the classification table is the stable
|
|
437
|
+
surface #962 consumes).`
|
|
438
|
+
|
|
439
|
+
function parseArgs(argv) {
|
|
440
|
+
const opts = {}
|
|
441
|
+
for (let i = 0; i < argv.length; i++) {
|
|
442
|
+
const a = argv[i]
|
|
443
|
+
if (a === '--help' || a === '-h' || a === '--save' || a === '--issue') opts[a.replace(/^--/, '')] = true
|
|
444
|
+
else if (a.startsWith('--')) opts[a.slice(2)] = argv[++i]
|
|
445
|
+
}
|
|
446
|
+
return opts
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
export function main(argv) {
|
|
450
|
+
const opts = parseArgs(argv)
|
|
451
|
+
if (opts.help) {
|
|
452
|
+
console.log(USAGE)
|
|
453
|
+
return 0
|
|
454
|
+
}
|
|
455
|
+
if (!nonEmpty(opts.dir) || !nonEmpty(opts.version)) {
|
|
456
|
+
console.error('version-postmortem: usage: --dir <durable-evidence-dir> --version <v> are both required (see --help)')
|
|
457
|
+
return 2
|
|
458
|
+
}
|
|
459
|
+
let p
|
|
460
|
+
try {
|
|
461
|
+
p = versionPostmortem({ dir: opts.dir, version: opts.version, runSlug: opts['run-slug'] ?? null })
|
|
462
|
+
} catch (e) {
|
|
463
|
+
// Fail closed: an unreadable evidence dir is not an empty history.
|
|
464
|
+
console.error(`version-postmortem: ${e.message}`)
|
|
465
|
+
return 2
|
|
466
|
+
}
|
|
467
|
+
if (opts.issue) {
|
|
468
|
+
console.log(JSON.stringify({ title: `resolve-release version post-mortem: ${opts.version} (${p.summaryLine})`, body: renderVersionPostmortem(p) }, null, 2))
|
|
469
|
+
return 0
|
|
470
|
+
}
|
|
471
|
+
process.stdout.write(renderVersionPostmortem(p))
|
|
472
|
+
if (opts.save) {
|
|
473
|
+
const written = saveVersionPostmortem(p)
|
|
474
|
+
if (written.saved) process.stdout.write(`\n saved: ${written.paths.json}\n series: ${written.paths.index}\n`)
|
|
475
|
+
else {
|
|
476
|
+
console.error(`warn: version post-mortem computed but NOT saved — ${written.error}`)
|
|
477
|
+
return 1
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
return 0
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (isMainModule(import.meta.url)) {
|
|
484
|
+
process.exitCode = main(process.argv.slice(2))
|
|
485
|
+
}
|