@1aboveio/skills 0.0.0-stage → 1.0.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/README.md +30 -2
- package/bin/cli.mjs +14 -0
- package/bin/select-core.mjs +172 -0
- package/package.json +146 -4
- package/skills/app-dev/airflow/LICENSE +3 -0
- package/skills/app-dev/airflow/SKILL.md +199 -0
- package/skills/app-dev/airflow/release-check.mjs +93 -0
- package/skills/app-dev/android-foundation/LICENSE +3 -0
- package/skills/app-dev/android-foundation/SKILL.md +192 -0
- package/skills/app-dev/android-foundation/assets/android-ci-job.yml +217 -0
- package/skills/app-dev/android-foundation/references/ci.md +87 -0
- package/skills/app-dev/android-foundation/references/gotchas.md +221 -0
- package/skills/app-dev/android-foundation/references/setup.md +164 -0
- package/skills/app-dev/android-foundation/release-check.mjs +93 -0
- package/skills/app-dev/android-foundation/scripts/device-run.sh +176 -0
- package/skills/app-dev/android-foundation/scripts/device-run.test.mjs +172 -0
- package/skills/app-dev/app-debug/LICENSE +3 -0
- package/skills/app-dev/app-debug/SKILL.md +110 -0
- package/skills/app-dev/app-debug/references/common-errors.md +128 -0
- package/skills/app-dev/app-debug/release-check.mjs +93 -0
- package/skills/app-dev/better-auth/LICENSE +3 -0
- package/skills/app-dev/better-auth/SKILL.md +598 -0
- package/skills/app-dev/better-auth/references/feishu-api.md +270 -0
- package/skills/app-dev/better-auth/release-check.mjs +93 -0
- package/skills/app-dev/data-catalog/LICENSE +3 -0
- package/skills/app-dev/data-catalog/SKILL.md +204 -0
- package/skills/app-dev/data-catalog/agents/openai.yaml +3 -0
- package/skills/app-dev/data-catalog/references/data-catalog-conventions.md +75 -0
- package/skills/app-dev/data-catalog/references/field-definitions.md +116 -0
- package/skills/app-dev/data-catalog/references/format.md +121 -0
- package/skills/app-dev/data-catalog/release-check.mjs +93 -0
- package/skills/app-dev/data-catalog/scripts/data-catalog.test.mjs +36 -0
- package/skills/app-dev/data-catalog/scripts/data_catalog.py +1392 -0
- package/skills/app-dev/data-catalog/scripts/persist-conventions-core.mjs +117 -0
- package/skills/app-dev/data-catalog/scripts/persist-conventions.mjs +17 -0
- package/skills/app-dev/data-catalog/scripts/test_data_catalog.py +571 -0
- package/skills/app-dev/ios-foundation/LICENSE +3 -0
- package/skills/app-dev/ios-foundation/SKILL.md +184 -0
- package/skills/app-dev/ios-foundation/references/setup.md +243 -0
- package/skills/app-dev/ios-foundation/release-check.mjs +93 -0
- package/skills/app-dev/monorepo/LICENSE +3 -0
- package/skills/app-dev/monorepo/SKILL.md +462 -0
- package/skills/app-dev/monorepo/release-check.mjs +93 -0
- package/skills/app-dev/nextjs-fullstack/LICENSE +3 -0
- package/skills/app-dev/nextjs-fullstack/SKILL.md +211 -0
- package/skills/app-dev/nextjs-fullstack/conventions.md +318 -0
- package/skills/app-dev/nextjs-fullstack/frontend-conventions.md +61 -0
- package/skills/app-dev/nextjs-fullstack/nextjs16.md +287 -0
- package/skills/app-dev/nextjs-fullstack/release-check.mjs +93 -0
- package/skills/app-dev/nextjs-fullstack/server-actions.md +409 -0
- package/skills/app-dev/prisma-setup/LICENSE +3 -0
- package/skills/app-dev/prisma-setup/SKILL.md +181 -0
- package/skills/app-dev/prisma-setup/nextjs.md +258 -0
- package/skills/app-dev/prisma-setup/release-check.mjs +93 -0
- package/skills/app-dev/prisma-setup/turborepo.md +301 -0
- package/skills/app-dev/pyspark/LICENSE +3 -0
- package/skills/app-dev/pyspark/SKILL.md +127 -0
- package/skills/app-dev/pyspark/assets/templates/etl.py +748 -0
- package/skills/app-dev/pyspark/assets/templates/utils/__init__.py +1 -0
- package/skills/app-dev/pyspark/assets/templates/utils/hudi_metadata.py +14 -0
- package/skills/app-dev/pyspark/references/diagnosis-and-profiling.md +169 -0
- package/skills/app-dev/pyspark/references/etl-contract.md +129 -0
- package/skills/app-dev/pyspark/references/production-validation.md +131 -0
- package/skills/app-dev/pyspark/references/reconciliation.md +38 -0
- package/skills/app-dev/pyspark/references/transformation-design.md +190 -0
- package/skills/app-dev/pyspark/references/velocity-feature-calculation.md +193 -0
- package/skills/app-dev/pyspark/release-check.mjs +93 -0
- package/skills/app-dev/pyspark/scripts/spark_eventlog_summary.py +223 -0
- package/skills/app-dev/python-backend/LICENSE +3 -0
- package/skills/app-dev/python-backend/SKILL.md +325 -0
- package/skills/app-dev/python-backend/assets/uv-install.sh +48 -0
- package/skills/app-dev/python-backend/references/uv-installs.md +83 -0
- package/skills/app-dev/python-backend/release-check.mjs +93 -0
- package/skills/app-dev/shadcn/LICENSE +3 -0
- package/skills/app-dev/shadcn/SKILL.md +120 -0
- package/skills/app-dev/shadcn/assets/shadcn-small.png +0 -0
- package/skills/app-dev/shadcn/assets/shadcn.png +0 -0
- package/skills/app-dev/shadcn/cli.md +411 -0
- package/skills/app-dev/shadcn/customization.md +224 -0
- package/skills/app-dev/shadcn/mcp.md +101 -0
- package/skills/app-dev/shadcn/release-check.mjs +93 -0
- package/skills/app-dev/shadcn/rules/base-vs-radix.md +323 -0
- package/skills/app-dev/shadcn/rules/component-selection.md +67 -0
- package/skills/app-dev/shadcn/rules/composition.md +195 -0
- package/skills/app-dev/shadcn/rules/data-table.md +201 -0
- package/skills/app-dev/shadcn/rules/forms.md +255 -0
- package/skills/app-dev/shadcn/rules/icons.md +103 -0
- package/skills/app-dev/shadcn/rules/styling.md +167 -0
- package/skills/app-dev/zod-v4/LICENSE +3 -0
- package/skills/app-dev/zod-v4/SKILL.md +286 -0
- package/skills/app-dev/zod-v4/release-check.mjs +93 -0
- package/skills/cicd/cloud-build/LICENSE +3 -0
- package/skills/cicd/cloud-build/SKILL.md +707 -0
- package/skills/cicd/cloud-build/release-check.mjs +93 -0
- package/skills/cicd/cloud-debug/LICENSE +3 -0
- package/skills/cicd/cloud-debug/SKILL.md +315 -0
- package/skills/cicd/cloud-debug/references/build-failures.md +238 -0
- package/skills/cicd/cloud-debug/references/deploy-failures.md +376 -0
- package/skills/cicd/cloud-debug/references/pipeline-failures.md +378 -0
- package/skills/cicd/cloud-debug/release-check.mjs +93 -0
- package/skills/cicd/cloud-deploy/LICENSE +3 -0
- package/skills/cicd/cloud-deploy/SKILL.md +230 -0
- package/skills/cicd/cloud-deploy/references/config-templates.md +257 -0
- package/skills/cicd/cloud-deploy/release-check.mjs +93 -0
- package/skills/cicd/docker/LICENSE +3 -0
- package/skills/cicd/docker/SKILL.md +151 -0
- package/skills/cicd/docker/release-check.mjs +93 -0
- package/skills/cicd/github-actions/LICENSE +3 -0
- package/skills/cicd/github-actions/SKILL.md +111 -0
- package/skills/cicd/github-actions/references/audit-profile-optimize.md +150 -0
- package/skills/cicd/github-actions/references/ci-case-studies.md +141 -0
- package/skills/cicd/github-actions/references/conditional-ci.md +214 -0
- package/skills/cicd/github-actions/references/debugging.md +13 -0
- package/skills/cicd/github-actions/references/pipeline.md +87 -0
- package/skills/cicd/github-actions/references/post-deploy-and-publishing.md +33 -0
- package/skills/cicd/github-actions/references/runners.md +253 -0
- package/skills/cicd/github-actions/references/security.md +29 -0
- package/skills/cicd/github-actions/references/speed-and-audit.md +115 -0
- package/skills/cicd/github-actions/references/two-pass-reading.md +92 -0
- package/skills/cicd/github-actions/release-check.mjs +93 -0
- package/skills/cicd/github-actions/scripts/ci-run-audit-core.mjs +612 -0
- package/skills/cicd/github-actions/scripts/ci-run-audit.mjs +4 -0
- package/skills/cicd/github-actions/scripts/ci-run-audit.test.mjs +637 -0
- package/skills/cicd/github-flow/LICENSE +3 -0
- package/skills/cicd/github-flow/SKILL.md +123 -0
- package/skills/cicd/github-flow/references/ci-conventions.md +14 -0
- package/skills/cicd/github-flow/release-check.mjs +93 -0
- package/skills/cicd/github-flow/scripts/flow-core.mjs +105 -0
- package/skills/cicd/github-flow/scripts/flow.mjs +81 -0
- package/skills/cicd/github-flow/scripts/flow.test.mjs +114 -0
- package/skills/cicd/github-flow/scripts/persist-conventions-core.mjs +117 -0
- package/skills/cicd/github-flow/scripts/persist-conventions.mjs +49 -0
- package/skills/cicd/github-flow/scripts/persist-conventions.test.mjs +108 -0
- package/skills/cicd/google-cloud/LICENSE +3 -0
- package/skills/cicd/google-cloud/SKILL.md +119 -0
- package/skills/cicd/google-cloud/references/gcs.md +469 -0
- package/skills/cicd/google-cloud/references/iam.md +451 -0
- package/skills/cicd/google-cloud/references/project.md +349 -0
- package/skills/cicd/google-cloud/references/secrets.md +336 -0
- package/skills/cicd/google-cloud/references/vpc.md +312 -0
- package/skills/cicd/google-cloud/release-check.mjs +93 -0
- package/skills/cicd/google-cloud/scripts/create-sa.sh +36 -0
- package/skills/cicd/google-cloud/scripts/gcp-config.sh +31 -0
- package/skills/cicd/google-cloud/scripts/grant-iap.sh +41 -0
- package/skills/cicd/google-cloud/scripts/setup-secrets.sh +48 -0
- package/skills/cicd/mergify/LICENSE +3 -0
- package/skills/cicd/mergify/SKILL.md +102 -0
- package/skills/cicd/mergify/assets/templates/mergify.yml +215 -0
- package/skills/cicd/mergify/assets/templates/ruleset.json +46 -0
- package/skills/cicd/mergify/references/branch-protection.md +282 -0
- package/skills/cicd/mergify/references/configuration.md +274 -0
- package/skills/cicd/mergify/references/diagnosis.md +109 -0
- package/skills/cicd/mergify/references/stacks.md +84 -0
- package/skills/cicd/mergify/references/traps.md +119 -0
- package/skills/cicd/mergify/references/watch-contract.md +274 -0
- package/skills/cicd/mergify/release-check.mjs +93 -0
- package/skills/cicd/mergify/scripts/audit-core.mjs +208 -0
- package/skills/cicd/mergify/scripts/audit.mjs +4 -0
- package/skills/cicd/mergify/scripts/audit.test.mjs +201 -0
- package/skills/cicd/mergify/scripts/entrypoints.test.mjs +106 -0
- package/skills/cicd/mergify/scripts/pr-620-mergify-comments.fixture.json +30 -0
- package/skills/cicd/mergify/scripts/stack-push-core.mjs +258 -0
- package/skills/cicd/mergify/scripts/stack-push.mjs +4 -0
- package/skills/cicd/mergify/scripts/stack-push.test.mjs +233 -0
- package/skills/cicd/mergify/scripts/watch-pr-delivery-core.mjs +1065 -0
- package/skills/cicd/mergify/scripts/watch-pr-delivery.mjs +4 -0
- package/skills/cicd/mergify/scripts/watch-pr-delivery.test.mjs +1826 -0
- package/skills/cicd/podman/LICENSE +3 -0
- package/skills/cicd/podman/SKILL.md +71 -0
- package/skills/cicd/podman/agents/openai.yaml +4 -0
- package/skills/cicd/podman/assets/templates/podman-compose-socket-directory.yml +6 -0
- package/skills/cicd/podman/assets/templates/podman-service-override.conf +3 -0
- package/skills/cicd/podman/references/compose-compatibility.md +70 -0
- package/skills/cicd/podman/references/networking-and-ports.md +74 -0
- package/skills/cicd/podman/references/rootless-services-and-sockets.md +156 -0
- package/skills/cicd/podman/references/troubleshooting.md +98 -0
- package/skills/cicd/podman/release-check.mjs +93 -0
- package/skills/content/article-review/LICENSE +3 -0
- package/skills/content/article-review/SKILL.md +66 -0
- package/skills/content/article-review/references/report-format.md +68 -0
- package/skills/content/article-review/references/template-guide.md +79 -0
- package/skills/content/article-review/references/template-wechat-payments.md +125 -0
- package/skills/content/article-review/release-check.mjs +93 -0
- package/skills/content/article-to-image/LICENSE +3 -0
- package/skills/content/article-to-image/SKILL.md +91 -0
- package/skills/content/article-to-image/references/formats.md +53 -0
- package/skills/content/article-to-image/release-check.mjs +93 -0
- package/skills/content/flowchart-svg/LICENSE +3 -0
- package/skills/content/flowchart-svg/SKILL.md +43 -0
- package/skills/content/flowchart-svg/release-check.mjs +93 -0
- package/skills/content/flowchart-svg/scripts/export_png.sh +8 -0
- package/skills/content/image-gen/LICENSE +3 -0
- package/skills/content/image-gen/SKILL.md +192 -0
- package/skills/content/image-gen/release-check.mjs +93 -0
- package/skills/content/image-gen/scripts/credentials.mjs +217 -0
- package/skills/content/image-gen/scripts/credentials.test.mjs +212 -0
- package/skills/content/image-gen/scripts/generate.mjs +240 -0
- package/skills/content/image-gen/scripts/options.mjs +293 -0
- package/skills/content/image-gen/scripts/options.test.mjs +245 -0
- package/skills/content/mermaid/LICENSE +3 -0
- package/skills/content/mermaid/SKILL.md +635 -0
- package/skills/content/mermaid/release-check.mjs +93 -0
- package/skills/content/wechat-article-writer/LICENSE +3 -0
- package/skills/content/wechat-article-writer/SKILL.md +92 -0
- package/skills/content/wechat-article-writer/references/style-selector.md +133 -0
- package/skills/content/wechat-article-writer/references/writing-rules.md +152 -0
- package/skills/content/wechat-article-writer/release-check.mjs +93 -0
- package/skills/content/wechat-article-writer/templates/wechat-comparison.md +52 -0
- package/skills/content/wechat-article-writer/templates/wechat-howto.md +51 -0
- package/skills/content/wechat-article-writer/templates/wechat-insight.md +50 -0
- package/skills/content/wechat-article-writer/templates/wechat-pitfall.md +52 -0
- package/skills/content/wechat-publisher/LICENSE +3 -0
- package/skills/content/wechat-publisher/SKILL.md +171 -0
- package/skills/content/wechat-publisher/references/api.md +105 -0
- package/skills/content/wechat-publisher/references/layout-rules.md +57 -0
- package/skills/content/wechat-publisher/references/wechat-safe-template.html +86 -0
- package/skills/content/wechat-publisher/release-check.mjs +93 -0
- package/skills/content/wechat-publisher/scripts/md_to_wechat_html.py +194 -0
- package/skills/content/wechat-publisher/scripts/publish.py +239 -0
- package/skills/content/wechat-search/LICENSE +3 -0
- package/skills/content/wechat-search/SKILL.md +211 -0
- package/skills/content/wechat-search/release-check.mjs +93 -0
- package/skills/content/wechat-search/scripts/start-chrome.sh +83 -0
- package/skills/content/xai-search/LICENSE +3 -0
- package/skills/content/xai-search/SKILL.md +79 -0
- package/skills/content/xai-search/release-check.mjs +93 -0
- package/skills/content/xai-search/scripts/xai_search.sh +187 -0
- package/skills/content/xai-search/tests/smoke_test.sh +36 -0
- package/skills/core/distill-writing/LICENSE +3 -0
- package/skills/core/distill-writing/SKILL.md +74 -0
- package/skills/core/distill-writing/references/extraction.md +95 -0
- package/skills/core/distill-writing/references/updating.md +46 -0
- package/skills/core/distill-writing/release-check.mjs +93 -0
- package/skills/core/doc-to-md/LICENSE +3 -0
- package/skills/core/doc-to-md/SKILL.md +78 -0
- package/skills/core/doc-to-md/release-check.mjs +93 -0
- package/skills/core/doc-to-md/scripts/convert.py +157 -0
- package/skills/core/editorial-review/LICENSE +3 -0
- package/skills/core/editorial-review/SKILL.md +79 -0
- package/skills/core/editorial-review/references/checklist.md +94 -0
- package/skills/core/editorial-review/release-check.mjs +93 -0
- package/skills/core/house-style/LICENSE +3 -0
- package/skills/core/house-style/SKILL.md +33 -0
- package/skills/core/house-style/references/defaults.md +66 -0
- package/skills/core/house-style/references/rules.md +267 -0
- package/skills/core/house-style/references/sources.md +27 -0
- package/skills/core/house-style/references/style-en.md +131 -0
- package/skills/core/house-style/references/style-zh.md +141 -0
- package/skills/core/house-style/references/terminology-zh.md +223 -0
- package/skills/core/house-style/release-check.mjs +93 -0
- package/skills/core/house-style/templates/brd.md +103 -0
- package/skills/core/house-style/templates/commercial-proposal.md +93 -0
- package/skills/core/house-style/templates/compliance-policy.md +109 -0
- package/skills/core/house-style/templates/gap-analysis.md +105 -0
- package/skills/core/house-style/templates/general.md +63 -0
- package/skills/core/house-style/templates/integration-guide.md +98 -0
- package/skills/core/house-style/templates/prd.md +81 -0
- package/skills/core/house-style/templates/risk-operations-procedure.md +94 -0
- package/skills/core/house-style/templates/technical-spec.md +96 -0
- package/skills/core/polish-cn/LICENSE +3 -0
- package/skills/core/polish-cn/SKILL.md +102 -0
- package/skills/core/polish-cn/references/brief.md +93 -0
- package/skills/core/polish-cn/references/models.md +76 -0
- package/skills/core/polish-cn/release-check.mjs +93 -0
- package/skills/core/polish-cn/scripts/polish-cn.test.mjs +244 -0
- package/skills/core/polish-cn/scripts/polish_blocks.py +489 -0
- package/skills/core/reg-parser/LICENSE +3 -0
- package/skills/core/reg-parser/SKILL.md +212 -0
- package/skills/core/reg-parser/references/checklist-schema.md +223 -0
- package/skills/core/reg-parser/references/checklists/hk-amlo-checklist.json +1306 -0
- package/skills/core/reg-parser/references/checklists/hk-amlo-checklist.md +469 -0
- package/skills/core/reg-parser/references/checklists/hk-fi-corporate-onboarding-cdd-checklist.json +634 -0
- package/skills/core/reg-parser/references/checklists/hk-fi-corporate-onboarding-cdd-checklist.md +233 -0
- package/skills/core/reg-parser/references/checklists/hk-mso-guideline-checklist.json +2194 -0
- package/skills/core/reg-parser/references/checklists/hk-mso-guideline-checklist.md +771 -0
- package/skills/core/reg-parser/references/checklists/kyc-aml-checklist.md +258 -0
- package/skills/core/reg-parser/references/checklists/visa-vars-checklist.md +1058 -0
- package/skills/core/reg-parser/references/mandates/hk-amlo-cap615.md +389 -0
- package/skills/core/reg-parser/references/mandates/visa-vars.md +205 -0
- package/skills/core/reg-parser/release-check.mjs +93 -0
- package/skills/core/reg-parser/scripts/compare_checklist.py +230 -0
- package/skills/core/reg-parser/scripts/dump_checklist.py +361 -0
- package/skills/core/reg-parser/scripts/preprocess-mandate.sh +187 -0
- package/skills/core/reg-parser/scripts/reg-parser.test.mjs +165 -0
- package/skills/core/reg-parser/scripts/test_quarantine.sh +105 -0
- package/skills/core/reg-parser/scripts/validate_checklist.py +563 -0
- package/skills/core/reg-parser/scripts/validate_mandate.py +273 -0
- package/skills/core/terminology/LICENSE +3 -0
- package/skills/core/terminology/SKILL.md +62 -0
- package/skills/core/terminology/references/format.md +50 -0
- package/skills/core/terminology/references/init.md +32 -0
- package/skills/core/terminology/release-check.mjs +93 -0
- package/skills/core/write-doc/LICENSE +3 -0
- package/skills/core/write-doc/SKILL.md +141 -0
- package/skills/core/write-doc/references/bilingual.md +60 -0
- package/skills/core/write-doc/release-check.mjs +93 -0
- package/skills/engineering/better-review/LICENSE +3 -0
- package/skills/engineering/better-review/SKILL.md +249 -0
- package/skills/engineering/better-review/agents/openai.yaml +9 -0
- package/skills/engineering/better-review/references/domain-hazards.md +53 -0
- package/skills/engineering/better-review/references/passes.md +147 -0
- package/skills/engineering/better-review/references/result.md +151 -0
- package/skills/engineering/better-review/release-check.mjs +93 -0
- package/skills/engineering/better-review/scripts/pr-context.mjs +212 -0
- package/skills/engineering/better-review/scripts/pr-context.test.mjs +168 -0
- package/skills/engineering/better-review/scripts/rereview-scope.mjs +503 -0
- package/skills/engineering/better-review/scripts/rereview-scope.test.mjs +587 -0
- package/skills/engineering/better-review/scripts/scan-diff.mjs +365 -0
- package/skills/engineering/better-review/scripts/scan-diff.test.mjs +414 -0
- package/skills/engineering/better-review/scripts/vendor-sync.test.mjs +96 -0
- package/skills/engineering/e2e-test/LICENSE +3 -0
- package/skills/engineering/e2e-test/SKILL.md +98 -0
- package/skills/engineering/e2e-test/assets/ci-gates.cloudbuild.yaml +24 -0
- package/skills/engineering/e2e-test/references/authoring/auth-flows.md +159 -0
- package/skills/engineering/e2e-test/references/authoring/playwright-config.md +73 -0
- package/skills/engineering/e2e-test/references/authoring/playwright-patterns.md +144 -0
- package/skills/engineering/e2e-test/references/ci-integration.md +25 -0
- package/skills/engineering/e2e-test/references/ci-playwright-container.md +290 -0
- package/skills/engineering/e2e-test/references/presentation-sweep.md +104 -0
- package/skills/engineering/e2e-test/references/reviewing.md +41 -0
- package/skills/engineering/e2e-test/references/route-manifest.md +51 -0
- package/skills/engineering/e2e-test/release-check.mjs +93 -0
- package/skills/engineering/e2e-test/scripts/detect-routes-nextjs.mjs +200 -0
- package/skills/engineering/e2e-test/scripts/presentation-checks.mjs +171 -0
- package/skills/engineering/e2e-test/scripts/presentation-checks.test.mjs +106 -0
- package/skills/engineering/e2e-test/scripts/presentation-perceivability.mjs +179 -0
- package/skills/engineering/e2e-test/scripts/presentation-perceivability.test.mjs +117 -0
- package/skills/engineering/e2e-test/scripts/presentation-reachability.mjs +154 -0
- package/skills/engineering/e2e-test/scripts/presentation-reachability.test.mjs +69 -0
- package/skills/engineering/e2e-test/scripts/presentation-render-health.mjs +141 -0
- package/skills/engineering/e2e-test/scripts/presentation-render-health.test.mjs +98 -0
- package/skills/engineering/e2e-test/scripts/presentation-sweep.mjs +147 -0
- package/skills/engineering/e2e-test/scripts/presentation-sweep.test.mjs +71 -0
- package/skills/engineering/e2e-test/scripts/presentation-temporal.mjs +127 -0
- package/skills/engineering/e2e-test/scripts/presentation-temporal.test.mjs +59 -0
- package/skills/engineering/e2e-test/scripts/project-route-manifest.mjs +82 -0
- package/skills/engineering/e2e-test/scripts/project-route-manifest.test.mjs +76 -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 +11 -0
- package/skills/engineering/engineering-runtime/agents/openai.yaml +6 -0
- package/skills/engineering/engineering-runtime/coherence/workflow.json +597 -0
- package/skills/engineering/engineering-runtime/engineering-runtime.test.mjs +68 -0
- package/skills/engineering/engineering-runtime/release-check.mjs +93 -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/invocation-policy.mjs +187 -0
- package/skills/engineering/engineering-runtime/scripts/invocation-policy.test.mjs +95 -0
- package/skills/engineering/engineering-runtime/scripts/main-module.mjs +79 -0
- package/skills/engineering/engineering-runtime/scripts/main-module.test.mjs +288 -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/required-check-plan.test.mjs +96 -0
- package/skills/engineering/engineering-runtime/scripts/review-completion.mjs +56 -0
- package/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +595 -0
- package/skills/engineering/engineering-runtime/scripts/workflow-policy.mjs +189 -0
- package/skills/engineering/engineering-runtime/scripts/yaml-mapping.mjs +18 -0
- package/skills/engineering/ensure-coverage/LICENSE +3 -0
- package/skills/engineering/ensure-coverage/SKILL.md +129 -0
- package/skills/engineering/ensure-coverage/references/breadth/coverage-ledger.md +92 -0
- package/skills/engineering/ensure-coverage/references/breadth/inventory-contract.md +85 -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 +19 -0
- package/skills/engineering/ensure-coverage/references/depth/grading.md +35 -0
- package/skills/engineering/ensure-coverage/references/depth/mock-policy.md +57 -0
- package/skills/engineering/ensure-coverage/references/depth/test-smells.md +39 -0
- package/skills/engineering/ensure-coverage/references/enforcement/ci-contract.md +44 -0
- package/skills/engineering/ensure-coverage/references/examples/coverage-ledger.md +110 -0
- package/skills/engineering/ensure-coverage/references/examples/refund-flow.md +35 -0
- package/skills/engineering/ensure-coverage/references/presentation/axis.md +64 -0
- package/skills/engineering/ensure-coverage/references/process/audit-mode.md +27 -0
- package/skills/engineering/ensure-coverage/references/process/output-template.md +139 -0
- package/skills/engineering/ensure-coverage/references/process/test-strategy.md +56 -0
- package/skills/engineering/ensure-coverage/release-check.mjs +93 -0
- package/skills/engineering/ensure-coverage/schemas/coverage-config.schema.json +67 -0
- package/skills/engineering/ensure-coverage/schemas/coverage-file.schema.json +140 -0
- package/skills/engineering/ensure-coverage/scripts/README.md +99 -0
- package/skills/engineering/ensure-coverage/scripts/adapters/nextjs-inventory.mjs +182 -0
- package/skills/engineering/ensure-coverage/scripts/adapters/nextjs-inventory.test.mjs +96 -0
- package/skills/engineering/ensure-coverage/scripts/check-quarantine-expiry.mjs +105 -0
- package/skills/engineering/ensure-coverage/scripts/check-quarantine-expiry.test.mjs +47 -0
- package/skills/engineering/ensure-coverage/scripts/ci-audit.mjs +137 -0
- package/skills/engineering/ensure-coverage/scripts/ci-audit.test.mjs +86 -0
- package/skills/engineering/ensure-coverage/scripts/coverage-ledger.mjs +674 -0
- package/skills/engineering/ensure-coverage/scripts/coverage-ledger.test.mjs +447 -0
- package/skills/engineering/ensure-coverage/scripts/design-parity.mjs +591 -0
- package/skills/engineering/ensure-coverage/scripts/design-parity.test.mjs +552 -0
- package/skills/engineering/ensure-coverage/scripts/lint-tests.mjs +282 -0
- package/skills/engineering/ensure-coverage/scripts/lint-tests.test.mjs +289 -0
- package/skills/engineering/ensure-coverage/scripts/mock-policy-config.mjs +176 -0
- package/skills/engineering/ensure-coverage/scripts/mock-policy-config.test.mjs +119 -0
- package/skills/engineering/ensure-coverage/scripts/package.json +19 -0
- package/skills/engineering/golden-path/LICENSE +3 -0
- package/skills/engineering/golden-path/SKILL.md +230 -0
- package/skills/engineering/golden-path/agents/openai.yaml +3 -0
- package/skills/engineering/golden-path/assets/golden-paths.example.json +57 -0
- package/skills/engineering/golden-path/references/contract.md +239 -0
- package/skills/engineering/golden-path/references/migration.md +111 -0
- package/skills/engineering/golden-path/release-check.mjs +93 -0
- package/skills/engineering/golden-path/scripts/golden-path-core.mjs +1779 -0
- package/skills/engineering/golden-path/scripts/golden-path.mjs +6 -0
- package/skills/engineering/golden-path/scripts/golden-path.test.mjs +1457 -0
- package/skills/engineering/harness-runtime/LICENSE +3 -0
- package/skills/engineering/harness-runtime/SKILL.md +19 -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/discover-models.test.mjs +363 -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/harness-runtime.test.mjs +967 -0
- package/skills/engineering/harness-runtime/index.mjs +767 -0
- package/skills/engineering/harness-runtime/model-catalog-seed.test.mjs +829 -0
- package/skills/engineering/harness-runtime/model-catalog.mjs +787 -0
- package/skills/engineering/harness-runtime/model-catalog.test.mjs +366 -0
- package/skills/engineering/harness-runtime/native-question-contracts.md +37 -0
- package/skills/engineering/harness-runtime/references/model-catalog-seed.json +408 -0
- package/skills/engineering/harness-runtime/references/model-catalog.md +57 -0
- package/skills/engineering/harness-runtime/release-check.mjs +93 -0
- package/skills/engineering/resolve-issues/LICENSE +3 -0
- package/skills/engineering/resolve-issues/SKILL.md +250 -0
- package/skills/engineering/resolve-issues/agents/openai.yaml +9 -0
- package/skills/engineering/resolve-issues/plugin-hooks.json +17 -0
- package/skills/engineering/resolve-issues/references/canary.md +28 -0
- package/skills/engineering/resolve-issues/references/ci.md +105 -0
- package/skills/engineering/resolve-issues/references/done.md +25 -0
- package/skills/engineering/resolve-issues/references/dropbox.md +90 -0
- package/skills/engineering/resolve-issues/references/expire.md +318 -0
- package/skills/engineering/resolve-issues/references/exploration.md +202 -0
- package/skills/engineering/resolve-issues/references/implementation.md +303 -0
- package/skills/engineering/resolve-issues/references/preflight.md +445 -0
- package/skills/engineering/resolve-issues/references/review.md +228 -0
- package/skills/engineering/resolve-issues/references/shared-modules.md +44 -0
- package/skills/engineering/resolve-issues/references/task-plan.md +264 -0
- package/skills/engineering/resolve-issues/release-check.mjs +93 -0
- package/skills/engineering/resolve-issues/scripts/cleanup-runs.mjs +364 -0
- package/skills/engineering/resolve-issues/scripts/cleanup-runs.test.mjs +280 -0
- package/skills/engineering/resolve-issues/scripts/discover-models.mjs +9 -0
- package/skills/engineering/resolve-issues/scripts/dropbox.mjs +473 -0
- package/skills/engineering/resolve-issues/scripts/dropbox.test.mjs +346 -0
- package/skills/engineering/resolve-issues/scripts/findings-reuse.mjs +98 -0
- package/skills/engineering/resolve-issues/scripts/findings-reuse.test.mjs +88 -0
- package/skills/engineering/resolve-issues/scripts/github-reviews.mjs +384 -0
- package/skills/engineering/resolve-issues/scripts/github-reviews.test.mjs +264 -0
- package/skills/engineering/resolve-issues/scripts/install-hooks.mjs +135 -0
- package/skills/engineering/resolve-issues/scripts/install-hooks.test.mjs +79 -0
- package/skills/engineering/resolve-issues/scripts/issue-tree.mjs +162 -0
- package/skills/engineering/resolve-issues/scripts/issue-tree.test.mjs +107 -0
- package/skills/engineering/resolve-issues/scripts/manual-invocation.test.mjs +14 -0
- package/skills/engineering/resolve-issues/scripts/model-catalog.mjs +9 -0
- package/skills/engineering/resolve-issues/scripts/next-actions-capacity.test.mjs +149 -0
- package/skills/engineering/resolve-issues/scripts/next-actions.mjs +856 -0
- package/skills/engineering/resolve-issues/scripts/next-actions.test.mjs +915 -0
- package/skills/engineering/resolve-issues/scripts/plan-sync.mjs +269 -0
- package/skills/engineering/resolve-issues/scripts/plan-sync.test.mjs +156 -0
- package/skills/engineering/resolve-issues/scripts/preflight-models.mjs +736 -0
- package/skills/engineering/resolve-issues/scripts/preflight-models.test.mjs +448 -0
- package/skills/engineering/resolve-issues/scripts/ready-check.mjs +185 -0
- package/skills/engineering/resolve-issues/scripts/ready-check.test.mjs +217 -0
- package/skills/engineering/resolve-issues/scripts/rehydrate.mjs +98 -0
- package/skills/engineering/resolve-issues/scripts/rehydrate.test.mjs +91 -0
- package/skills/engineering/resolve-issues/scripts/review-verdict.mjs +509 -0
- package/skills/engineering/resolve-issues/scripts/review-verdict.test.mjs +1060 -0
- package/skills/engineering/resolve-issues/scripts/size-check.mjs +492 -0
- package/skills/engineering/resolve-issues/scripts/size-check.test.mjs +458 -0
- package/skills/engineering/resolve-release/LICENSE +3 -0
- package/skills/engineering/resolve-release/SKILL.md +215 -0
- package/skills/engineering/resolve-release/agents/openai.yaml +8 -0
- package/skills/engineering/resolve-release/flows/cloud-run-canary.md +13 -0
- package/skills/engineering/resolve-release/flows/cloud-run.md +7 -0
- package/skills/engineering/resolve-release/flows/root-tag-npm.md +13 -0
- package/skills/engineering/resolve-release/flows/workspace-npm.md +22 -0
- package/skills/engineering/resolve-release/references/canary.md +51 -0
- package/skills/engineering/resolve-release/references/candidate.md +41 -0
- package/skills/engineering/resolve-release/references/cut.md +131 -0
- package/skills/engineering/resolve-release/references/dark-launch.md +66 -0
- package/skills/engineering/resolve-release/references/flows.md +150 -0
- package/skills/engineering/resolve-release/references/preflight.md +154 -0
- package/skills/engineering/resolve-release/references/prepared-metadata.md +260 -0
- package/skills/engineering/resolve-release/references/promotion.md +82 -0
- package/skills/engineering/resolve-release/references/publish.md +232 -0
- package/skills/engineering/resolve-release/references/workspace-npm.md +103 -0
- package/skills/engineering/resolve-release/release-check.mjs +93 -0
- package/skills/engineering/resolve-release/scripts/apply.mjs +212 -0
- package/skills/engineering/resolve-release/scripts/apply.test.mjs +99 -0
- package/skills/engineering/resolve-release/scripts/dark-flags.mjs +134 -0
- package/skills/engineering/resolve-release/scripts/dark-flags.test.mjs +100 -0
- package/skills/engineering/resolve-release/scripts/flow.mjs +500 -0
- package/skills/engineering/resolve-release/scripts/flow.test.mjs +340 -0
- package/skills/engineering/resolve-release/scripts/green-head.mjs +231 -0
- package/skills/engineering/resolve-release/scripts/green-head.test.mjs +101 -0
- package/skills/engineering/resolve-release/scripts/npm-version-absence.mjs +138 -0
- package/skills/engineering/resolve-release/scripts/npm-version-absence.test.mjs +426 -0
- package/skills/engineering/resolve-release/scripts/plan-npm.test.mjs +659 -0
- package/skills/engineering/resolve-release/scripts/plan-toml-boundary.test.mjs +317 -0
- package/skills/engineering/resolve-release/scripts/plan-uv.test.mjs +291 -0
- package/skills/engineering/resolve-release/scripts/plan.mjs +806 -0
- package/skills/engineering/resolve-release/scripts/plan.test.mjs +428 -0
- package/skills/engineering/rush-issues/LICENSE +3 -0
- package/skills/engineering/rush-issues/SKILL.md +58 -0
- package/skills/engineering/rush-issues/agents/openai.yaml +8 -0
- package/skills/engineering/rush-issues/rehydrate.mjs +10 -0
- package/skills/engineering/rush-issues/release-check.mjs +93 -0
- package/skills/engineering/rush-release/LICENSE +3 -0
- package/skills/engineering/rush-release/SKILL.md +35 -0
- package/skills/engineering/rush-release/agents/openai.yaml +8 -0
- package/skills/engineering/rush-release/release-check.mjs +93 -0
- package/skills/engineering/test-quality/LICENSE +3 -0
- package/skills/engineering/test-quality/SKILL.md +79 -0
- package/skills/engineering/test-quality/references/audit.md +80 -0
- package/skills/engineering/test-quality/references/lint.md +24 -0
- package/skills/engineering/test-quality/references/principles.md +143 -0
- package/skills/engineering/test-quality/references/testing-conventions.md +9 -0
- package/skills/engineering/test-quality/release-check.mjs +93 -0
- package/skills/engineering/test-quality/scripts/lint-tests.mjs +574 -0
- package/skills/engineering/test-quality/scripts/lint-tests.test.mjs +591 -0
- package/skills/engineering/test-quality/scripts/mock-policy-config.mjs +176 -0
- package/skills/engineering/test-quality/scripts/mock-policy-config.test.mjs +119 -0
- package/skills/engineering/test-quality/scripts/persist-conventions-core.mjs +117 -0
- package/skills/engineering/test-quality/scripts/persist-conventions.mjs +17 -0
- package/skills/engineering/test-quality/scripts/persist-conventions.test.mjs +104 -0
- package/skills/risk-compliance/compliance-audit/LICENSE +3 -0
- package/skills/risk-compliance/compliance-audit/SKILL.md +439 -0
- package/skills/risk-compliance/compliance-audit/references/findings-schema.md +293 -0
- package/skills/risk-compliance/compliance-audit/references/normalization-rules.md +45 -0
- package/skills/risk-compliance/compliance-audit/references/vars-checklist.md +1068 -0
- package/skills/risk-compliance/compliance-audit/references/vars-findings-schema.md +398 -0
- package/skills/risk-compliance/compliance-audit/release-check.mjs +93 -0
- package/skills/risk-compliance/compliance-audit/scripts/compliance-audit.test.mjs +14 -0
- package/skills/risk-compliance/compliance-audit/scripts/export_tracker_xlsx.py +221 -0
- package/skills/risk-compliance/compliance-audit/scripts/reconcile_tracker.py +458 -0
- package/skills/risk-compliance/compliance-audit/scripts/test_export_tracker_xlsx.py +97 -0
- package/skills/risk-compliance/compliance-audit/scripts/test_validate_tracker.py +157 -0
- package/skills/risk-compliance/compliance-audit/scripts/validate_tracker.py +829 -0
- package/skills/risk-compliance/contract-compare/LICENSE +3 -0
- package/skills/risk-compliance/contract-compare/SKILL.md +199 -0
- package/skills/risk-compliance/contract-compare/references/comparison-dimensions.md +209 -0
- package/skills/risk-compliance/contract-compare/release-check.mjs +93 -0
- package/skills/risk-compliance/contract-review/LICENSE +3 -0
- package/skills/risk-compliance/contract-review/SKILL.md +240 -0
- package/skills/risk-compliance/contract-review/references/review-criteria.md +223 -0
- package/skills/risk-compliance/contract-review/release-check.mjs +93 -0
- package/skills/risk-compliance/doc-authenticity/LICENSE +3 -0
- package/skills/risk-compliance/doc-authenticity/SKILL.md +160 -0
- package/skills/risk-compliance/doc-authenticity/agents/openai.yaml +8 -0
- package/skills/risk-compliance/doc-authenticity/manual-invocation.test.mjs +12 -0
- package/skills/risk-compliance/doc-authenticity/references/agreement.md +66 -0
- package/skills/risk-compliance/doc-authenticity/references/bank-statement.md +150 -0
- package/skills/risk-compliance/doc-authenticity/references/forensic-signals.md +157 -0
- package/skills/risk-compliance/doc-authenticity/references/image-signals.md +114 -0
- package/skills/risk-compliance/doc-authenticity/references/invoice.md +66 -0
- package/skills/risk-compliance/doc-authenticity/references/issuer-profiles.md +86 -0
- package/skills/risk-compliance/doc-authenticity/references/verdict-and-report.md +95 -0
- package/skills/risk-compliance/doc-authenticity/references/verdict.schema.json +81 -0
- package/skills/risk-compliance/doc-authenticity/release-check.mjs +93 -0
- package/skills/risk-compliance/doc-authenticity/scripts/image_forensics.py +294 -0
- package/skills/risk-compliance/doc-authenticity/scripts/pdf_forensics.py +401 -0
- package/skills/risk-compliance/doc-authenticity/scripts/validate_codes.py +224 -0
- package/skills/risk-compliance/doc-authenticity/scripts/validate_verdict.py +308 -0
- package/skills/risk-compliance/doc-authenticity/scripts/validate_verdict.test.mjs +212 -0
- package/skills/risk-compliance/fraud-analysis/LICENSE +3 -0
- package/skills/risk-compliance/fraud-analysis/SKILL.md +114 -0
- package/skills/risk-compliance/fraud-analysis/references/archetypes/authorized-payment-scam.md +41 -0
- package/skills/risk-compliance/fraud-analysis/references/archetypes/first-party-fraud.md +44 -0
- package/skills/risk-compliance/fraud-analysis/references/archetypes/third-party-fraud.md +27 -0
- package/skills/risk-compliance/fraud-analysis/references/contexts/bank-transfer.md +24 -0
- package/skills/risk-compliance/fraud-analysis/references/contexts/card-payment.md +30 -0
- package/skills/risk-compliance/fraud-analysis/references/contexts/payment-collection.md +20 -0
- package/skills/risk-compliance/fraud-analysis/references/contexts/payout.md +20 -0
- package/skills/risk-compliance/fraud-analysis/references/feature-engineering.md +158 -0
- package/skills/risk-compliance/fraud-analysis/references/mechanisms/account-takeover.md +36 -0
- package/skills/risk-compliance/fraud-analysis/references/report-rationale.md +45 -0
- package/skills/risk-compliance/fraud-analysis/references/report-template.md +190 -0
- package/skills/risk-compliance/fraud-analysis/references/review-checklist.md +175 -0
- package/skills/risk-compliance/fraud-analysis/references/taxonomy.md +79 -0
- package/skills/risk-compliance/fraud-analysis/references/terminology.md +108 -0
- package/skills/risk-compliance/fraud-analysis/references/workflow.md +175 -0
- package/skills/risk-compliance/fraud-analysis/release-check.mjs +93 -0
- package/skills/risk-compliance/payment-analysis/LICENSE +3 -0
- package/skills/risk-compliance/payment-analysis/SKILL.md +180 -0
- package/skills/risk-compliance/payment-analysis/references/auth-rate-actions.md +30 -0
- package/skills/risk-compliance/payment-analysis/references/chargebacks.md +208 -0
- package/skills/risk-compliance/payment-analysis/references/chinese-polish.md +51 -0
- package/skills/risk-compliance/payment-analysis/references/event-layers.md +127 -0
- package/skills/risk-compliance/payment-analysis/references/fx.md +59 -0
- package/skills/risk-compliance/payment-analysis/references/journey.md +79 -0
- package/skills/risk-compliance/payment-analysis/references/metrics.md +67 -0
- package/skills/risk-compliance/payment-analysis/references/report-template.md +119 -0
- package/skills/risk-compliance/payment-analysis/references/terminology.md +95 -0
- package/skills/risk-compliance/payment-analysis/references/visualization.md +56 -0
- package/skills/risk-compliance/payment-analysis/release-check.mjs +93 -0
- package/skills/risk-compliance/report-writing/LICENSE +3 -0
- package/skills/risk-compliance/report-writing/SKILL.md +220 -0
- package/skills/risk-compliance/report-writing/references/audit-report-template.md +121 -0
- package/skills/risk-compliance/report-writing/references/external-assessment-template.md +167 -0
- package/skills/risk-compliance/report-writing/references/management-report-template.md +145 -0
- package/skills/risk-compliance/report-writing/references/vars-report-template.md +224 -0
- package/skills/risk-compliance/report-writing/release-check.mjs +93 -0
- package/skills/risk-compliance/similar-domain/LICENSE +3 -0
- package/skills/risk-compliance/similar-domain/SKILL.md +203 -0
- package/skills/risk-compliance/similar-domain/references/entities.md +67 -0
- package/skills/risk-compliance/similar-domain/references/fingerprints.md +134 -0
- package/skills/risk-compliance/similar-domain/references/pivots.md +82 -0
- package/skills/risk-compliance/similar-domain/references/report-template.md +93 -0
- package/skills/risk-compliance/similar-domain/references/scoring.md +130 -0
- package/skills/risk-compliance/similar-domain/references/terminology.md +41 -0
- package/skills/risk-compliance/similar-domain/references/workflow.md +85 -0
- package/skills/risk-compliance/similar-domain/release-check.mjs +93 -0
- package/skills/risk-compliance/tld-detection/LICENSE +3 -0
- package/skills/risk-compliance/tld-detection/SKILL.md +245 -0
- package/skills/risk-compliance/tld-detection/agents/openai.yaml +9 -0
- package/skills/risk-compliance/tld-detection/references/chinese-polish.md +55 -0
- package/skills/risk-compliance/tld-detection/references/clean.md +51 -0
- package/skills/risk-compliance/tld-detection/references/clustering.md +235 -0
- package/skills/risk-compliance/tld-detection/references/decision.md +189 -0
- package/skills/risk-compliance/tld-detection/references/fx-conversion.md +232 -0
- package/skills/risk-compliance/tld-detection/references/report-template.md +272 -0
- package/skills/risk-compliance/tld-detection/references/streams.md +141 -0
- package/skills/risk-compliance/tld-detection/references/terminology.md +95 -0
- package/skills/risk-compliance/tld-detection/references/workflow.md +95 -0
- package/skills/risk-compliance/tld-detection/release-check.mjs +93 -0
- package/skills/risk-compliance/tld-detection/scripts/detect_ladders.py +390 -0
- package/skills/risk-compliance/tld-detection/scripts/detect_ladders.test.mjs +14 -0
- package/skills/risk-compliance/tld-detection/scripts/detect_streams.py +650 -0
- package/skills/risk-compliance/tld-detection/scripts/detect_streams.test.mjs +14 -0
- package/skills/risk-compliance/tld-detection/tests/test_detect_ladders.py +134 -0
- package/skills/risk-compliance/tld-detection/tests/test_detect_streams.py +520 -0
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Decide what follows a resolve-issues review round, from the reviewer's result file
|
|
3
|
+
// alone, so a P2 can never send a PR back for fixes and a P3 is ignored: never deferred,
|
|
4
|
+
// never filed, never checked.
|
|
5
|
+
//
|
|
6
|
+
// review-verdict.mjs --review <run>/reviews/<unit>-r<N>.json --head <sha> [--base <ref>] [--decision <id>] [--exit-verdict]
|
|
7
|
+
// review-verdict.mjs --carry --review <run>/reviews/<unit>-r<N+1>.json --head <sha> --base <ref>
|
|
8
|
+
//
|
|
9
|
+
// --carry: a HEAD pushed after a passing review (a CI fix or a base update) gets no
|
|
10
|
+
// reviewer. Run it in the unit's review clone with --head fetched. It writes the carry
|
|
11
|
+
// record for --head, a skip with `carry: "after-pass"` that carries round N's approval and
|
|
12
|
+
// records --head's merge-base and patch identity (so a later tier can anchor on it), and
|
|
13
|
+
// exits 0; then run github-reviews.mjs merge and this verdict on it as on any round. Round N
|
|
14
|
+
// is always the file just before it, and no later round may exist. It refuses (exit 3) when
|
|
15
|
+
// round N did not pass: incomplete, a P0/P1 open, or a GitHub re-review awaited. Only a P0/P1
|
|
16
|
+
// fix is reviewed. The verdict refuses a carry whose round is not its file's number.
|
|
17
|
+
//
|
|
18
|
+
// A skip record (`tier: "skip"`, written by better-review's rereview-scope.mjs
|
|
19
|
+
// --write-skip) is not taken on its word: each skip back to the last real review must
|
|
20
|
+
// carry the record just before it (same unit, previous round, complete, no open P0/P1,
|
|
21
|
+
// same fixRound), walked by filename; and with --base, the scope script is run again
|
|
22
|
+
// against the prior round and must still pick `skip` for --head. A carry record needs no
|
|
23
|
+
// rerun: the chain alone shows the approval it carries.
|
|
24
|
+
//
|
|
25
|
+
// Prints a JSON verdict. The verdict is in the output (`verdict`: pass, fix, handback, wait,
|
|
26
|
+
// decide, or review-again), and the exit code is 0 whenever the script ran and decided; 2 when
|
|
27
|
+
// it could not (bad command, unreadable file). --exit-verdict restores the old verdict exit
|
|
28
|
+
// codes below for one release (DEPRECATED: read `verdict` instead). Old exit codes:
|
|
29
|
+
// 0 pass: no P0/P1 on --head (or a confirmed skip); record `reviewedHead`; ready
|
|
30
|
+
// once CI is green
|
|
31
|
+
// 1 fix: the printed `fixBrief` file holds the P0/P1 findings only; it is
|
|
32
|
+
// the fix worker's whole finding list. `action` says how to fix, from a
|
|
33
|
+
// comparison with the previous round's file (`<unit>-r<N-1>.json` beside
|
|
34
|
+
// it, or --prior):
|
|
35
|
+
// fix first blocking round: fix each finding's rule at its one
|
|
36
|
+
// owner and close every row in its paths
|
|
37
|
+
// test-first a P0/P1 came back after a fix: name the rule it breaks,
|
|
38
|
+
// list every path that can break it, test each path, see
|
|
39
|
+
// the tests fail, then fix until all pass, with the
|
|
40
|
+
// escalation model
|
|
41
|
+
// one-by-one only new P0/P1, introduced by the last fix: one finding
|
|
42
|
+
// per fix worker, in turn, the smallest change that closes
|
|
43
|
+
// every row at the rule's owner
|
|
44
|
+
// 5 wait: no P0/P1 is open, but a GitHub reviewer whose changes request a fix brief
|
|
45
|
+
// already carried has not re-reviewed (`awaiting`, from github-reviews.mjs merge);
|
|
46
|
+
// request their re-review and run the merge and this verdict again when it lands
|
|
47
|
+
// 6 decide: a recurring spec finding came back with the same trigger and the same open
|
|
48
|
+
// paths, so implementer and reviewer read the requirement differently
|
|
49
|
+
// (`reason: "spec-question"`). The printed `decide` file (`<unit>-r<N>.decide.json`)
|
|
50
|
+
// holds the question; answer it in the plan's `decisions` (`answers` names the file),
|
|
51
|
+
// then run this verdict again with --decision <that entry's id>: it writes a test-first
|
|
52
|
+
// fix brief that carries the decision. A changed trigger or new open paths is an
|
|
53
|
+
// incomplete fix, fixed test-first, never a question
|
|
54
|
+
// 4 hand back: stop fixing. Two fix rounds passed without a new low of open P0/P1 path
|
|
55
|
+
// rows (`reason: "not-converging"`, read from every `<unit>-r<K>.json` before this one);
|
|
56
|
+
// hand back the PR and open findings
|
|
57
|
+
// 2 bad command or unreadable file (still 2 without --exit-verdict)
|
|
58
|
+
// 3 review-again: incomplete review (unfinished passes, an unresolved execution incident,
|
|
59
|
+
// no checks run, a P2 with neither a follow-up URL nor a dropbox id, a bad finding (a P0/P1
|
|
60
|
+
// needs trigger, rule, and paths with an open row), or another
|
|
61
|
+
// HEAD); finish the review after resolving any execution incident
|
|
62
|
+
// Completion is checked first; the reviewer's verdict is ignored and only
|
|
63
|
+
// P0/P1 findings decide whether a completed review needs product fixes. `gh-review-<n>` and
|
|
64
|
+
// `gh-comment-<n>` findings come from github-reviews.mjs merge and block like the reviewer's,
|
|
65
|
+
// on a skip record too, which carries only the reviewer's approval.
|
|
66
|
+
|
|
67
|
+
import { existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
68
|
+
import { basename, dirname, join } from 'node:path'
|
|
69
|
+
|
|
70
|
+
import { execFileSync } from 'node:child_process'
|
|
71
|
+
|
|
72
|
+
import { prPatchFingerprint, prPatchId, rereviewScope } from '../../better-review/scripts/rereview-scope.mjs'
|
|
73
|
+
import { isMainModule } from '../../engineering-runtime/scripts/main-module.mjs'
|
|
74
|
+
import { reviewCompletionProblems, reviewTier } from '../../engineering-runtime/scripts/review-completion.mjs'
|
|
75
|
+
|
|
76
|
+
export const REVIEW_VERSION = 1
|
|
77
|
+
export const BLOCKING = Object.freeze(['P0', 'P1'])
|
|
78
|
+
export const DEFERRABLE = Object.freeze(['P2'])
|
|
79
|
+
// Read and ignored: no dropbox entry, no follow-up issue, never in `deferred`.
|
|
80
|
+
export const SKIPPED = Object.freeze(['P3'])
|
|
81
|
+
// Fixes converge while the open P0/P1 path rows keep reaching new lows; STALLED_ROUNDS fix
|
|
82
|
+
// rounds without a new low are not converging: stop and hand back. No fixed round cap.
|
|
83
|
+
export const STALLED_ROUNDS = 2
|
|
84
|
+
|
|
85
|
+
export class ReviewVerdictError extends Error {}
|
|
86
|
+
|
|
87
|
+
// The reviewer, not the orchestrator, fixes an incomplete review.
|
|
88
|
+
export class ReviewIncomplete extends ReviewVerdictError {}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
const isText = (value) => typeof value === 'string' && value.trim() !== ''
|
|
92
|
+
const isRecord = (value) => value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
93
|
+
const hasFixRound = (review) => Object.hasOwn(review ?? {}, 'fixRound')
|
|
94
|
+
const isCounter = (value) => Number.isSafeInteger(value) && value >= 1
|
|
95
|
+
// github-reviews.mjs merge owns these ids; only they may ride on a skip record.
|
|
96
|
+
const GITHUB_ID = /^gh-(?:review|comment)-\d+$/
|
|
97
|
+
// A skip record that carries a passing review to a HEAD pushed after it, whatever changed.
|
|
98
|
+
export const AFTER_PASS = 'after-pass'
|
|
99
|
+
const isCarry = (review) => reviewTier(review) === 'skip' && review?.carry === AFTER_PASS
|
|
100
|
+
// A blocking finding names its rule and every path that must hold it, at least one open; a P0/P1
|
|
101
|
+
// with every row closed is resolved and belongs out of the file. Checked on the current review
|
|
102
|
+
// only, so earlier records written without the fields stay readable as priors.
|
|
103
|
+
function blockingShapeProblems(f, label) {
|
|
104
|
+
const problems = []
|
|
105
|
+
if (!isText(f?.trigger)) problems.push(`${label} (P0/P1) has no trigger`)
|
|
106
|
+
if (!isText(f?.rule)) problems.push(`${label} (P0/P1) has no rule`)
|
|
107
|
+
if (!Array.isArray(f?.paths) || f.paths.length === 0) return [...problems, `${label} (P0/P1) has no paths rows`]
|
|
108
|
+
for (const [j, row] of f.paths.entries()) {
|
|
109
|
+
if (!isText(row?.path)) problems.push(`${label}.paths[${j}] has no path`)
|
|
110
|
+
if (!['open', 'closed'].includes(row?.status)) problems.push(`${label}.paths[${j}] status is ${JSON.stringify(row?.status)}, expected open or closed`)
|
|
111
|
+
else if (row.status === 'closed' && !isText(row?.evidence)) problems.push(`${label}.paths[${j}] is closed without evidence`)
|
|
112
|
+
}
|
|
113
|
+
if (!f.paths.some((row) => row?.status === 'open')) problems.push(`${label} (P0/P1) has no open paths row; a resolved finding is left out of the file`)
|
|
114
|
+
return problems
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function checkReview(review, head, { requireCompletion = true, requireRulePaths = false } = {}) {
|
|
118
|
+
const problems = requireCompletion ? reviewCompletionProblems(review) : []
|
|
119
|
+
if (review?.version !== REVIEW_VERSION) problems.push(`review version is ${JSON.stringify(review?.version)}, expected ${REVIEW_VERSION}`)
|
|
120
|
+
if (!isText(review?.unit)) problems.push('review has no unit')
|
|
121
|
+
if (!isCounter(review?.round)) problems.push('review has no round (a safe integer from 1)')
|
|
122
|
+
if (hasFixRound(review) && (!isCounter(review.fixRound) || review.fixRound > review.round)) {
|
|
123
|
+
problems.push('review fixRound must be a positive safe integer no greater than round')
|
|
124
|
+
}
|
|
125
|
+
if (review?.head !== head) problems.push(`review is of HEAD ${JSON.stringify(review?.head)}, not ${head}`)
|
|
126
|
+
|
|
127
|
+
const findings = Array.isArray(review?.findings) ? review.findings : null
|
|
128
|
+
if (!findings) problems.push('review has no findings array (use [] for none)')
|
|
129
|
+
const ids = new Set()
|
|
130
|
+
for (const [i, f] of (findings ?? []).entries()) {
|
|
131
|
+
if (SKIPPED.includes(f?.severity)) continue
|
|
132
|
+
const label = isText(f?.id) ? f.id : `findings[${i}]`
|
|
133
|
+
if (!isText(f?.id)) problems.push(`${label} has no id`)
|
|
134
|
+
else if (ids.has(f.id)) problems.push(`duplicate finding id ${f.id}`)
|
|
135
|
+
else ids.add(f.id)
|
|
136
|
+
if (![...BLOCKING, ...DEFERRABLE].includes(f?.severity)) problems.push(`${label} has severity ${JSON.stringify(f?.severity)}, expected P0-P3`)
|
|
137
|
+
if (!isText(f?.summary)) problems.push(`${label} has no summary`)
|
|
138
|
+
if (requireRulePaths && BLOCKING.includes(f?.severity)) problems.push(...blockingShapeProblems(f, label))
|
|
139
|
+
}
|
|
140
|
+
return problems
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Blocking finding ids from the previous round's file, or null when there is none.
|
|
144
|
+
function priorBlockingIds(prior) {
|
|
145
|
+
if (!isRecord(prior) || !Array.isArray(prior.findings)) return null
|
|
146
|
+
return new Set(prior.findings.filter((f) => BLOCKING.includes(f?.severity) && isText(f?.id)).map((f) => f.id))
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// New counters need an identified immediate predecessor, not caller-authored cap relief.
|
|
150
|
+
// Legacy records keep their attempt-based accounting, including when used as a prior.
|
|
151
|
+
function effectiveFixRound(review, prior) {
|
|
152
|
+
if (!isCounter(review?.round)) throw new ReviewIncomplete('review round must be a positive safe integer')
|
|
153
|
+
const fixRound = hasFixRound(review) ? review.fixRound : review.round
|
|
154
|
+
if (!isCounter(fixRound) || fixRound > review.round) {
|
|
155
|
+
throw new ReviewIncomplete('review fixRound must be a positive safe integer no greater than round')
|
|
156
|
+
}
|
|
157
|
+
if (!hasFixRound(review) && !hasFixRound(prior)) return fixRound
|
|
158
|
+
if (prior === null || prior === undefined) {
|
|
159
|
+
if (fixRound !== review.round) throw new ReviewIncomplete('review fixRound cannot be lower than round without a prior review')
|
|
160
|
+
return fixRound
|
|
161
|
+
}
|
|
162
|
+
const problems = checkReview(prior, prior?.head, { requireCompletion: reviewTier(review) !== 'full' })
|
|
163
|
+
if (!isText(prior?.head)) problems.push('prior review has no HEAD')
|
|
164
|
+
if (prior?.unit !== review.unit) problems.push('prior review unit does not match current review')
|
|
165
|
+
if (prior?.round !== review.round - 1) problems.push('prior review round must immediately precede current round')
|
|
166
|
+
if (problems.length) throw new ReviewIncomplete(`invalid prior review for fixRound:\n- ${problems.join('\n- ')}`)
|
|
167
|
+
if (reviewCompletionProblems(prior).length > 0) {
|
|
168
|
+
const currentProblems = reviewCompletionProblems(review, { requirePasses: true })
|
|
169
|
+
if (currentProblems.length) throw new ReviewIncomplete(`full retry must complete every pass:\n- ${currentProblems.join('\n- ')}`)
|
|
170
|
+
}
|
|
171
|
+
const priorRound = hasFixRound(prior) ? prior.fixRound : prior.round
|
|
172
|
+
const expected = priorRound + (reviewCompletionProblems(prior).length === 0 && priorBlockingIds(prior).size > 0 ? 1 : 0)
|
|
173
|
+
if (fixRound !== expected) throw new ReviewIncomplete(`review fixRound is ${fixRound}, expected ${expected} from prior review`)
|
|
174
|
+
return expected
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const openPaths = (f) => (Array.isArray(f?.paths) ? f.paths : []).filter((row) => row?.status === 'open').map((row) => String(row.path)).sort().join('\n')
|
|
178
|
+
function sameOpenPaths(finding, prior) {
|
|
179
|
+
const before = prior.findings.find((f) => f?.id === finding.id)
|
|
180
|
+
return openPaths(before) === openPaths(finding)
|
|
181
|
+
}
|
|
182
|
+
const squash = (text) => String(text ?? '').trim().replace(/\s+/g, ' ')
|
|
183
|
+
function sameTrigger(finding, prior) {
|
|
184
|
+
const before = prior.findings.find((f) => f?.id === finding.id)
|
|
185
|
+
return squash(before?.trigger) === squash(finding.trigger)
|
|
186
|
+
}
|
|
187
|
+
// What the decide file asks: the finding's requirement as the reviewer reads it.
|
|
188
|
+
const questionOf = ({ id, summary, rule, trigger, specReference, requestedAction }) => ({ id, summary, rule, trigger, specReference, requestedAction })
|
|
189
|
+
|
|
190
|
+
// The work a fix still owes: open path rows across P0/P1 findings.
|
|
191
|
+
export function openBlockingRows(review) {
|
|
192
|
+
return (Array.isArray(review?.findings) ? review.findings : [])
|
|
193
|
+
.filter((f) => BLOCKING.includes(f?.severity))
|
|
194
|
+
.reduce((n, f) => n + (Array.isArray(f.paths) ? f.paths.filter((row) => row?.status === 'open').length : 0), 0)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Rounds since the last new low of open P0/P1 path rows, over this blocking loop: the completed
|
|
198
|
+
// records after the last completed pass, oldest first, ending with `review`. Incomplete records
|
|
199
|
+
// are passed over, never counted as progress; a record with a P0/P1 that has no `paths` (legacy)
|
|
200
|
+
// cannot be measured and is left out. Lows only fall and never go below 0, so a loop that hands
|
|
201
|
+
// back at 2 rounds without a new low always ends, whatever the counts do between lows.
|
|
202
|
+
export function roundsWithoutNewLow(review, history = []) {
|
|
203
|
+
const done = [...history, review].filter((r) => isRecord(r) && Array.isArray(r.findings) && reviewCompletionProblems(r).length === 0)
|
|
204
|
+
const lastPass = done.findLastIndex((r) => !r.findings.some((f) => BLOCKING.includes(f?.severity)))
|
|
205
|
+
const loop = done.slice(lastPass + 1).filter((r) => r.findings.every((f) => !BLOCKING.includes(f?.severity) || Array.isArray(f.paths)))
|
|
206
|
+
let low = Infinity
|
|
207
|
+
let since = 0
|
|
208
|
+
for (const r of loop) {
|
|
209
|
+
const rows = openBlockingRows(r)
|
|
210
|
+
if (rows < low) { low = rows; since = 0 } else since++
|
|
211
|
+
}
|
|
212
|
+
return since
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// How to act on a blocking round. better-review keeps a still-open finding's id across
|
|
216
|
+
// rounds, so a shared id means the last fix did not resolve it.
|
|
217
|
+
// `history` is every record before `prior`, oldest first, or a function that reads them; it is
|
|
218
|
+
// read only after the counters check out.
|
|
219
|
+
// `decision` is the id of the plan's `decisions` entry that answered this round's question:
|
|
220
|
+
// the fix it starts is new work, so this verdict skips the stall guard; later rounds count as usual.
|
|
221
|
+
export function fixAction(review, blocking, prior = null, history = [], { decision = null } = {}) {
|
|
222
|
+
effectiveFixRound(review, prior)
|
|
223
|
+
const earlier = typeof history === 'function' ? history() : history
|
|
224
|
+
if (!decision && roundsWithoutNewLow(review, [...earlier, prior]) >= STALLED_ROUNDS) return { verdict: 'handback', reason: 'not-converging' }
|
|
225
|
+
const before = review.round > 1 && reviewCompletionProblems(prior).length === 0 ? priorBlockingIds(prior) : null
|
|
226
|
+
if (!before || before.size === 0) return { verdict: 'fix', action: 'fix', recurring: [], introduced: [] }
|
|
227
|
+
const recurring = blocking.filter((f) => before.has(f.id)).map((f) => f.id)
|
|
228
|
+
const introduced = blocking.filter((f) => !before.has(f.id)).map((f) => f.id)
|
|
229
|
+
// A recurring spec finding is a question only when nothing about it moved: the same trigger
|
|
230
|
+
// fails on the same open paths, so the fix and the review read the requirement differently.
|
|
231
|
+
// A new trigger on the same line, or new open paths, is an incomplete fix.
|
|
232
|
+
const questions = blocking.filter((f) => before.has(f.id) && f.pass === 'spec' && sameOpenPaths(f, prior) && sameTrigger(f, prior))
|
|
233
|
+
if (questions.length && !decision) return { verdict: 'decide', reason: 'spec-question', question: questions.map(questionOf), recurring, introduced }
|
|
234
|
+
if (questions.length) return { verdict: 'fix', action: 'test-first', decision, recurring, introduced }
|
|
235
|
+
if (recurring.length > 0) return { verdict: 'fix', action: 'test-first', recurring, introduced }
|
|
236
|
+
return { verdict: 'fix', action: 'one-by-one', recurring, introduced }
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// One link of a skip chain: `link` must carry the record just before it, which is
|
|
240
|
+
// complete, of the same unit and the previous round, and left no P0/P1 open.
|
|
241
|
+
function skipLinkProblems(link, before) {
|
|
242
|
+
if (!isRecord(before)) return ['the review it carries is missing']
|
|
243
|
+
const problems = reviewCompletionProblems(before, { requirePasses: true }).map((p) => `the review it carries: ${p}`)
|
|
244
|
+
if (link.carriedFrom !== before.head) problems.push(`it carries ${JSON.stringify(link.carriedFrom)}, but the review before it is of ${JSON.stringify(before.head)}`)
|
|
245
|
+
if (before.unit !== link.unit) problems.push('its unit differs from the review it carries')
|
|
246
|
+
if (before.round !== link.round - 1) problems.push('its round does not follow the review it carries')
|
|
247
|
+
if (!Array.isArray(before.findings)) problems.push('the review it carries has no findings array')
|
|
248
|
+
else if (before.findings.some((f) => BLOCKING.includes(f?.severity))) problems.push('the review it carries has an open P0/P1')
|
|
249
|
+
const beforeFixRound = hasFixRound(before) ? before.fixRound : before.round
|
|
250
|
+
if (link.fixRound !== beforeFixRound) problems.push(`its fixRound is ${JSON.stringify(link.fixRound)}, expected ${beforeFixRound}`)
|
|
251
|
+
return problems
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// A skip carries an approval only when every link back to a real review holds, and the
|
|
255
|
+
// scope script, rerun now, still picks skip. `chain` is the previous record, then the
|
|
256
|
+
// ones before it, newest first; `confirmSkip()` returns the rerun's { tier, reasons }.
|
|
257
|
+
function checkSkip(review, chain, confirmSkip) {
|
|
258
|
+
let link = review
|
|
259
|
+
for (const [i, before] of [...chain, undefined].entries()) {
|
|
260
|
+
if (i > 0) {
|
|
261
|
+
const own = checkReview(link, link.head)
|
|
262
|
+
if (own.length) throw new ReviewIncomplete(`skip chain: round ${link.round} is not a valid record; spawn the reviewer:\n- ${own.join('\n- ')}`)
|
|
263
|
+
}
|
|
264
|
+
const problems = skipLinkProblems(link, before)
|
|
265
|
+
if (problems.length) throw new ReviewIncomplete(`skip record does not hold: round ${link.round}: ${problems.join('; ')}; spawn the reviewer`)
|
|
266
|
+
if (reviewTier(before) !== 'skip') break
|
|
267
|
+
link = before
|
|
268
|
+
}
|
|
269
|
+
if (isCarry(review)) return
|
|
270
|
+
if (typeof confirmSkip !== 'function') throw new ReviewVerdictError('--base is required to confirm a skip record')
|
|
271
|
+
const rerun = confirmSkip()
|
|
272
|
+
if (rerun?.tier !== 'skip') {
|
|
273
|
+
throw new ReviewIncomplete(`skip record does not hold: the scope script picks ${JSON.stringify(rerun?.tier)} for this HEAD; spawn the reviewer${rerun?.reasons?.length ? `:\n- ${rerun.reasons.join('\n- ')}` : ''}`)
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export function reviewVerdict(review, head, prior = null, { confirmSkip, earlier = [], history = [], decision = null } = {}) {
|
|
278
|
+
if (!isText(head)) throw new ReviewVerdictError('--head is required')
|
|
279
|
+
const problems = checkReview(review, head, { requireRulePaths: true })
|
|
280
|
+
if (problems.length) throw new ReviewIncomplete(`incomplete review; spawn the reviewer again only after resolving any execution incident:\n- ${problems.join('\n- ')}`)
|
|
281
|
+
const skip = reviewTier(review) === 'skip'
|
|
282
|
+
if (skip) checkSkip(review, [prior, ...earlier], confirmSkip)
|
|
283
|
+
|
|
284
|
+
const fixRound = effectiveFixRound(review, prior)
|
|
285
|
+
const blocking = review.findings.filter((f) => BLOCKING.includes(f.severity))
|
|
286
|
+
const deferred = review.findings
|
|
287
|
+
.filter((f) => DEFERRABLE.includes(f.severity))
|
|
288
|
+
.map(({ id, severity, summary, followUpUrl, dropboxId }) => ({ id, severity, summary, followUpUrl, dropboxId }))
|
|
289
|
+
const awaiting = Array.isArray(review.external?.awaiting) ? review.external.awaiting : []
|
|
290
|
+
const base = { unit: review.unit, round: review.round, fixRound, head, commands: review.commands, deferred, ...(awaiting.length ? { awaiting } : {}) }
|
|
291
|
+
const skipped = skip ? { tier: 'skip', carriedFrom: review.carriedFrom } : {}
|
|
292
|
+
if (blocking.length === 0) {
|
|
293
|
+
return awaiting.length ? { verdict: 'wait', ...base, ...skipped } : { verdict: 'pass', reviewedHead: head, ...base, ...skipped }
|
|
294
|
+
}
|
|
295
|
+
if (skip && blocking.some((f) => !GITHUB_ID.test(String(f.id)))) throw new ReviewIncomplete('a skip record cannot hold findings; spawn the reviewer')
|
|
296
|
+
return { ...fixAction(review, blocking, prior, history, { decision }), ...base, fix: blocking }
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// The record --carry writes for `head`: the previous round's approval, carried with no
|
|
300
|
+
// reviewer. The previous round must have passed: complete, every pass done, no P0/P1, and no
|
|
301
|
+
// GitHub re-review awaited.
|
|
302
|
+
// `identity` is --head's { mergeBase, patchId, patchFingerprint }.
|
|
303
|
+
export function carryRecord(prior, head, identity = {}) {
|
|
304
|
+
if (!isText(head)) throw new ReviewVerdictError('--head is required')
|
|
305
|
+
if (!isRecord(prior)) throw new ReviewIncomplete('no previous round to carry; spawn the reviewer')
|
|
306
|
+
const problems = [...new Set([...checkReview(prior, prior.head), ...reviewCompletionProblems(prior, { requirePasses: true })])]
|
|
307
|
+
if (!isText(prior.head)) problems.push('it has no head')
|
|
308
|
+
if (prior.complete !== true) problems.push('it is not marked complete')
|
|
309
|
+
if (Array.isArray(prior.findings) && prior.findings.some((f) => BLOCKING.includes(f?.severity))) problems.push('it has an open P0/P1')
|
|
310
|
+
if (Array.isArray(prior.external?.awaiting) && prior.external.awaiting.length > 0) problems.push('it awaits a GitHub re-review')
|
|
311
|
+
if (prior.head === head) problems.push(`it is already of ${head}`)
|
|
312
|
+
if (problems.length) throw new ReviewIncomplete(`round ${prior.round} did not pass, so ${head} cannot carry it; review it:\n- ${problems.join('\n- ')}`)
|
|
313
|
+
return {
|
|
314
|
+
version: prior.version,
|
|
315
|
+
unit: prior.unit,
|
|
316
|
+
round: prior.round + 1,
|
|
317
|
+
fixRound: hasFixRound(prior) ? prior.fixRound : prior.round,
|
|
318
|
+
head,
|
|
319
|
+
...identity,
|
|
320
|
+
tier: 'skip',
|
|
321
|
+
carry: AFTER_PASS,
|
|
322
|
+
carriedFrom: prior.head,
|
|
323
|
+
...(Number.isInteger(prior.fixLinesSinceFull) ? { fixLinesSinceFull: prior.fixLinesSinceFull } : {}),
|
|
324
|
+
oversize: false,
|
|
325
|
+
complete: true,
|
|
326
|
+
passes: prior.passes,
|
|
327
|
+
commands: [],
|
|
328
|
+
sweeps: prior.sweeps ?? [],
|
|
329
|
+
boundaries: prior.boundaries ?? [],
|
|
330
|
+
findings: [],
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const INSTRUCTIONS = {
|
|
335
|
+
'fix': 'Fix only these findings. For each, fix its `rule` at the one place every path passes through, never by copying a guard to each path, and close every row in its `paths`.',
|
|
336
|
+
'test-first': 'The findings listed in `recurring` survived a fix: the last fix closed one path and missed others. For each, before changing code: name the rule the bug breaks; list every code path that can break it, including the trigger of this finding id in each earlier round\'s review file beside this one; write a test per path from its trigger and run them: see it fail on each open path. Then fix the rule at its one owner, the place every path passes through, not path by path, until every test passes. "Fixed" is every path\'s test passing. Fix the rest as usual.',
|
|
337
|
+
'one-by-one': 'The last fix introduced these findings. Fix them one at a time, one fix worker each, with the smallest change that closes every row of its `paths` at the rule\'s owner; touch nothing else.',
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// The fix worker's brief carries only P0/P1 findings; P2/P3 never reach it.
|
|
341
|
+
export function fixBriefOf(result) {
|
|
342
|
+
const fixRound = hasFixRound(result) ? result.fixRound : result.round
|
|
343
|
+
if (!isCounter(result.round) || !isCounter(fixRound) || fixRound > result.round) {
|
|
344
|
+
throw new ReviewIncomplete('fix brief round and fixRound must be positive safe integers with fixRound no greater than round')
|
|
345
|
+
}
|
|
346
|
+
return {
|
|
347
|
+
unit: result.unit,
|
|
348
|
+
round: result.round,
|
|
349
|
+
fixRound,
|
|
350
|
+
head: result.head,
|
|
351
|
+
action: result.action,
|
|
352
|
+
instruction: `${INSTRUCTIONS[result.action]}${result.decision ? ` Decision ${result.decision} in the plan's \`decisions\` settles what the recurring spec finding requires: fix it as that decision reads.` : ''} Every other finding is out of scope: a P2 is deferred to the dropbox or a follow-up issue, and a P3 is skipped.`,
|
|
353
|
+
...(result.decision ? { decision: result.decision } : {}),
|
|
354
|
+
recurring: result.recurring,
|
|
355
|
+
fix: result.fix,
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const USAGE = 'usage: review-verdict.mjs --review <reviews/<unit>-r<N>.json> --head <sha> [--prior <reviews/<unit>-r<N-1>.json>] [--base <ref> (required for a scope-script skip record)] [--decision <decisions id>]\n review-verdict.mjs --carry --review <reviews/<unit>-r<N+1>.json> --head <sha> --base <ref>'
|
|
360
|
+
|
|
361
|
+
const ROUND_FILE = /^(.*-r)(\d+)\.json$/
|
|
362
|
+
// The round a review file's name gives it, or null when the name has none.
|
|
363
|
+
const roundOfFile = (path) => { const m = basename(path).match(ROUND_FILE); return m ? Number(m[2]) : null }
|
|
364
|
+
|
|
365
|
+
function parseArgs(argv) {
|
|
366
|
+
const opts = {}
|
|
367
|
+
for (let i = 0; i < argv.length; i += 2) {
|
|
368
|
+
if (argv[i] === '--carry') { opts.carry = true; i--; continue }
|
|
369
|
+
if (argv[i] === '--exit-verdict') { opts.exitVerdict = true; i--; continue }
|
|
370
|
+
if (argv[i] === '--json') { i--; continue } // the output is always JSON
|
|
371
|
+
if (!argv[i]?.startsWith('--') || argv[i + 1] === undefined) throw new ReviewVerdictError(USAGE)
|
|
372
|
+
opts[argv[i].slice(2)] = argv[i + 1]
|
|
373
|
+
}
|
|
374
|
+
if (!opts.review || !opts.head) throw new ReviewVerdictError(USAGE)
|
|
375
|
+
return opts
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function readJson(path) {
|
|
379
|
+
let text
|
|
380
|
+
try {
|
|
381
|
+
text = readFileSync(path, 'utf8')
|
|
382
|
+
} catch (error) {
|
|
383
|
+
throw new ReviewVerdictError(`cannot read ${path}: ${error.message}`)
|
|
384
|
+
}
|
|
385
|
+
try {
|
|
386
|
+
return JSON.parse(text)
|
|
387
|
+
} catch (error) {
|
|
388
|
+
throw new ReviewIncomplete(`incomplete review; spawn the reviewer again:\n- ${path} is not JSON: ${error.message}`)
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// The records before a skip prior, newest first, by filename, back to the first non-skip.
|
|
393
|
+
// A chain that cannot be walked stays short, and the skip check then fails closed.
|
|
394
|
+
function readEarlier(opts, prior) {
|
|
395
|
+
const path = opts.prior ?? opts.review
|
|
396
|
+
const m = basename(path).match(/^(.*-r)(\d+)\.json$/)
|
|
397
|
+
if (!m) return []
|
|
398
|
+
const out = []
|
|
399
|
+
let current = prior
|
|
400
|
+
let n = Number(m[2]) - (opts.prior ? 1 : 2)
|
|
401
|
+
while (reviewTier(current) === 'skip' && n >= 1) {
|
|
402
|
+
try { current = JSON.parse(readFileSync(join(dirname(path), `${m[1]}${n}.json`), 'utf8')) } catch { break }
|
|
403
|
+
out.push(current)
|
|
404
|
+
n--
|
|
405
|
+
}
|
|
406
|
+
return out
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Legacy unreadable priors remain optional. New counters must not silently discard
|
|
410
|
+
// a supplied or malformed prior; a missing inferred file still means no evidence.
|
|
411
|
+
function readPrior(opts, review) {
|
|
412
|
+
let path = opts.prior
|
|
413
|
+
if (!path) {
|
|
414
|
+
const m = basename(opts.review).match(/^(.*-r)(\d+)\.json$/)
|
|
415
|
+
if (!m || Number(m[2]) < 2) return null
|
|
416
|
+
path = join(dirname(opts.review), `${m[1]}${Number(m[2]) - 1}.json`)
|
|
417
|
+
}
|
|
418
|
+
try {
|
|
419
|
+
const prior = JSON.parse(readFileSync(path, 'utf8'))
|
|
420
|
+
if (hasFixRound(review) && !isRecord(prior)) throw new ReviewIncomplete('prior review must be an object')
|
|
421
|
+
return prior
|
|
422
|
+
} catch (error) {
|
|
423
|
+
if (hasFixRound(review) && (opts.prior || error.code !== 'ENOENT')) {
|
|
424
|
+
throw new ReviewIncomplete(`cannot use prior review for fixRound at ${path}: ${error.message}`)
|
|
425
|
+
}
|
|
426
|
+
return null
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Every record before the prior, oldest first, for the stall guard: `<unit>-r1.json` up to the
|
|
431
|
+
// one before the prior, beside the prior (or the review). Fails closed: the guard cannot be
|
|
432
|
+
// skipped by a missing file or a name it cannot number.
|
|
433
|
+
function readHistory(opts, review) {
|
|
434
|
+
const from = opts.prior ?? opts.review
|
|
435
|
+
const m = basename(from).match(/^(.*-r)(\d+)\.json$/)
|
|
436
|
+
const last = m && Number(m[2]) - (opts.prior ? 1 : 2)
|
|
437
|
+
if (!m) {
|
|
438
|
+
if (isCounter(review?.round) && review.round >= 3) {
|
|
439
|
+
throw new ReviewIncomplete(`cannot number earlier reviews from ${from}; name review files <unit>-r<N>.json so the stall guard can read every round`)
|
|
440
|
+
}
|
|
441
|
+
return []
|
|
442
|
+
}
|
|
443
|
+
const out = []
|
|
444
|
+
for (let n = 1; n <= last; n++) {
|
|
445
|
+
const path = join(dirname(from), `${m[1]}${m[2].startsWith('0') ? String(n).padStart(m[2].length, '0') : n}.json`)
|
|
446
|
+
try { out.push(JSON.parse(readFileSync(path, 'utf8'))) } catch (error) {
|
|
447
|
+
throw new ReviewIncomplete(`cannot read earlier review ${path} for the stall guard: ${error.message}`)
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return out
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (isMainModule(import.meta.url)) {
|
|
454
|
+
try {
|
|
455
|
+
const opts = parseArgs(process.argv.slice(2))
|
|
456
|
+
if (opts.carry) {
|
|
457
|
+
const n = roundOfFile(opts.review)
|
|
458
|
+
if (n === null || n < 2) throw new ReviewVerdictError(`--carry needs --review named <unit>-r<N>.json with N from 2: ${opts.review}`)
|
|
459
|
+
if (opts.prior) throw new ReviewVerdictError('--carry always carries the round just before --review; drop --prior')
|
|
460
|
+
if (!opts.base) throw new ReviewVerdictError('--carry needs --base <review base> to record the merge-base')
|
|
461
|
+
if (existsSync(opts.review)) throw new ReviewVerdictError(`${opts.review} exists; carry writes a new round`)
|
|
462
|
+
const stem = basename(opts.review).match(ROUND_FILE)[1]
|
|
463
|
+
const later = readdirSync(dirname(opts.review)).find((name) => name.startsWith(stem) && roundOfFile(name) > n)
|
|
464
|
+
if (later) throw new ReviewVerdictError(`${later} exists; carry only the newest round`)
|
|
465
|
+
const prior = readPrior(opts, { fixRound: 1 })
|
|
466
|
+
if (isRecord(prior) && prior.round !== n - 1) throw new ReviewIncomplete(`the round before ${basename(opts.review)} records round ${prior.round}, not ${n - 1}; review it`)
|
|
467
|
+
carryRecord(prior, opts.head) // refuse a round that did not pass before touching git
|
|
468
|
+
const git = (args) => execFileSync('git', args, { encoding: 'utf8' }).trim()
|
|
469
|
+
let identity
|
|
470
|
+
try {
|
|
471
|
+
const mergeBase = git(['merge-base', opts.base, opts.head])
|
|
472
|
+
identity = { mergeBase, patchId: prPatchId(mergeBase, opts.head), patchFingerprint: prPatchFingerprint(mergeBase, opts.head) }
|
|
473
|
+
} catch (error) {
|
|
474
|
+
throw new ReviewVerdictError(`cannot read ${opts.head} against ${opts.base} here; run --carry in the unit's review clone with both fetched: ${String(error.stderr || error.message).trim()}`)
|
|
475
|
+
}
|
|
476
|
+
const record = carryRecord(prior, opts.head, identity)
|
|
477
|
+
writeFileSync(opts.review, `${JSON.stringify(record, null, 2)}\n`)
|
|
478
|
+
process.stdout.write(`${JSON.stringify({ carried: opts.review, round: record.round, carriedFrom: record.carriedFrom, head: record.head }, null, 2)}\n`)
|
|
479
|
+
} else {
|
|
480
|
+
const review = readJson(opts.review)
|
|
481
|
+
const n = roundOfFile(opts.review)
|
|
482
|
+
if (isCarry(review) && n !== review.round) throw new ReviewIncomplete(`carry record ${opts.review} records round ${JSON.stringify(review.round)}, not its file's ${n}; review it`)
|
|
483
|
+
const prior = readPrior(opts, review)
|
|
484
|
+
const confirmSkip = opts.base ? () => rereviewScope({ prior, base: opts.base, head: opts.head }) : undefined
|
|
485
|
+
const earlier = reviewTier(review) === 'skip' ? readEarlier(opts, prior) : []
|
|
486
|
+
const history = () => readHistory(opts, review)
|
|
487
|
+
const result = reviewVerdict(review, opts.head, prior, { confirmSkip, earlier, history, decision: opts.decision ?? null })
|
|
488
|
+
if (result.verdict === 'fix') {
|
|
489
|
+
result.fixBrief = join(dirname(opts.review), `${basename(opts.review, '.json')}.fix.json`)
|
|
490
|
+
writeFileSync(result.fixBrief, `${JSON.stringify(fixBriefOf(result), null, 2)}\n`)
|
|
491
|
+
}
|
|
492
|
+
if (result.verdict === 'decide') {
|
|
493
|
+
result.decide = join(dirname(opts.review), `${basename(opts.review, '.json')}.decide.json`)
|
|
494
|
+
writeFileSync(result.decide, `${JSON.stringify({ unit: result.unit, round: result.round, head: result.head, reason: result.reason, question: result.question }, null, 2)}\n`)
|
|
495
|
+
}
|
|
496
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`)
|
|
497
|
+
process.exitCode = opts.exitVerdict ? { pass: 0, fix: 1, handback: 4, wait: 5, decide: 6 }[result.verdict] : 0
|
|
498
|
+
}
|
|
499
|
+
} catch (error) {
|
|
500
|
+
if (error instanceof ReviewIncomplete) {
|
|
501
|
+
// An incomplete review is an answer (review again), not a failure of the script.
|
|
502
|
+
process.stdout.write(`${JSON.stringify({ verdict: 'review-again', reason: error.message }, null, 2)}\n`)
|
|
503
|
+
process.exitCode = process.argv.includes('--exit-verdict') ? 3 : 0
|
|
504
|
+
} else {
|
|
505
|
+
process.stderr.write(`${error instanceof ReviewVerdictError ? error.message : error.stack}\n`)
|
|
506
|
+
process.exitCode = 2
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|