@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,488 @@
|
|
|
1
|
+
// Correctness-only run finalization. Diagnostics are deliberately outside this gate.
|
|
2
|
+
import { execFileSync, spawnSync } from 'node:child_process'
|
|
3
|
+
|
|
4
|
+
import { deliveryForUnit, terminalDisposition, terminalDispositionTally } from './terminal-dispositions.mjs'
|
|
5
|
+
import { effectiveUnitKind, unitKindComplete } from './unit-lifecycle.mjs'
|
|
6
|
+
import { validateComponentCandidates } from './component-candidate.mjs'
|
|
7
|
+
|
|
8
|
+
const isObject = (value) => value != null && typeof value === 'object' && !Array.isArray(value)
|
|
9
|
+
const arr = (value) => (Array.isArray(value) ? value.filter(isObject) : [])
|
|
10
|
+
const nonEmpty = (value) => typeof value === 'string' && value.trim().length > 0
|
|
11
|
+
const sha7 = (value) => nonEmpty(value) ? value.slice(0, 7) : '(none)'
|
|
12
|
+
const refNumber = (value) => {
|
|
13
|
+
if (Number.isInteger(value) && value > 0) return value
|
|
14
|
+
const match = String(value ?? '').match(/(?:^|#)(\d+)$/)
|
|
15
|
+
return match ? Number(match[1]) : null
|
|
16
|
+
}
|
|
17
|
+
const componentUnitIds = (component) => (Array.isArray(component?.units) ? component.units : [])
|
|
18
|
+
.map((unit) => typeof unit === 'string' ? unit : unit?.id).filter(nonEmpty)
|
|
19
|
+
|
|
20
|
+
function normalizedGithubObservation(kind, requestedNumber, live) {
|
|
21
|
+
if (!isObject(live)) throw new Error(`${kind} #${requestedNumber} returned no structured observation`)
|
|
22
|
+
if (!Number.isInteger(live.number) || live.number <= 0) {
|
|
23
|
+
throw new Error(`${kind} #${requestedNumber} returned missing or malformed numeric identity`)
|
|
24
|
+
}
|
|
25
|
+
if (live.number !== requestedNumber) {
|
|
26
|
+
throw new Error(`${kind} #${requestedNumber} returned identity ${live.number}`)
|
|
27
|
+
}
|
|
28
|
+
return { ...structuredClone(live), number: requestedNumber }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function expectedIssueState(unit, disposition) {
|
|
32
|
+
if (disposition === 'delivered' || disposition === 'already-done' || disposition === 'descoped') return 'CLOSED'
|
|
33
|
+
if (disposition === 'handed-back') return null
|
|
34
|
+
const kind = effectiveUnitKind(unit)
|
|
35
|
+
if (kind !== 'implementation') return unitKindComplete(unit) ? 'CLOSED' : 'OPEN'
|
|
36
|
+
return null
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function expectedPrState(disposition, scopeDecision) {
|
|
40
|
+
if (disposition === 'descoped') return { state: 'CLOSED', merged: false, label: scopeDecision?.prDisposition ?? 'closed-unmerged' }
|
|
41
|
+
if (disposition === 'delivered') return { terminal: true, label: 'closed or merged after component delivery' }
|
|
42
|
+
if (disposition === 'handed-back') return { merged: false, label: 'open or closed unmerged hand-back' }
|
|
43
|
+
if (disposition === 'already-done') return { terminal: true, label: 'closed or merged prior art' }
|
|
44
|
+
return null
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function workspaceErrors(cleanup) {
|
|
48
|
+
const errors = []
|
|
49
|
+
if (!isObject(cleanup)) return ['workspace cleanup returned no structured result']
|
|
50
|
+
if (nonEmpty(cleanup.commandError)) errors.push(`workspace cleanup command failed: ${cleanup.commandError}`)
|
|
51
|
+
if (cleanup.collection?.ran !== true) errors.push(`workspace orphan collection did not run: ${cleanup.collection?.reason ?? 'no reason recorded'}`)
|
|
52
|
+
for (const failure of Array.isArray(cleanup.removalFailures) ? cleanup.removalFailures : []) {
|
|
53
|
+
errors.push(`workspace removal failed: ${typeof failure === 'string' ? failure : failure?.path ?? JSON.stringify(failure)}`)
|
|
54
|
+
}
|
|
55
|
+
const byPath = new Map(arr(cleanup.decisions).map((row) => [row.path, row]))
|
|
56
|
+
for (const remaining of Array.isArray(cleanup.remaining) ? cleanup.remaining : []) {
|
|
57
|
+
const path = typeof remaining === 'string' ? remaining : remaining?.path
|
|
58
|
+
const row = byPath.get(path) ?? (isObject(remaining) ? remaining : null)
|
|
59
|
+
if (/orchestrator's own checkout|orchestrator's tree/i.test(row?.reason ?? '')) continue
|
|
60
|
+
if (row?.unpublished === true || /unpublished work|uncommitted|untracked|on no remote/i.test(row?.reason ?? '')) {
|
|
61
|
+
errors.push(`unpublished work was preserved, never discarded, at ${path}: ${row?.reason ?? 'reason not recorded'}`)
|
|
62
|
+
} else {
|
|
63
|
+
errors.push(`unreaped workspace remains at ${path}: ${row?.reason ?? 'cleanup did not remove it'}`)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return errors
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// A non-implementation unit is settled two ways, not one: it completed its kind's own protocol,
|
|
70
|
+
// or a human descoped it. `record-scope-decision` never restricted descoping by kind, so a run
|
|
71
|
+
// could record an honest descope that finalization then refused forever (#1087) — and
|
|
72
|
+
// `external-verification`, the kind that exists precisely for work no agent session can perform,
|
|
73
|
+
// is the kind most likely to need one. This is not a bypass: `terminalDisposition` re-validates
|
|
74
|
+
// the whole scope decision on every read, so a loop-applied, non-human, or delivery-claiming
|
|
75
|
+
// "descope" resolves to null and the unit stays pending.
|
|
76
|
+
const nonImplementationSettled = (unit, manifest) => unitKindComplete(unit)
|
|
77
|
+
|| terminalDisposition(unit, manifest) === 'descoped'
|
|
78
|
+
|
|
79
|
+
const descopedIn = (roster, manifest) => roster
|
|
80
|
+
.filter((unit) => !unitKindComplete(unit) && terminalDisposition(unit, manifest) === 'descoped')
|
|
81
|
+
|
|
82
|
+
function scopeTally(roster, manifest) {
|
|
83
|
+
return {
|
|
84
|
+
total: roster.length,
|
|
85
|
+
complete: roster.filter(unitKindComplete).length,
|
|
86
|
+
descoped: descopedIn(roster, manifest).length,
|
|
87
|
+
pending: roster.filter((unit) => !nonImplementationSettled(unit, manifest)).length,
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// The two scope rosters, named once: the record, its read-back validator and the rendered handoff
|
|
92
|
+
// all derive from these, so a finalized run can never be rejected by a tally computed differently
|
|
93
|
+
// from the one that was written.
|
|
94
|
+
const SCOPE_ROSTERS = [
|
|
95
|
+
['external', 'external verification', (unit) => effectiveUnitKind(unit) === 'external-verification'],
|
|
96
|
+
['humanOrPrerequisite', 'human decisions/prerequisites', (unit) => ['human-decision', 'prerequisite'].includes(effectiveUnitKind(unit))],
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
function scopeRecord(units, manifest) {
|
|
100
|
+
return Object.fromEntries(SCOPE_ROSTERS.map(([key, , belongs]) => [key, scopeTally(units.filter(belongs), manifest)]))
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// A run finalized before `descoped` joined the tally recorded three counts, not four. Absent
|
|
104
|
+
// reads as 0 — the true historical value, since a descoped non-implementation unit could not
|
|
105
|
+
// finalize at all then. Field-by-field so the comparison is order-independent and all four
|
|
106
|
+
// numbers stay strictly checked rather than the whole block being waved through.
|
|
107
|
+
const normalizedScopeTally = (tally) => ({
|
|
108
|
+
total: tally?.total, complete: tally?.complete, descoped: tally?.descoped ?? 0, pending: tally?.pending,
|
|
109
|
+
})
|
|
110
|
+
// Driven off SCOPE_ROSTERS, never a second hand-written key list: a roster named in one place and
|
|
111
|
+
// forgotten in the other would silently drop out of the staleness comparison.
|
|
112
|
+
const normalizedScope = (scope) => Object.fromEntries(
|
|
113
|
+
SCOPE_ROSTERS.map(([key]) => [key, normalizedScopeTally(scope?.[key])]))
|
|
114
|
+
|
|
115
|
+
function autonomousDecisions(units) {
|
|
116
|
+
const rows = []
|
|
117
|
+
for (const unit of units) {
|
|
118
|
+
for (const decision of [...arr(unit.breakerLog), ...(isObject(unit.breaker) ? [unit.breaker] : [])]) {
|
|
119
|
+
if (decision.autoApplied === true) rows.push({
|
|
120
|
+
unit: unit.id,
|
|
121
|
+
decision: decision.decision,
|
|
122
|
+
signal: decision.signal ?? 'not recorded',
|
|
123
|
+
autonomyAtDecision: decision.autonomyAtDecision ?? null,
|
|
124
|
+
decidedAt: decision.decidedAt ?? null,
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return rows
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function renderFinalHandoff(manifest, finalization) {
|
|
132
|
+
const units = arr(manifest.units)
|
|
133
|
+
const byId = new Map(units.map((unit) => [unit.id, unit]))
|
|
134
|
+
const lines = [
|
|
135
|
+
'FINAL HANDOFF',
|
|
136
|
+
`run ${manifest.runSlug ?? '(unknown)'} -> ${manifest.targetBranch ?? '(unknown target)'} @ ${sha7(finalization.target?.headSha)}`,
|
|
137
|
+
'',
|
|
138
|
+
'Delivery:',
|
|
139
|
+
]
|
|
140
|
+
for (const unit of units.filter((entry) => effectiveUnitKind(entry) === 'implementation')) {
|
|
141
|
+
const disposition = terminalDisposition(unit, manifest)
|
|
142
|
+
const delivery = deliveryForUnit(unit, manifest)
|
|
143
|
+
if (disposition === 'delivered') lines.push(` - ${unit.id}: delivered via component ${unit.deliveryRef?.componentId ?? '(legacy unit)'} @ ${sha7(delivery?.mergedSha)}`)
|
|
144
|
+
else if (disposition === 'handed-back') lines.push(` - ${unit.id}: handed back - ${delivery?.blockedOn ?? 'reason not recorded'}`)
|
|
145
|
+
else if (disposition === 'already-done') lines.push(` - ${unit.id}: already done (verified prior art)`)
|
|
146
|
+
else if (disposition === 'descoped') lines.push(` - ${unit.id}: human-descoped - ${unit.scopeDecision?.reason ?? 'reason not recorded'}`)
|
|
147
|
+
}
|
|
148
|
+
for (const component of arr(manifest.components)) {
|
|
149
|
+
if (componentUnitIds(component).some((id) => terminalDisposition(byId.get(id), manifest) === 'delivered')) {
|
|
150
|
+
lines.push(` - component ${component.id}: merged ${sha7(component.delivery?.mergedSha ?? component.mergedSha)}`)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
lines.push('', 'Scope:')
|
|
155
|
+
for (const [, label, belongs] of SCOPE_ROSTERS) {
|
|
156
|
+
const roster = units.filter(belongs)
|
|
157
|
+
const tally = scopeTally(roster, manifest)
|
|
158
|
+
lines.push(` - ${label}: ${tally.complete} complete, ${tally.descoped ? `${tally.descoped} descoped, ` : ''}${tally.pending} pending`)
|
|
159
|
+
// A count nobody can act on is not a handoff: name each unit the human removed, and why.
|
|
160
|
+
for (const unit of descopedIn(roster, manifest)) {
|
|
161
|
+
lines.push(` - ${unit.id}: human-descoped - ${unit.scopeDecision?.reason ?? 'reason not recorded'}`)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
lines.push('', 'Cleanup:')
|
|
166
|
+
lines.push(` - original branch: ${finalization.branchRestore.expected ?? 'not applicable'} (${finalization.branchRestore.restored ? 'restored' : 'not restored'})`)
|
|
167
|
+
const cleanupRows = arr(finalization.cleanup?.decisions)
|
|
168
|
+
lines.push(` - workspaces: ${cleanupRows.filter((row) => row.verdict === 'absent').length} absent, ${cleanupRows.filter((row) => row.verdict === 'reap').length} reaped, ${cleanupRows.filter((row) => row.verdict === 'preserve').length} preserved`)
|
|
169
|
+
|
|
170
|
+
lines.push('', 'Deferrals and known debt:')
|
|
171
|
+
const deferrals = arr(manifest.deferrals)
|
|
172
|
+
if (!deferrals.length) lines.push(' - none recorded')
|
|
173
|
+
else for (const row of deferrals) lines.push(` - ${row.requirement ?? '(unnamed requirement)'} -> ${row.trackedIn ?? '(untracked)'}`)
|
|
174
|
+
|
|
175
|
+
lines.push('', 'Autonomous breaker decisions:')
|
|
176
|
+
if (!finalization.autonomousBreakerDecisions.length) lines.push(' - none')
|
|
177
|
+
else for (const row of finalization.autonomousBreakerDecisions) {
|
|
178
|
+
lines.push(` - ${row.unit}: ${row.decision} (${row.signal})${row.autonomyAtDecision ? ` under ${row.autonomyAtDecision}` : ''}`)
|
|
179
|
+
}
|
|
180
|
+
return lines.join('\n')
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export async function finalizeRun(manifest, boundary) {
|
|
184
|
+
const input = structuredClone(manifest)
|
|
185
|
+
const errors = []
|
|
186
|
+
const units = arr(input.units)
|
|
187
|
+
if (input.finalization !== undefined) errors.push('run is already finalized; finalization evidence is immutable')
|
|
188
|
+
if (!units.length) errors.push('finalize requires at least one typed unit')
|
|
189
|
+
const implementations = units.filter((unit) => effectiveUnitKind(unit) === 'implementation')
|
|
190
|
+
if (input.completionAudit !== undefined) {
|
|
191
|
+
errors.push('caller-authored completionAudit cannot authorize finalization; every completion fact is re-established live')
|
|
192
|
+
}
|
|
193
|
+
for (const unit of implementations) {
|
|
194
|
+
const disposition = terminalDisposition(unit, input)
|
|
195
|
+
if (!['delivered', 'handed-back', 'already-done', 'descoped'].includes(disposition)) {
|
|
196
|
+
errors.push(`${unit.id ?? '(unknown unit)'} has no honest in-scope terminal disposition (expected delivered, handed-back, already-done, or human-descoped)`)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
errors.push(...validateComponentCandidates(input).map((error) => `component candidate: ${error}`))
|
|
200
|
+
for (const unit of units.filter((entry) => effectiveUnitKind(entry) !== 'implementation')) {
|
|
201
|
+
if (!nonImplementationSettled(unit, input)) errors.push(`${unit.id ?? '(unknown unit)'} ${effectiveUnitKind(unit)} is still pending and prevents whole-run finalization`)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
let target = null
|
|
205
|
+
try { target = await boundary.fetchTarget(input.targetBranch) } catch (error) { errors.push(`fresh target fetch failed: ${error.message ?? error}`) }
|
|
206
|
+
if (!isObject(target) || !nonEmpty(target.ref) || !nonEmpty(target.headSha)) errors.push('fresh target fetch returned no target ref/head SHA')
|
|
207
|
+
|
|
208
|
+
const ancestry = []
|
|
209
|
+
const ancestryClaims = []
|
|
210
|
+
const byId = new Map(units.map((unit) => [unit.id, unit]))
|
|
211
|
+
const deliveredComponents = arr(input.components).filter((component) => componentUnitIds(component)
|
|
212
|
+
.some((id) => terminalDisposition(byId.get(id), input) === 'delivered'))
|
|
213
|
+
const canonicallyDeliveredUnits = new Set(deliveredComponents
|
|
214
|
+
.filter((component) => component.delivery?.outcome === 'merged')
|
|
215
|
+
.flatMap(componentUnitIds))
|
|
216
|
+
for (const unit of implementations) {
|
|
217
|
+
if (terminalDisposition(unit, input) === 'delivered' && !canonicallyDeliveredUnits.has(unit.id)) {
|
|
218
|
+
ancestryClaims.push({ kind: 'unit', id: unit.id, sha: deliveryForUnit(unit, input)?.mergedSha })
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
for (const component of deliveredComponents) {
|
|
222
|
+
const mergedSha = component.delivery?.mergedSha ?? component.mergedSha
|
|
223
|
+
if (!nonEmpty(mergedSha)) errors.push(`delivered component ${component.id ?? '(unknown)'} has no recorded merge SHA`)
|
|
224
|
+
else ancestryClaims.push({ kind: 'component', id: component.id, sha: mergedSha })
|
|
225
|
+
}
|
|
226
|
+
if (target?.ref) {
|
|
227
|
+
for (const claim of ancestryClaims) {
|
|
228
|
+
let contained = false
|
|
229
|
+
try { contained = await boundary.isAncestor(claim.sha, target.ref) } catch (error) { errors.push(`ancestry check failed for ${claim.kind} ${claim.id}: ${error.message ?? error}`) }
|
|
230
|
+
ancestry.push({ ...claim, targetRef: target.ref, contained })
|
|
231
|
+
if (!contained) errors.push(`${claim.kind} ${claim.id} merge SHA ${claim.sha} is not an ancestor of freshly fetched ${target.ref}`)
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const github = { issues: [], prs: [] }
|
|
236
|
+
for (const unit of units) {
|
|
237
|
+
const issue = refNumber(unit.issue ?? unit.id)
|
|
238
|
+
if (issue == null) { errors.push(`${unit.id ?? '(unknown unit)'} has no GitHub issue reference for live terminal verification`); continue }
|
|
239
|
+
try {
|
|
240
|
+
const observed = await boundary.readIssue(issue, input.repository)
|
|
241
|
+
const live = normalizedGithubObservation('issue', issue, observed)
|
|
242
|
+
github.issues.push({ ...live, unit: unit.id, number: issue })
|
|
243
|
+
const disposition = terminalDisposition(unit, input)
|
|
244
|
+
const expected = expectedIssueState(unit, disposition)
|
|
245
|
+
if (expected && live.state !== expected) errors.push(`${unit.id} issue is ${live.state ?? 'unreadable'}, expected ${expected} for ${disposition ?? effectiveUnitKind(unit)}`)
|
|
246
|
+
if (disposition === 'descoped' && unit.scopeDecision?.issueDisposition === 'closed-not-planned'
|
|
247
|
+
&& live.stateReason !== undefined && live.stateReason !== null && live.stateReason !== 'NOT_PLANNED') {
|
|
248
|
+
errors.push(`${unit.id} issue state reason is ${live.stateReason}, expected NOT_PLANNED for closed-not-planned descope`)
|
|
249
|
+
}
|
|
250
|
+
} catch (error) { errors.push(`${unit.id} issue could not be verified live: ${error.message ?? error}`) }
|
|
251
|
+
|
|
252
|
+
const pr = refNumber(unit.pr)
|
|
253
|
+
if (pr != null) {
|
|
254
|
+
try {
|
|
255
|
+
const observed = await boundary.readPr(pr, input.repository)
|
|
256
|
+
const live = normalizedGithubObservation('PR', pr, observed)
|
|
257
|
+
github.prs.push({ ...live, unit: unit.id, role: 'unit', number: pr })
|
|
258
|
+
const expected = expectedPrState(terminalDisposition(unit, input), unit.scopeDecision)
|
|
259
|
+
if (expected?.merged === false && live.merged === true) errors.push(`${unit.id} PR #${pr} is merged, expected ${expected.label}`)
|
|
260
|
+
if (expected?.state && live.state !== expected.state) errors.push(`${unit.id} PR #${pr} is ${live.state}, expected ${expected.state} (${expected.label})`)
|
|
261
|
+
if (expected?.terminal && !['CLOSED', 'MERGED'].includes(live.state)) errors.push(`${unit.id} PR #${pr} is ${live.state}, expected terminal CLOSED or MERGED`)
|
|
262
|
+
} catch (error) { errors.push(`${unit.id} PR #${pr} could not be verified live: ${error.message ?? error}`) }
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
for (const component of arr(input.components)) {
|
|
266
|
+
const pr = refNumber(component.pr)
|
|
267
|
+
if (pr == null) continue
|
|
268
|
+
try {
|
|
269
|
+
const observed = await boundary.readPr(pr, input.repository)
|
|
270
|
+
const live = normalizedGithubObservation('PR', pr, observed)
|
|
271
|
+
github.prs.push({ ...live, component: component.id, role: 'component', number: pr })
|
|
272
|
+
const hasDelivered = componentUnitIds(component).some((id) => terminalDisposition(byId.get(id), input) === 'delivered')
|
|
273
|
+
if (hasDelivered && (live.state !== 'MERGED' || live.merged !== true)) errors.push(`delivered component ${component.id} PR #${pr} is ${live.state}, expected MERGED`)
|
|
274
|
+
const expectedMergeSha = component.delivery?.mergedSha ?? component.mergedSha
|
|
275
|
+
if (hasDelivered && nonEmpty(expectedMergeSha) && live.mergeCommitSha !== expectedMergeSha) {
|
|
276
|
+
errors.push(`delivered component ${component.id} PR #${pr} merge SHA is ${live.mergeCommitSha ?? 'missing'}, expected ${expectedMergeSha}`)
|
|
277
|
+
}
|
|
278
|
+
if (!hasDelivered && live.merged === true) errors.push(`non-delivered component ${component.id} PR #${pr} is unexpectedly merged`)
|
|
279
|
+
} catch (error) { errors.push(`component ${component.id} PR #${pr} could not be verified live: ${error.message ?? error}`) }
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
let branchRestore = null
|
|
283
|
+
try { branchRestore = await boundary.restoreOriginalBranch(input.originalBranch) } catch (error) { errors.push(`original branch restoration failed: ${error.message ?? error}`) }
|
|
284
|
+
if (!isObject(branchRestore) || branchRestore.restored !== true || branchRestore.observed !== input.originalBranch) {
|
|
285
|
+
errors.push(`original branch was not restored to ${input.originalBranch ?? '(not recorded)'} (observed ${branchRestore?.observed ?? 'unknown'})`)
|
|
286
|
+
}
|
|
287
|
+
if (Array.isArray(branchRestore?.unpublished) && branchRestore.unpublished.length) {
|
|
288
|
+
errors.push(`original branch restoration refused with unpublished checkout work: ${branchRestore.unpublished.join(', ')}`)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
let cleanup = null
|
|
292
|
+
if (errors.length === 0) {
|
|
293
|
+
try { cleanup = await boundary.cleanupWorkspaces(input) } catch (error) { errors.push(`workspace cleanup failed: ${error.message ?? error}`) }
|
|
294
|
+
errors.push(...workspaceErrors(cleanup))
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const tally = terminalDispositionTally(implementations, input)
|
|
298
|
+
const finalization = {
|
|
299
|
+
status: 'finalized', finalizedAt: boundary.now(),
|
|
300
|
+
target, ancestry, github, branchRestore, cleanup,
|
|
301
|
+
delivery: tally,
|
|
302
|
+
scope: scopeRecord(units, input),
|
|
303
|
+
deferrals: structuredClone(Array.isArray(input.deferrals) ? input.deferrals : []),
|
|
304
|
+
autonomousBreakerDecisions: autonomousDecisions(units),
|
|
305
|
+
}
|
|
306
|
+
const resultManifest = structuredClone(input)
|
|
307
|
+
if (errors.length) return { ok: false, errors, manifest: resultManifest, target, ancestry, github, branchRestore, cleanup, handoff: null }
|
|
308
|
+
delete resultManifest.completionAudit
|
|
309
|
+
finalization.handoff = renderFinalHandoff(resultManifest, finalization)
|
|
310
|
+
resultManifest.finalization = finalization
|
|
311
|
+
const candidateErrors = finalizationErrors(resultManifest)
|
|
312
|
+
if (candidateErrors.length) {
|
|
313
|
+
return { ok: false, errors: candidateErrors, manifest: structuredClone(input), target, ancestry, github, branchRestore, cleanup, handoff: null }
|
|
314
|
+
}
|
|
315
|
+
return { ok: true, errors: [], manifest: resultManifest, target, ancestry, github, branchRestore, cleanup, handoff: finalization.handoff }
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function expectedAncestryClaims(manifest) {
|
|
319
|
+
const units = arr(manifest?.units)
|
|
320
|
+
const implementations = units.filter((unit) => effectiveUnitKind(unit) === 'implementation')
|
|
321
|
+
const byId = new Map(units.map((unit) => [unit.id, unit]))
|
|
322
|
+
const claims = []
|
|
323
|
+
const deliveredComponents = arr(manifest?.components).filter((component) => componentUnitIds(component)
|
|
324
|
+
.some((id) => terminalDisposition(byId.get(id), manifest) === 'delivered'))
|
|
325
|
+
const canonicalMembers = new Set(deliveredComponents
|
|
326
|
+
.filter((component) => component.delivery?.outcome === 'merged')
|
|
327
|
+
.flatMap(componentUnitIds))
|
|
328
|
+
for (const unit of implementations) {
|
|
329
|
+
if (terminalDisposition(unit, manifest) === 'delivered' && !canonicalMembers.has(unit.id)) {
|
|
330
|
+
claims.push({ kind: 'unit', id: unit.id, sha: deliveryForUnit(unit, manifest)?.mergedSha })
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
for (const component of deliveredComponents) {
|
|
334
|
+
claims.push({ kind: 'component', id: component.id, sha: component.delivery?.mergedSha ?? component.mergedSha })
|
|
335
|
+
}
|
|
336
|
+
return claims
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function ancestryRecordErrors(manifest, record) {
|
|
340
|
+
const expected = expectedAncestryClaims(manifest)
|
|
341
|
+
const actual = arr(record?.ancestry)
|
|
342
|
+
const normalized = (rows) => rows.map(({ kind, id, sha }) => ({ kind, id, sha }))
|
|
343
|
+
if (JSON.stringify(normalized(actual)) !== JSON.stringify(normalized(expected))) {
|
|
344
|
+
return ['finalization.ancestry is stale or does not match the delivered unit/component claim set']
|
|
345
|
+
}
|
|
346
|
+
if (actual.some((row) => row.contained !== true || row.targetRef !== record.target?.ref)) {
|
|
347
|
+
return ['finalization.ancestry is stale or does not prove every claim against finalization.target.ref']
|
|
348
|
+
}
|
|
349
|
+
return []
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function githubRecordErrors(manifest, record) {
|
|
353
|
+
const errors = []
|
|
354
|
+
const units = arr(manifest?.units)
|
|
355
|
+
const byId = new Map(units.map((unit) => [unit.id, unit]))
|
|
356
|
+
const expectedIssues = units.map((unit) => ({ unit, number: refNumber(unit.issue ?? unit.id) }))
|
|
357
|
+
const issues = arr(record?.github?.issues)
|
|
358
|
+
const issueKeys = issues.map((row) => `${row.unit}:${row.number}`)
|
|
359
|
+
const expectedIssueKeys = expectedIssues.map(({ unit, number }) => `${unit.id}:${number}`)
|
|
360
|
+
if (expectedIssues.some(({ number }) => number == null)
|
|
361
|
+
|| issues.some((row) => !Number.isInteger(row.number) || row.number <= 0)
|
|
362
|
+
|| JSON.stringify(issueKeys) !== JSON.stringify(expectedIssueKeys)) {
|
|
363
|
+
errors.push('finalization GitHub issue observations are stale or incomplete')
|
|
364
|
+
} else {
|
|
365
|
+
for (let index = 0; index < expectedIssues.length; index++) {
|
|
366
|
+
const { unit } = expectedIssues[index]
|
|
367
|
+
const live = issues[index]
|
|
368
|
+
const disposition = terminalDisposition(unit, manifest)
|
|
369
|
+
const expectedState = expectedIssueState(unit, disposition)
|
|
370
|
+
if (expectedState && live.state !== expectedState) errors.push('finalization GitHub issue observations are stale or incomplete')
|
|
371
|
+
if (disposition === 'descoped' && unit.scopeDecision?.issueDisposition === 'closed-not-planned'
|
|
372
|
+
&& live.stateReason !== undefined && live.stateReason !== null && live.stateReason !== 'NOT_PLANNED') {
|
|
373
|
+
errors.push('finalization GitHub issue observations are stale or incomplete')
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const expectedPrs = []
|
|
379
|
+
for (const unit of units) {
|
|
380
|
+
const number = refNumber(unit.pr)
|
|
381
|
+
if (number != null) expectedPrs.push({ role: 'unit', unit, number })
|
|
382
|
+
}
|
|
383
|
+
for (const component of arr(manifest?.components)) {
|
|
384
|
+
const number = refNumber(component.pr)
|
|
385
|
+
if (number != null) expectedPrs.push({ role: 'component', component, number })
|
|
386
|
+
}
|
|
387
|
+
const prs = arr(record?.github?.prs)
|
|
388
|
+
const prKey = (row) => row.role === 'unit'
|
|
389
|
+
? `unit:${row.unit}:${row.number}`
|
|
390
|
+
: `component:${row.component}:${row.number}`
|
|
391
|
+
const expectedPrKeys = expectedPrs.map((row) => row.role === 'unit'
|
|
392
|
+
? `unit:${row.unit.id}:${row.number}`
|
|
393
|
+
: `component:${row.component.id}:${row.number}`)
|
|
394
|
+
if (prs.some((row) => !Number.isInteger(row.number) || row.number <= 0)
|
|
395
|
+
|| JSON.stringify(prs.map(prKey)) !== JSON.stringify(expectedPrKeys)) {
|
|
396
|
+
errors.push('finalization GitHub PR observations are stale or incomplete')
|
|
397
|
+
} else {
|
|
398
|
+
for (let index = 0; index < expectedPrs.length; index++) {
|
|
399
|
+
const expected = expectedPrs[index]
|
|
400
|
+
const live = prs[index]
|
|
401
|
+
if (expected.role === 'unit') {
|
|
402
|
+
const terminal = expectedPrState(terminalDisposition(expected.unit, manifest), expected.unit.scopeDecision)
|
|
403
|
+
if (terminal?.merged === false && live.merged === true) errors.push('finalization GitHub PR observations are stale or incomplete')
|
|
404
|
+
if (terminal?.state && live.state !== terminal.state) errors.push('finalization GitHub PR observations are stale or incomplete')
|
|
405
|
+
if (terminal?.terminal && !['CLOSED', 'MERGED'].includes(live.state)) errors.push('finalization GitHub PR observations are stale or incomplete')
|
|
406
|
+
} else {
|
|
407
|
+
const delivered = componentUnitIds(expected.component).some((id) => terminalDisposition(byId.get(id), manifest) === 'delivered')
|
|
408
|
+
const expectedMergeSha = expected.component.delivery?.mergedSha ?? expected.component.mergedSha
|
|
409
|
+
if (delivered && (live.state !== 'MERGED' || live.merged !== true || live.mergeCommitSha !== expectedMergeSha)) {
|
|
410
|
+
errors.push('finalization GitHub PR observations are stale or incomplete')
|
|
411
|
+
}
|
|
412
|
+
if (!delivered && live.merged === true) errors.push('finalization GitHub PR observations are stale or incomplete')
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return [...new Set(errors)]
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
export function finalizationErrors(manifest) {
|
|
420
|
+
const record = manifest?.finalization
|
|
421
|
+
if (record === undefined) return []
|
|
422
|
+
if (!isObject(record)) return ['finalization must be an object written by the dedicated finalize command']
|
|
423
|
+
const errors = []
|
|
424
|
+
if (record.status !== 'finalized') errors.push('finalization.status must be "finalized"')
|
|
425
|
+
if (!nonEmpty(record.finalizedAt)) errors.push('finalization.finalizedAt is required')
|
|
426
|
+
if (!nonEmpty(record.target?.ref) || !nonEmpty(record.target?.headSha)) errors.push('finalization.target requires the freshly fetched ref and headSha')
|
|
427
|
+
if (!Array.isArray(record.ancestry) || record.ancestry.some((row) => !isObject(row) || row.contained !== true || !nonEmpty(row.sha))) {
|
|
428
|
+
errors.push('finalization.ancestry must contain only proved merge-SHA ancestry rows')
|
|
429
|
+
}
|
|
430
|
+
if (!isObject(record.github) || !Array.isArray(record.github.issues) || !Array.isArray(record.github.prs)) errors.push('finalization.github requires live issue and PR observations')
|
|
431
|
+
errors.push(...ancestryRecordErrors(manifest, record))
|
|
432
|
+
errors.push(...githubRecordErrors(manifest, record))
|
|
433
|
+
if (!isObject(record.branchRestore) || record.branchRestore.restored !== true || record.branchRestore.observed !== manifest.originalBranch) {
|
|
434
|
+
errors.push('finalization.branchRestore must prove the recorded originalBranch was restored')
|
|
435
|
+
}
|
|
436
|
+
errors.push(...workspaceErrors(record.cleanup).map((error) => `finalization: ${error}`))
|
|
437
|
+
const units = arr(manifest?.units)
|
|
438
|
+
const implementations = units.filter((unit) => effectiveUnitKind(unit) === 'implementation')
|
|
439
|
+
const expectedDelivery = terminalDispositionTally(implementations, manifest)
|
|
440
|
+
if (JSON.stringify(record.delivery) !== JSON.stringify(expectedDelivery)) errors.push('finalization.delivery is stale or does not match current typed dispositions')
|
|
441
|
+
const expectedScope = scopeRecord(units, manifest)
|
|
442
|
+
if (JSON.stringify(normalizedScope(record.scope)) !== JSON.stringify(normalizedScope(expectedScope))) errors.push('finalization.scope is stale or does not match current non-implementation completion')
|
|
443
|
+
if (expectedScope.external.pending || expectedScope.humanOrPrerequisite.pending) errors.push('finalization cannot coexist with pending external/human/prerequisite units')
|
|
444
|
+
if (JSON.stringify(record.deferrals) !== JSON.stringify(Array.isArray(manifest?.deferrals) ? manifest.deferrals : [])) errors.push('finalization.deferrals is stale')
|
|
445
|
+
if (JSON.stringify(record.autonomousBreakerDecisions) !== JSON.stringify(autonomousDecisions(units))) errors.push('finalization autonomous breaker decisions are stale')
|
|
446
|
+
const expectedHandoff = renderFinalHandoff(manifest, record)
|
|
447
|
+
if (record.handoff !== expectedHandoff) errors.push('finalization.handoff was not rendered from the current validated state')
|
|
448
|
+
return errors
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function runJson(command, args, cwd) {
|
|
452
|
+
return JSON.parse(execFileSync(command, args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }))
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
export function createLiveFinalizeBoundary({ cwd = process.cwd(), cleanupWorkspaces, now = () => new Date().toISOString() } = {}) {
|
|
456
|
+
return {
|
|
457
|
+
now,
|
|
458
|
+
fetchTarget: async (branch) => {
|
|
459
|
+
if (!nonEmpty(branch)) throw new Error('targetBranch is not recorded')
|
|
460
|
+
execFileSync('git', ['fetch', 'origin', branch], { cwd, stdio: ['ignore', 'ignore', 'pipe'] })
|
|
461
|
+
const ref = `origin/${branch}`
|
|
462
|
+
const headSha = execFileSync('git', ['rev-parse', ref], { cwd, encoding: 'utf8' }).trim()
|
|
463
|
+
return { remote: 'origin', branch, ref, headSha, fetchedAt: now() }
|
|
464
|
+
},
|
|
465
|
+
isAncestor: async (sha, ref) => spawnSync('git', ['merge-base', '--is-ancestor', sha, ref], { cwd }).status === 0,
|
|
466
|
+
readIssue: async (id, repository) => {
|
|
467
|
+
const number = refNumber(id)
|
|
468
|
+
if (number == null || !nonEmpty(repository)) throw new Error('issue number and repository are required')
|
|
469
|
+
const view = runJson('gh', ['issue', 'view', String(number), '--repo', repository, '--json', 'number,state,stateReason,url'], cwd)
|
|
470
|
+
return { id, number: view.number, state: view.state, stateReason: view.stateReason ?? null, url: view.url }
|
|
471
|
+
},
|
|
472
|
+
readPr: async (id, repository) => {
|
|
473
|
+
if (!Number.isInteger(id) || !nonEmpty(repository)) throw new Error('PR number and repository are required')
|
|
474
|
+
const view = runJson('gh', ['pr', 'view', String(id), '--repo', repository, '--json', 'number,state,mergedAt,mergeCommit,url,headRefOid'], cwd)
|
|
475
|
+
return { id, number: view.number, state: view.state, merged: view.state === 'MERGED' || nonEmpty(view.mergedAt), mergedAt: view.mergedAt ?? null, mergeCommitSha: view.mergeCommit?.oid ?? null, headSha: view.headRefOid ?? null, url: view.url }
|
|
476
|
+
},
|
|
477
|
+
restoreOriginalBranch: async (expected) => {
|
|
478
|
+
const before = execFileSync('git', ['branch', '--show-current'], { cwd, encoding: 'utf8' }).trim() || null
|
|
479
|
+
if (expected == null) return { expected: null, before, observed: null, restored: true, unpublished: [], reason: 'detached/disposable checkout recorded at pre-flight' }
|
|
480
|
+
const status = execFileSync('git', ['status', '--porcelain'], { cwd, encoding: 'utf8' }).split('\n').filter(Boolean)
|
|
481
|
+
if (status.length) return { expected, before, observed: before, restored: before === expected, unpublished: status }
|
|
482
|
+
if (before !== expected) execFileSync('git', ['switch', expected], { cwd, stdio: ['ignore', 'ignore', 'pipe'] })
|
|
483
|
+
const observed = execFileSync('git', ['branch', '--show-current'], { cwd, encoding: 'utf8' }).trim() || null
|
|
484
|
+
return { expected, before, observed, restored: observed === expected, unpublished: [] }
|
|
485
|
+
},
|
|
486
|
+
cleanupWorkspaces: cleanupWorkspaces ?? (async () => { throw new Error('no workspace cleanup boundary configured') }),
|
|
487
|
+
}
|
|
488
|
+
}
|