@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,182 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// REFERENCE Next.js App Router discovery adapter — copy and adapt per repo.
|
|
3
|
+
// Emits a surface inventory satisfying references/breadth/inventory-contract.md.
|
|
4
|
+
//
|
|
5
|
+
// node nextjs-inventory.mjs <appDir> [--out docs/tests/_generated/surface-inventory.json] [--check]
|
|
6
|
+
//
|
|
7
|
+
// Routes come from `page.*` files; APIs from `route.*` files. Route groups
|
|
8
|
+
// `(group)` are stripped; dynamic segments `[id]`/`[...slug]` are kept as-is so
|
|
9
|
+
// ids are stable.
|
|
10
|
+
//
|
|
11
|
+
// It also emits the MECHANICAL taxonomy the Surface Baseline Contract needs:
|
|
12
|
+
// - class: route -> read | detail | mutation; api -> read | mutation
|
|
13
|
+
// - reachability: best-effort reachable | guarded (from the layout/middleware chain)
|
|
14
|
+
// These are deliberately conservative heuristics: the adapter may only emit the
|
|
15
|
+
// mechanical class (never `sensitive`/exclusion — those are human-declared). When
|
|
16
|
+
// in doubt about a write surface it biases to `mutation`, because under-calling a
|
|
17
|
+
// write form `read` would wrongly let it be excluded. Reachability here is a hint;
|
|
18
|
+
// the full Route Reachability Protocol (and human review) refine `blocked`/
|
|
19
|
+
// `unreachable`. It does NOT cover DB tables or events — extend for those kinds.
|
|
20
|
+
|
|
21
|
+
import fs from 'node:fs'
|
|
22
|
+
import path from 'node:path'
|
|
23
|
+
import process from 'node:process'
|
|
24
|
+
|
|
25
|
+
const IGNORE = new Set(['node_modules', '.git', '.next'])
|
|
26
|
+
const PAGE_RE = /^page\.(tsx|ts|jsx|js|mdx)$/
|
|
27
|
+
const ROUTE_RE = /^route\.(ts|js)$/
|
|
28
|
+
const LAYOUT_RE = /^layout\.(tsx|ts|jsx|js)$/
|
|
29
|
+
const COMPONENT_RE = /\.(tsx|ts|jsx|js|mdx)$/
|
|
30
|
+
|
|
31
|
+
// A page (or a component colocated in its route segment) that mutates state.
|
|
32
|
+
const WRITE_RE = /<form\b|onSubmit|action=\{|formAction|use(FormState|ActionState|Mutation)\b|type=["']submit["']|method:\s*["'](post|put|patch|delete)["']|fetch\([^)]*method\s*:\s*["'](post|put|patch|delete)|['"]use server['"]/i
|
|
33
|
+
// An API route file exporting a write method.
|
|
34
|
+
const METHOD_EXPORT_RE = /export\s+(?:async\s+function|const|let|var|function)\s+(POST|PUT|PATCH|DELETE)\b|export\s*\{[^}]*\b(POST|PUT|PATCH|DELETE)\b/
|
|
35
|
+
// Guard signals in a layout / middleware that gate reachability.
|
|
36
|
+
const GUARD_RE = /\bredirect\(|\bnotFound\(|getServerSession|\bauth\(\)|requireAuth|getToken|currentUser|\bunauthorized\(|clerkMiddleware|withAuth/i
|
|
37
|
+
|
|
38
|
+
function* walk(dir) {
|
|
39
|
+
let entries
|
|
40
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }) } catch { return }
|
|
41
|
+
for (const e of entries) {
|
|
42
|
+
const full = path.join(dir, e.name)
|
|
43
|
+
if (e.isDirectory()) { if (!IGNORE.has(e.name)) yield* walk(full) }
|
|
44
|
+
else yield full
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function read(file) { try { return fs.readFileSync(file, 'utf8') } catch { return '' } }
|
|
49
|
+
|
|
50
|
+
function toRoute(appDir, file) {
|
|
51
|
+
const rel = path.relative(appDir, path.dirname(file)).split(path.sep)
|
|
52
|
+
.filter((seg) => seg && !(seg.startsWith('(') && seg.endsWith(')'))) // drop route groups
|
|
53
|
+
.filter((seg) => !seg.startsWith('@')) // drop parallel route slots
|
|
54
|
+
.join('/')
|
|
55
|
+
return '/' + rel
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Mechanical class for a page: mutation > detail > read.
|
|
59
|
+
function routeClass(pageFile) {
|
|
60
|
+
const dir = path.dirname(pageFile)
|
|
61
|
+
let text = ''
|
|
62
|
+
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
63
|
+
if (!e.isFile() || !COMPONENT_RE.test(e.name)) continue
|
|
64
|
+
if (LAYOUT_RE.test(e.name) || ROUTE_RE.test(e.name)) continue
|
|
65
|
+
text += read(path.join(dir, e.name)) + '\n'
|
|
66
|
+
}
|
|
67
|
+
if (WRITE_RE.test(text)) return 'mutation'
|
|
68
|
+
if (/\[[^\]]+\]/.test(dir)) return 'detail' // dynamic segment -> renders one entity
|
|
69
|
+
return 'read'
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Mechanical class for an API route: mutation if it exports a write method.
|
|
73
|
+
function apiClass(routeFile) {
|
|
74
|
+
return METHOD_EXPORT_RE.test(read(routeFile)) ? 'mutation' : 'read'
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Best-effort reachability from the layout chain + root middleware.
|
|
78
|
+
function reachability(appDir, pageFile, hasMiddleware) {
|
|
79
|
+
if (hasMiddleware) return 'guarded'
|
|
80
|
+
let dir = path.dirname(pageFile)
|
|
81
|
+
const stop = path.resolve(appDir)
|
|
82
|
+
while (true) {
|
|
83
|
+
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
84
|
+
if (e.isFile() && LAYOUT_RE.test(e.name) && GUARD_RE.test(read(path.join(dir, e.name)))) return 'guarded'
|
|
85
|
+
}
|
|
86
|
+
if (path.resolve(dir) === stop) break
|
|
87
|
+
const parent = path.dirname(dir)
|
|
88
|
+
if (parent === dir) break
|
|
89
|
+
dir = parent
|
|
90
|
+
}
|
|
91
|
+
return 'reachable'
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function detectMiddleware(appDir) {
|
|
95
|
+
const parent = path.dirname(appDir)
|
|
96
|
+
for (const base of [appDir, parent]) {
|
|
97
|
+
for (const m of ['middleware.ts', 'middleware.js']) {
|
|
98
|
+
if (fs.existsSync(path.join(base, m))) return true
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return false
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Feature-derived presentation signals (Dimension B escalation). Detected
|
|
105
|
+
// deterministically at the app level and attached to every route, so the ledger
|
|
106
|
+
// raises the presentation floor (dark-theme cell, RTL/long-locale cells). This is
|
|
107
|
+
// the reference granularity; a stack can refine to per-route detection.
|
|
108
|
+
// - themeToggle: a `next-themes` dep or a useTheme/ThemeProvider reference
|
|
109
|
+
// - i18n: a known i18n library dependency
|
|
110
|
+
function detectSignals(appDir) {
|
|
111
|
+
const roots = [appDir, path.dirname(appDir)]
|
|
112
|
+
let pkg = {}
|
|
113
|
+
for (const base of roots) {
|
|
114
|
+
const p = path.join(base, 'package.json')
|
|
115
|
+
if (fs.existsSync(p)) { try { pkg = JSON.parse(fs.readFileSync(p, 'utf8')) } catch { /* ignore */ } break }
|
|
116
|
+
}
|
|
117
|
+
const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) }
|
|
118
|
+
const dep = (re) => Object.keys(deps).some((d) => re.test(d))
|
|
119
|
+
const I18N_DEP = /^(next-intl|next-i18next|react-i18next|i18next|@lingui|react-intl|@formatjs)/
|
|
120
|
+
const THEME_DEP = /^(next-themes)$/
|
|
121
|
+
// a cheap source grep for theme toggling when there's no dedicated dep
|
|
122
|
+
let themeInSource = false
|
|
123
|
+
for (const f of walk(appDir)) {
|
|
124
|
+
if (!COMPONENT_RE.test(f)) continue
|
|
125
|
+
if (/\buseTheme\b|<ThemeProvider\b|data-theme=/.test(fs.readFileSync(f, 'utf8'))) { themeInSource = true; break }
|
|
126
|
+
}
|
|
127
|
+
const signals = {}
|
|
128
|
+
if (dep(THEME_DEP) || themeInSource) signals.themeToggle = true
|
|
129
|
+
if (dep(I18N_DEP)) signals.i18n = true
|
|
130
|
+
return Object.keys(signals).length ? signals : null
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function build(appDir) {
|
|
134
|
+
const hasMiddleware = detectMiddleware(appDir)
|
|
135
|
+
const signals = detectSignals(appDir)
|
|
136
|
+
const surfaces = []
|
|
137
|
+
for (const f of walk(appDir)) {
|
|
138
|
+
const base = path.basename(f)
|
|
139
|
+
if (PAGE_RE.test(base)) {
|
|
140
|
+
const s = { id: toRoute(appDir, f), kind: 'route', class: routeClass(f), reachability: reachability(appDir, f, hasMiddleware) }
|
|
141
|
+
if (signals) s.signals = signals
|
|
142
|
+
surfaces.push(s)
|
|
143
|
+
} else if (ROUTE_RE.test(base)) {
|
|
144
|
+
surfaces.push({ id: toRoute(appDir, f), kind: 'api', class: apiClass(f) })
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// de-dupe by id (e.g. page + nested layout edge cases), keep strongest class.
|
|
148
|
+
const byId = new Map()
|
|
149
|
+
const rank = { mutation: 3, detail: 2, read: 1, undefined: 0 }
|
|
150
|
+
for (const s of surfaces) {
|
|
151
|
+
const prev = byId.get(s.id)
|
|
152
|
+
if (!prev || (rank[s.class] || 0) > (rank[prev.class] || 0)) byId.set(s.id, s)
|
|
153
|
+
}
|
|
154
|
+
return { surfaces: [...byId.values()].sort((a, b) => a.id.localeCompare(b.id)) }
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function main() {
|
|
158
|
+
const [, , appDir, ...rest] = process.argv
|
|
159
|
+
const usage = 'usage: nextjs-inventory.mjs <appDir> [--out file] [--check]'
|
|
160
|
+
// A flag in the appDir slot (`--help`, `--out`) used to be walked as a directory, find
|
|
161
|
+
// nothing, and WRITE a zero-surface inventory into the cwd. Fail before any write.
|
|
162
|
+
if (!appDir || appDir.startsWith('-')) { console.error(usage); process.exit(2) }
|
|
163
|
+
if (!fs.existsSync(appDir) || !fs.statSync(appDir).isDirectory()) { console.error(`nextjs-inventory: app directory not found: ${appDir}\n${usage}`); process.exit(2) }
|
|
164
|
+
let out = 'docs/tests/_generated/surface-inventory.json'
|
|
165
|
+
let check = false
|
|
166
|
+
for (let i = 0; i < rest.length; i++) {
|
|
167
|
+
if (rest[i] === '--out') out = rest[++i]
|
|
168
|
+
else if (rest[i] === '--check') check = true
|
|
169
|
+
}
|
|
170
|
+
const inv = build(appDir)
|
|
171
|
+
const json = JSON.stringify(inv, null, 2) + '\n'
|
|
172
|
+
if (check) {
|
|
173
|
+
const prev = fs.existsSync(out) ? fs.readFileSync(out, 'utf8') : ''
|
|
174
|
+
if (prev !== json) { console.error(`inventory stale: ${out} does not match discovered surfaces. Re-run without --check.`); process.exit(1) }
|
|
175
|
+
console.log(`inventory up to date (${inv.surfaces.length} surfaces)`); return
|
|
176
|
+
}
|
|
177
|
+
fs.mkdirSync(path.dirname(out), { recursive: true })
|
|
178
|
+
fs.writeFileSync(out, json)
|
|
179
|
+
console.log(`wrote ${out} (${inv.surfaces.length} surfaces)`)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
main()
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// Tests for the reference Next.js adapter's feature-derived signal detection
|
|
2
|
+
// (issue #200). Runs the adapter as a subprocess against temp fixtures — proves
|
|
3
|
+
// AC1 (emits signals) and AC5 (deterministic) without mocking. Run: `node --test`.
|
|
4
|
+
|
|
5
|
+
import { test } from 'node:test'
|
|
6
|
+
import assert from 'node:assert/strict'
|
|
7
|
+
import fs from 'node:fs'
|
|
8
|
+
import os from 'node:os'
|
|
9
|
+
import path from 'node:path'
|
|
10
|
+
import { fileURLToPath } from 'node:url'
|
|
11
|
+
import { execFileSync, spawnSync } from 'node:child_process'
|
|
12
|
+
|
|
13
|
+
const ADAPTER = fileURLToPath(new URL('./nextjs-inventory.mjs', import.meta.url))
|
|
14
|
+
|
|
15
|
+
function runAdapter(appDir) {
|
|
16
|
+
const out = path.join(appDir, '..', 'inv.json')
|
|
17
|
+
execFileSync('node', [ADAPTER, appDir, '--out', out], { stdio: 'pipe' })
|
|
18
|
+
return JSON.parse(fs.readFileSync(out, 'utf8'))
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function fixture(pkg, files) {
|
|
22
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'nextadp-'))
|
|
23
|
+
fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify(pkg))
|
|
24
|
+
for (const [rel, content] of Object.entries(files)) {
|
|
25
|
+
const p = path.join(dir, rel)
|
|
26
|
+
fs.mkdirSync(path.dirname(p), { recursive: true })
|
|
27
|
+
fs.writeFileSync(p, content)
|
|
28
|
+
}
|
|
29
|
+
return dir
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
test('detects themeToggle (next-themes dep) and i18n (next-intl dep) on every route', () => {
|
|
33
|
+
const dir = fixture(
|
|
34
|
+
{ dependencies: { 'next-themes': '^0.2.0', 'next-intl': '^3.0.0' } },
|
|
35
|
+
{ 'app/page.tsx': 'export default function P(){return <div/>}',
|
|
36
|
+
'app/dashboard/page.tsx': 'export default function D(){return <div/>}' },
|
|
37
|
+
)
|
|
38
|
+
try {
|
|
39
|
+
const inv = runAdapter(path.join(dir, 'app'))
|
|
40
|
+
assert.equal(inv.surfaces.length, 2)
|
|
41
|
+
for (const s of inv.surfaces) {
|
|
42
|
+
assert.equal(s.signals?.themeToggle, true)
|
|
43
|
+
assert.equal(s.signals?.i18n, true)
|
|
44
|
+
}
|
|
45
|
+
} finally { fs.rmSync(dir, { recursive: true, force: true }) }
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('detects themeToggle from source (useTheme) with no dep', () => {
|
|
49
|
+
const dir = fixture(
|
|
50
|
+
{ dependencies: {} },
|
|
51
|
+
{ 'app/page.tsx': "import { useTheme } from 'somewhere'\nexport default function P(){ useTheme(); return <div/> }" },
|
|
52
|
+
)
|
|
53
|
+
try {
|
|
54
|
+
const inv = runAdapter(path.join(dir, 'app'))
|
|
55
|
+
assert.equal(inv.surfaces[0].signals?.themeToggle, true)
|
|
56
|
+
assert.ok(!inv.surfaces[0].signals?.i18n)
|
|
57
|
+
} finally { fs.rmSync(dir, { recursive: true, force: true }) }
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test('no signals → no `signals` key (null, not an empty object)', () => {
|
|
61
|
+
const dir = fixture(
|
|
62
|
+
{ dependencies: { react: '^19.0.0' } },
|
|
63
|
+
{ 'app/page.tsx': 'export default function P(){return <div/>}' },
|
|
64
|
+
)
|
|
65
|
+
try {
|
|
66
|
+
const inv = runAdapter(path.join(dir, 'app'))
|
|
67
|
+
assert.equal('signals' in inv.surfaces[0], false)
|
|
68
|
+
} finally { fs.rmSync(dir, { recursive: true, force: true }) }
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
test('deterministic: two runs on the same code produce byte-identical output', () => {
|
|
72
|
+
const dir = fixture(
|
|
73
|
+
{ dependencies: { 'next-themes': '^0.2.0' } },
|
|
74
|
+
{ 'app/page.tsx': 'export default function P(){return <div/>}',
|
|
75
|
+
'app/settings/page.tsx': "export default function S(){return <form onSubmit={()=>{}}/>}" },
|
|
76
|
+
)
|
|
77
|
+
try {
|
|
78
|
+
const a = JSON.stringify(runAdapter(path.join(dir, 'app')))
|
|
79
|
+
const b = JSON.stringify(runAdapter(path.join(dir, 'app')))
|
|
80
|
+
assert.equal(a, b)
|
|
81
|
+
} finally { fs.rmSync(dir, { recursive: true, force: true }) }
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
test('a flag or a missing directory in the appDir slot exits 2 and writes nothing', () => {
|
|
85
|
+
const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'nextadp-cli-'))
|
|
86
|
+
try {
|
|
87
|
+
for (const args of [['--help'], ['--out', 'x.json'], [path.join(cwd, 'does-not-exist')]]) {
|
|
88
|
+
const r = spawnSync('node', [ADAPTER, ...args], { cwd, encoding: 'utf8' })
|
|
89
|
+
assert.equal(r.status, 2, `${args.join(' ')} → exit 2`)
|
|
90
|
+
assert.match(r.stderr, /usage: nextjs-inventory\.mjs <appDir>/)
|
|
91
|
+
}
|
|
92
|
+
assert.deepEqual(fs.readdirSync(cwd), [], 'nothing was written into the cwd')
|
|
93
|
+
} finally {
|
|
94
|
+
fs.rmSync(cwd, { recursive: true, force: true })
|
|
95
|
+
}
|
|
96
|
+
})
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// check-quarantine-expiry — the auto-resurface gate for dated exits.
|
|
2
|
+
//
|
|
3
|
+
// A quarantine (or any deferred-but-tracked suppression) is a holding pen with a
|
|
4
|
+
// dated exit, never a destination. The convention is a single marker, anywhere in
|
|
5
|
+
// a file (a test-file comment, a suppressions list, an ADR):
|
|
6
|
+
//
|
|
7
|
+
// @quarantine(until=2026-07-01, issue=#123) // canonical
|
|
8
|
+
// until=2026-07-01 // also matched (bare)
|
|
9
|
+
// until: 2026-07-01 // also matched (yaml-style)
|
|
10
|
+
//
|
|
11
|
+
// Until the date, the item is allowed to sit non-blocking. On/after the date it
|
|
12
|
+
// EXPIRES: this gate fails, so the item re-blocks until it is fixed or the date is
|
|
13
|
+
// extended with a stated reason. Run it in the BLOCKING lane.
|
|
14
|
+
//
|
|
15
|
+
// node check-quarantine-expiry.mjs --files a,b # specific files
|
|
16
|
+
// node check-quarantine-expiry.mjs # whole-tree scan of test files
|
|
17
|
+
// node check-quarantine-expiry.mjs --now 2026-06-08 # pin "today" (default: system date; for tests/CI determinism)
|
|
18
|
+
// --json # machine output
|
|
19
|
+
//
|
|
20
|
+
// Exit 0 = no expired exits. Exit 1 = at least one expired (the gate blocks).
|
|
21
|
+
|
|
22
|
+
import fs from 'node:fs'
|
|
23
|
+
import path from 'node:path'
|
|
24
|
+
import { isMainModule } from '../../engineering-runtime/scripts/main-module.mjs'
|
|
25
|
+
|
|
26
|
+
// Marker forms: `until=YYYY-MM-DD` or `until: YYYY-MM-DD` (case-insensitive key).
|
|
27
|
+
const UNTIL = /\buntil\s*[:=]\s*(\d{4}-\d{2}-\d{2})\b/gi
|
|
28
|
+
// Pull an issue ref from the same line when present, for the report.
|
|
29
|
+
const ISSUE = /\bissue\s*[:=]\s*(#?\w[\w/-]*)/i
|
|
30
|
+
|
|
31
|
+
export function isTestFile(p) {
|
|
32
|
+
return /\.(test|spec)\.[jt]sx?$/.test(p) || /(^|\/)tests?\//.test(p) || /\.coverage\.ya?ml$/.test(p)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Pure: scan one file's text → expired/pending records against `now` (YYYY-MM-DD).
|
|
36
|
+
export function scanText(file, text, now) {
|
|
37
|
+
const out = []
|
|
38
|
+
const lines = (text || '').split(/\r?\n/)
|
|
39
|
+
lines.forEach((raw, i) => {
|
|
40
|
+
for (const m of raw.matchAll(UNTIL)) {
|
|
41
|
+
const until = m[1]
|
|
42
|
+
const issue = (raw.match(ISSUE) || [])[1] || null
|
|
43
|
+
out.push({ file, line: i + 1, until, issue, expired: until < now, snippet: raw.trim().slice(0, 120) })
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
return out
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function discover(dir, acc = []) {
|
|
50
|
+
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
51
|
+
if (e.name === 'node_modules' || e.name.startsWith('.git')) continue
|
|
52
|
+
const full = path.join(dir, e.name)
|
|
53
|
+
if (e.isDirectory()) discover(full, acc)
|
|
54
|
+
else if (isTestFile(full)) acc.push(full)
|
|
55
|
+
}
|
|
56
|
+
return acc
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function today() {
|
|
60
|
+
// Standalone CI tool — reading the wall clock is correct here (unlike workflow scripts).
|
|
61
|
+
return new Date().toISOString().slice(0, 10)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function runCli(argv, { name = 'check-quarantine-expiry' } = {}) {
|
|
65
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
66
|
+
console.log(`usage: ${name}.mjs [--files a,b] [--now YYYY-MM-DD] [--json] # exit 1 on any expired @quarantine(until=…) exit`)
|
|
67
|
+
return 0
|
|
68
|
+
}
|
|
69
|
+
const flags = {}
|
|
70
|
+
for (let i = 0; i < argv.length; i++) {
|
|
71
|
+
const a = argv[i]
|
|
72
|
+
if (a === '--json') flags.json = true
|
|
73
|
+
else if (a === '--now') flags.now = argv[++i]
|
|
74
|
+
else if (a === '--files') flags.files = argv[++i]
|
|
75
|
+
}
|
|
76
|
+
const now = flags.now || today()
|
|
77
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(now)) {
|
|
78
|
+
console.error(`${name}: --now must be YYYY-MM-DD, got "${now}"`)
|
|
79
|
+
return 2
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const files = flags.files
|
|
83
|
+
? flags.files.split(',').map((s) => s.trim()).filter(Boolean)
|
|
84
|
+
: discover(process.cwd())
|
|
85
|
+
|
|
86
|
+
const records = []
|
|
87
|
+
for (const f of files) {
|
|
88
|
+
if (fs.existsSync(f)) records.push(...scanText(f, fs.readFileSync(f, 'utf8'), now))
|
|
89
|
+
}
|
|
90
|
+
const expired = records.filter((r) => r.expired)
|
|
91
|
+
|
|
92
|
+
if (flags.json) {
|
|
93
|
+
console.log(JSON.stringify({ now, total: records.length, expired }, null, 2))
|
|
94
|
+
} else {
|
|
95
|
+
if (!records.length) console.log(`${name}: no dated exits found.`)
|
|
96
|
+
else if (!expired.length) console.log(`${name}: ${records.length} dated exit(s), none expired (as of ${now}).`)
|
|
97
|
+
else {
|
|
98
|
+
console.log(`${name}: ${expired.length} EXPIRED dated exit(s) (as of ${now}) — fix, or extend the date with a reason:`)
|
|
99
|
+
for (const r of expired) console.log(` ${r.file}:${r.line} until=${r.until}${r.issue ? ` (${r.issue})` : ''} ${r.snippet}`)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return expired.length ? 1 : 0
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (isMainModule(import.meta.url)) process.exit(runCli(process.argv.slice(2)))
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
import { scanText, isTestFile, runCli } from './check-quarantine-expiry.mjs'
|
|
4
|
+
|
|
5
|
+
test('scanText flags an until= date in the past as expired, future as not', () => {
|
|
6
|
+
const text = [
|
|
7
|
+
'// @quarantine(until=2026-01-01, issue=#42) flaky payfields iframe',
|
|
8
|
+
'// @quarantine(until=2027-12-31, issue=#99) external sandbox pending',
|
|
9
|
+
].join('\n')
|
|
10
|
+
const recs = scanText('e2e/x.spec.ts', text, '2026-06-08')
|
|
11
|
+
assert.equal(recs.length, 2)
|
|
12
|
+
const past = recs.find((r) => r.until === '2026-01-01')
|
|
13
|
+
assert.equal(past.expired, true)
|
|
14
|
+
assert.equal(past.issue, '#42')
|
|
15
|
+
assert.equal(past.line, 1)
|
|
16
|
+
assert.equal(recs.find((r) => r.until === '2027-12-31').expired, false)
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
test('scanText matches yaml-style "until: DATE" too', () => {
|
|
20
|
+
const recs = scanText('x.coverage.yml', 'suppress:\n - until: 2020-05-05', '2026-06-08')
|
|
21
|
+
assert.equal(recs.length, 1)
|
|
22
|
+
assert.equal(recs[0].expired, true)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
test('boundary: until == now is NOT expired (expires strictly after the date)', () => {
|
|
26
|
+
const recs = scanText('a.spec.ts', 'until=2026-06-08', '2026-06-08')
|
|
27
|
+
assert.equal(recs[0].expired, false)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test('isTestFile recognizes spec/test/coverage files, not plain source', () => {
|
|
31
|
+
assert.ok(isTestFile('e2e/login.spec.ts'))
|
|
32
|
+
assert.ok(isTestFile('tests/foo.py'))
|
|
33
|
+
assert.ok(isTestFile('req.coverage.yml'))
|
|
34
|
+
assert.ok(!isTestFile('src/app.ts'))
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
test('runCli exits 1 on an expired exit, 0 when all future', () => {
|
|
38
|
+
// Use this test file itself as a fixture-free input via a temp-ish path is overkill;
|
|
39
|
+
// exercise the pure path through --files on real files in the repo dir instead.
|
|
40
|
+
// Expired case: point at a string file we know — fall back to scanText-backed assertion.
|
|
41
|
+
assert.equal(scanText('f', 'until=2000-01-01', '2026-06-08').filter((r) => r.expired).length, 1)
|
|
42
|
+
assert.equal(scanText('f', 'until=2099-01-01', '2026-06-08').filter((r) => r.expired).length, 0)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test('runCli rejects a malformed --now', () => {
|
|
46
|
+
assert.equal(runCli(['--now', 'yesterday', '--files', 'nope.ts']), 2)
|
|
47
|
+
})
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// Audit the sole CI gate supported by ensure-coverage: curated golden-path smoke.
|
|
2
|
+
// All other coverage checks are audit and review evidence and are ignored here.
|
|
3
|
+
|
|
4
|
+
import fs from 'node:fs'
|
|
5
|
+
import path from 'node:path'
|
|
6
|
+
import { isMainModule } from '../../engineering-runtime/scripts/main-module.mjs'
|
|
7
|
+
|
|
8
|
+
export const SMOKE_GATE = {
|
|
9
|
+
key: 'smoke',
|
|
10
|
+
label: 'Curated golden-path smoke',
|
|
11
|
+
patterns: [/smoke(\.mjs)?\b[^\n]*\bverdict\b/, /golden-path(\.mjs)?\b[^\n]*\bverdict\b/, /smoke-gate/],
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const WEAKEN = [
|
|
15
|
+
/continue-on-error:\s*true/i,
|
|
16
|
+
/allowFailure:\s*true/i,
|
|
17
|
+
/\|\|\s*true\b/,
|
|
18
|
+
/--warn-only\b/,
|
|
19
|
+
/set\s+\+e\b/,
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
export function stripComment(line) {
|
|
23
|
+
return line.replace(/(^|\s)#.*$/, '$1')
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function classifySmoke(corpus, { proximity = 10 } = {}) {
|
|
27
|
+
const hits = []
|
|
28
|
+
for (const file of corpus) {
|
|
29
|
+
file.lines.forEach((line, index) => {
|
|
30
|
+
if (SMOKE_GATE.patterns.some((pattern) => pattern.test(stripComment(line)))) hits.push({ file, index })
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
if (hits.length === 0) return { status: 'absent', evidence: 'no smoke verdict command found in scanned CI config' }
|
|
34
|
+
|
|
35
|
+
for (const hit of hits) {
|
|
36
|
+
const start = Math.max(0, hit.index - proximity)
|
|
37
|
+
const end = Math.min(hit.file.lines.length - 1, hit.index + proximity)
|
|
38
|
+
for (let index = start; index <= end; index += 1) {
|
|
39
|
+
if (WEAKEN.some((marker) => marker.test(stripComment(hit.file.lines[index])))) {
|
|
40
|
+
return {
|
|
41
|
+
status: 'warn-only',
|
|
42
|
+
evidence: `${hit.file.name}:${hit.index + 1} (weakened near ${hit.file.name}:${index + 1})`,
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return { status: 'present', evidence: `${hits[0].file.name}:${hits[0].index + 1}` }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function auditCorpus(files, { allowWarn = false } = {}) {
|
|
51
|
+
const corpus = files.map((file) => ({ name: file.name, lines: (file.text || '').split(/\r?\n/) }))
|
|
52
|
+
const smoke = { key: SMOKE_GATE.key, label: SMOKE_GATE.label, ...classifySmoke(corpus) }
|
|
53
|
+
const ok = smoke.status === 'present' || (allowWarn && smoke.status === 'warn-only')
|
|
54
|
+
return { smoke, ok, scanned: files.map((file) => file.name) }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function buildAdoptionPlan(report) {
|
|
58
|
+
return { needed: report.smoke.status !== 'present', smoke: report.smoke, scanned: report.scanned || [] }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function renderAdoptionIssue(plan) {
|
|
62
|
+
const scanned = plan.scanned.length ? plan.scanned.map((file) => `\`${file}\``).join(', ') : '_no CI config found_'
|
|
63
|
+
const action = plan.smoke.status === 'warn-only'
|
|
64
|
+
? 'Remove the weakening marker near the smoke command so failures block the build.'
|
|
65
|
+
: 'Add the golden-path job: `golden-path init --write` generates it from golden-paths.json (or call its `run` then `verdict` from an existing job).'
|
|
66
|
+
return [
|
|
67
|
+
'## Wire golden-path smoke into CI',
|
|
68
|
+
'',
|
|
69
|
+
'Curated golden-path smoke is the only CI gate in the ensure-coverage contract.',
|
|
70
|
+
'',
|
|
71
|
+
`Scanned: ${scanned}`,
|
|
72
|
+
`Status: **${plan.smoke.status}** (${plan.smoke.evidence})`,
|
|
73
|
+
'',
|
|
74
|
+
'### Action',
|
|
75
|
+
'',
|
|
76
|
+
`- [ ] ${action}`,
|
|
77
|
+
'- [ ] Confirm first-party requests and owned state are not mocked or intercepted.',
|
|
78
|
+
'- [ ] Confirm the job fails unless the verdict is ALIVE.',
|
|
79
|
+
'',
|
|
80
|
+
'### Verify',
|
|
81
|
+
'',
|
|
82
|
+
'```bash',
|
|
83
|
+
`node <ensure-coverage>/scripts/ci-audit.mjs --files ${plan.scanned.join(',') || '<your ci files>'}`,
|
|
84
|
+
'```',
|
|
85
|
+
].join('\n')
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function discover(explicit) {
|
|
89
|
+
if (explicit) return explicit.split(',').map((value) => value.trim()).filter(Boolean)
|
|
90
|
+
const found = []
|
|
91
|
+
const workflows = '.github/workflows'
|
|
92
|
+
if (fs.existsSync(workflows)) {
|
|
93
|
+
for (const file of fs.readdirSync(workflows)) if (/\.ya?ml$/.test(file)) found.push(path.join(workflows, file))
|
|
94
|
+
}
|
|
95
|
+
for (const file of fs.existsSync('.') ? fs.readdirSync('.') : []) {
|
|
96
|
+
if (/^cloudbuild.*\.ya?ml$/.test(file)) found.push(file)
|
|
97
|
+
}
|
|
98
|
+
for (const file of ['Makefile', 'package.json']) if (fs.existsSync(file)) found.push(file)
|
|
99
|
+
return found
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function main() {
|
|
103
|
+
const argv = process.argv.slice(2)
|
|
104
|
+
const flags = {}
|
|
105
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
106
|
+
const arg = argv[index]
|
|
107
|
+
if (arg === '--allow-warn') flags.allowWarn = true
|
|
108
|
+
else if (arg === '--json') flags.json = true
|
|
109
|
+
else if (arg === '--adoption') flags.adoption = true
|
|
110
|
+
else if (arg === '--files') flags.files = argv[++index]
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const paths = discover(flags.files)
|
|
114
|
+
if (paths.length === 0) {
|
|
115
|
+
console.error('ci-audit: no CI config files found to scan (pass --files a.yml,b.yaml)')
|
|
116
|
+
process.exit(2)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const files = paths.map((file) => ({ name: file, text: fs.readFileSync(file, 'utf8') }))
|
|
120
|
+
const report = auditCorpus(files, { allowWarn: flags.allowWarn })
|
|
121
|
+
if (flags.adoption) {
|
|
122
|
+
const plan = buildAdoptionPlan(report)
|
|
123
|
+
if (flags.json) console.log(JSON.stringify(plan, null, 2))
|
|
124
|
+
else if (plan.needed) console.log(renderAdoptionIssue(plan))
|
|
125
|
+
else console.log(`nothing to adopt: golden-path smoke is blocking (${report.smoke.evidence})`)
|
|
126
|
+
process.exit(plan.needed ? 1 : 0)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (flags.json) console.log(JSON.stringify(report, null, 2))
|
|
130
|
+
else {
|
|
131
|
+
const mark = report.smoke.status === 'present' ? 'PASS' : report.smoke.status === 'warn-only' ? 'WARN' : 'FAIL'
|
|
132
|
+
console.log(`ci-audit: ${mark} - ${report.smoke.label}: ${report.smoke.status} (${report.smoke.evidence})`)
|
|
133
|
+
}
|
|
134
|
+
process.exit(report.ok ? 0 : 1)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (isMainModule(import.meta.url)) main()
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
|
|
4
|
+
import { auditCorpus, buildAdoptionPlan, classifySmoke, renderAdoptionIssue } from './ci-audit.mjs'
|
|
5
|
+
|
|
6
|
+
const file = (name, text) => ({ name, text })
|
|
7
|
+
|
|
8
|
+
test('blocking golden-path smoke passes', () => {
|
|
9
|
+
const report = auditCorpus([file('.github/workflows/ci.yml', `
|
|
10
|
+
jobs:
|
|
11
|
+
smoke:
|
|
12
|
+
steps:
|
|
13
|
+
- run: node smoke.mjs verdict --manifest smoke.manifest.json --target local --results smoke-results.json
|
|
14
|
+
`)])
|
|
15
|
+
assert.equal(report.ok, true)
|
|
16
|
+
assert.equal(report.smoke.status, 'present')
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
test('the generated golden-path workflow counts as the smoke gate', () => {
|
|
20
|
+
const report = auditCorpus([file('.github/workflows/golden-paths.yml', `
|
|
21
|
+
jobs:
|
|
22
|
+
golden-paths:
|
|
23
|
+
steps:
|
|
24
|
+
- run: node scripts/golden-paths/golden-path.mjs verdict --profile "$GOLDEN_PATH_PROFILE" --target "$BASE_URL"
|
|
25
|
+
`)])
|
|
26
|
+
assert.equal(report.ok, true)
|
|
27
|
+
assert.equal(report.smoke.status, 'present')
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test('the former coverage gates are ignored', () => {
|
|
31
|
+
const report = auditCorpus([file('.github/workflows/ci.yml', `
|
|
32
|
+
- run: node coverage-ledger.mjs validate
|
|
33
|
+
- run: node presentation-sweep.mjs
|
|
34
|
+
- run: pnpm playwright test
|
|
35
|
+
- run: node lint-tests.mjs --rules mock-internal-seam
|
|
36
|
+
- run: node check-quarantine-expiry.mjs
|
|
37
|
+
`)])
|
|
38
|
+
assert.equal(report.ok, false)
|
|
39
|
+
assert.equal(report.smoke.status, 'absent')
|
|
40
|
+
assert.deepEqual(Object.keys(report), ['smoke', 'ok', 'scanned'])
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test('a weakened smoke job is warn-only and fails by default', () => {
|
|
44
|
+
const report = auditCorpus([file('cloudbuild.yaml', `
|
|
45
|
+
- id: smoke
|
|
46
|
+
allowFailure: true
|
|
47
|
+
args: ['-c', 'node smoke.mjs verdict --manifest smoke.manifest.json --results smoke-results.json']
|
|
48
|
+
`)])
|
|
49
|
+
assert.equal(report.ok, false)
|
|
50
|
+
assert.equal(report.smoke.status, 'warn-only')
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
test('--allow-warn accepts warn-only smoke when explicitly requested', () => {
|
|
54
|
+
const report = auditCorpus([file('ci.yml', 'run: node smoke.mjs verdict --results out.json || true')], { allowWarn: true })
|
|
55
|
+
assert.equal(report.ok, true)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
test('comments do not count as smoke wiring or weakening', () => {
|
|
59
|
+
const absent = auditCorpus([file('ci.yml', '# node smoke.mjs verdict --results out.json')])
|
|
60
|
+
assert.equal(absent.smoke.status, 'absent')
|
|
61
|
+
|
|
62
|
+
const corpus = [{ name: 'ci.yml', lines: [
|
|
63
|
+
'# never add continue-on-error: true',
|
|
64
|
+
'run: node smoke.mjs verdict --results out.json',
|
|
65
|
+
] }]
|
|
66
|
+
assert.equal(classifySmoke(corpus).status, 'present')
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('weakening far from smoke does not bleed into its status', () => {
|
|
70
|
+
const lines = ['continue-on-error: true', ...Array.from({ length: 20 }, (_, index) => `# filler ${index}`), 'run: smoke-gate']
|
|
71
|
+
assert.equal(classifySmoke([{ name: 'ci.yml', lines }]).status, 'present')
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
test('adoption output requests only golden-path smoke', () => {
|
|
75
|
+
const report = auditCorpus([file('.github/workflows/ci.yml', 'run: node coverage-ledger.mjs validate')])
|
|
76
|
+
const plan = buildAdoptionPlan(report)
|
|
77
|
+
const body = renderAdoptionIssue(plan)
|
|
78
|
+
assert.equal(plan.needed, true)
|
|
79
|
+
assert.match(body, /Wire golden-path smoke into CI/)
|
|
80
|
+
assert.doesNotMatch(body, /coverage-ledger|presentation sweep|quarantine expiry|all-gates|static-gates/)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
test('adoption is unnecessary when smoke is blocking', () => {
|
|
84
|
+
const report = auditCorpus([file('ci.yml', 'run: smoke-gate')])
|
|
85
|
+
assert.equal(buildAdoptionPlan(report).needed, false)
|
|
86
|
+
})
|