@codyswann/lisa 4.54.13 → 4.55.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/all/copy-contents/.gitattributes +8 -0
- package/all/copy-overwrite/scripts/check-orphaned-branches.mjs +78 -9
- package/all/copy-overwrite/scripts/check-third-party-action-pins.mjs +17 -1
- package/all/copy-overwrite/scripts/lib/process-tree-runner.mjs +194 -7
- package/all/copy-overwrite/scripts/lisa-enforcement-fallback.sh +31 -0
- package/all/copy-overwrite/scripts/lisa-hooks/block-blind-automerge.sh +81 -2
- package/all/copy-overwrite/scripts/lisa-hooks/block-direct-issue-create.sh +293 -8
- package/all/copy-overwrite/scripts/lisa-hooks/block-instruction-file-edits.sh +21 -0
- package/all/copy-overwrite/scripts/lisa-hooks/block-managed-file-edits.sh +78 -15
- package/all/copy-overwrite/scripts/lisa-hooks/block-no-verify.sh +21 -0
- package/all/copy-overwrite/scripts/lisa-hooks/block-shell-json-parsing.sh +21 -0
- package/all/copy-overwrite/scripts/lisa-hooks/guard-dedupe.bash +340 -0
- package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +29 -1
- package/all/copy-overwrite/scripts/lisa-hooks/worktree-binding-guard.mjs +183 -1
- package/all/copy-overwrite/scripts/lisa-hooks/worktree-binding-guard.sh +21 -0
- package/all/copy-overwrite/scripts/lisa-postinstall.mjs +21 -1
- package/all/copy-overwrite/scripts/lisa-run-gates.mjs +100 -5
- package/all/copy-overwrite/scripts/lisa-work-item.mjs +1085 -80
- package/all/create-only/.github/workflows/continuous-gates.yml +7 -0
- package/cdk/create-only/.github/workflows/ci.yml +7 -0
- package/cdk/create-only/.github/workflows/deploy.yml +21 -0
- package/dist/cli/apply.d.ts.map +1 -1
- package/dist/cli/apply.js +9 -1
- package/dist/cli/apply.js.map +1 -1
- package/dist/cli/doctor-apply-deletions.d.ts +61 -0
- package/dist/cli/doctor-apply-deletions.d.ts.map +1 -0
- package/dist/cli/doctor-apply-deletions.js +137 -0
- package/dist/cli/doctor-apply-deletions.js.map +1 -0
- package/dist/cli/doctor-cdk-preset-adoption.d.ts +15 -0
- package/dist/cli/doctor-cdk-preset-adoption.d.ts.map +1 -0
- package/dist/cli/doctor-cdk-preset-adoption.js +316 -0
- package/dist/cli/doctor-cdk-preset-adoption.js.map +1 -0
- package/dist/cli/doctor-config-shadowing.d.ts +22 -0
- package/dist/cli/doctor-config-shadowing.d.ts.map +1 -0
- package/dist/cli/doctor-config-shadowing.js +123 -0
- package/dist/cli/doctor-config-shadowing.js.map +1 -0
- package/dist/cli/doctor-rails-deploy-intent.d.ts +15 -0
- package/dist/cli/doctor-rails-deploy-intent.d.ts.map +1 -0
- package/dist/cli/doctor-rails-deploy-intent.js +197 -0
- package/dist/cli/doctor-rails-deploy-intent.js.map +1 -0
- package/dist/cli/doctor-seeded-artifacts.d.ts +12 -0
- package/dist/cli/doctor-seeded-artifacts.d.ts.map +1 -0
- package/dist/cli/doctor-seeded-artifacts.js +54 -0
- package/dist/cli/doctor-seeded-artifacts.js.map +1 -0
- package/dist/cli/doctor-stale-banner-scan.d.ts +44 -0
- package/dist/cli/doctor-stale-banner-scan.d.ts.map +1 -0
- package/dist/cli/doctor-stale-banner-scan.js +152 -0
- package/dist/cli/doctor-stale-banner-scan.js.map +1 -0
- package/dist/cli/doctor-stale-managed-banner.d.ts +19 -0
- package/dist/cli/doctor-stale-managed-banner.d.ts.map +1 -0
- package/dist/cli/doctor-stale-managed-banner.js +198 -0
- package/dist/cli/doctor-stale-managed-banner.js.map +1 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +16 -0
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/ui-detected-stacks.d.ts.map +1 -1
- package/dist/cli/ui-detected-stacks.js +3 -2
- package/dist/cli/ui-detected-stacks.js.map +1 -1
- package/dist/cli/update-check.d.ts.map +1 -1
- package/dist/cli/update-check.js +14 -0
- package/dist/cli/update-check.js.map +1 -1
- package/dist/cli/worktree-liveness.d.ts +5 -0
- package/dist/cli/worktree-liveness.d.ts.map +1 -1
- package/dist/cli/worktree-liveness.js +7 -6
- package/dist/cli/worktree-liveness.js.map +1 -1
- package/dist/cli/worktree-ownership.d.ts +13 -0
- package/dist/cli/worktree-ownership.d.ts.map +1 -1
- package/dist/cli/worktree-ownership.js +14 -0
- package/dist/cli/worktree-ownership.js.map +1 -1
- package/dist/configs/eslint/base.d.ts +34 -0
- package/dist/configs/eslint/base.d.ts.map +1 -1
- package/dist/configs/eslint/base.js +34 -0
- package/dist/configs/eslint/base.js.map +1 -1
- package/dist/core/apply-receipt.d.ts +65 -0
- package/dist/core/apply-receipt.d.ts.map +1 -1
- package/dist/core/apply-receipt.js +62 -2
- package/dist/core/apply-receipt.js.map +1 -1
- package/dist/core/cdk-preset-adoption.d.ts +136 -0
- package/dist/core/cdk-preset-adoption.d.ts.map +1 -0
- package/dist/core/cdk-preset-adoption.js +135 -0
- package/dist/core/cdk-preset-adoption.js.map +1 -0
- package/dist/core/config-shadowing.d.ts +36 -0
- package/dist/core/config-shadowing.d.ts.map +1 -1
- package/dist/core/config-shadowing.js +38 -0
- package/dist/core/config-shadowing.js.map +1 -1
- package/dist/core/config.d.ts +11 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js.map +1 -1
- package/dist/core/learnings-merge-driver.d.ts +10 -0
- package/dist/core/learnings-merge-driver.d.ts.map +1 -1
- package/dist/core/learnings-merge-driver.js +18 -0
- package/dist/core/learnings-merge-driver.js.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +136 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/lisa.d.ts.map +1 -1
- package/dist/core/lisa.js +9 -1
- package/dist/core/lisa.js.map +1 -1
- package/dist/core/nightly-e2e-guard-behavior-certificate.d.ts +5 -5
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +7 -7
- package/dist/core/nightly-e2e-guard-behavior-certificate.js.map +1 -1
- package/dist/core/ownership-header.d.ts +74 -0
- package/dist/core/ownership-header.d.ts.map +1 -0
- package/dist/core/ownership-header.js +76 -0
- package/dist/core/ownership-header.js.map +1 -0
- package/dist/core/rails-deploy-production-intent.d.ts +36 -0
- package/dist/core/rails-deploy-production-intent.d.ts.map +1 -0
- package/dist/core/rails-deploy-production-intent.js +79 -0
- package/dist/core/rails-deploy-production-intent.js.map +1 -0
- package/dist/core/stale-managed-banner.d.ts +78 -0
- package/dist/core/stale-managed-banner.d.ts.map +1 -0
- package/dist/core/stale-managed-banner.js +118 -0
- package/dist/core/stale-managed-banner.js.map +1 -0
- package/dist/core/two-channel-delivery-scan.d.ts +33 -1
- package/dist/core/two-channel-delivery-scan.d.ts.map +1 -1
- package/dist/core/two-channel-delivery-scan.js +133 -6
- package/dist/core/two-channel-delivery-scan.js.map +1 -1
- package/dist/core/two-channel-delivery.d.ts +101 -4
- package/dist/core/two-channel-delivery.d.ts.map +1 -1
- package/dist/core/two-channel-delivery.js +121 -13
- package/dist/core/two-channel-delivery.js.map +1 -1
- package/dist/core/two-channel-staleness.d.ts +137 -0
- package/dist/core/two-channel-staleness.d.ts.map +1 -0
- package/dist/core/two-channel-staleness.js +268 -0
- package/dist/core/two-channel-staleness.js.map +1 -0
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +265 -121
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/core/workflow-deletion-ownership.d.ts +6 -24
- package/dist/core/workflow-deletion-ownership.d.ts.map +1 -1
- package/dist/core/workflow-deletion-ownership.js +22 -42
- package/dist/core/workflow-deletion-ownership.js.map +1 -1
- package/dist/detection/detectors/cdk.d.ts.map +1 -1
- package/dist/detection/detectors/cdk.js +2 -1
- package/dist/detection/detectors/cdk.js.map +1 -1
- package/dist/health/template-inspection.d.ts.map +1 -1
- package/dist/health/template-inspection.js +2 -1
- package/dist/health/template-inspection.js.map +1 -1
- package/dist/migrations/ensure-lisa-postinstall.d.ts +16 -0
- package/dist/migrations/ensure-lisa-postinstall.d.ts.map +1 -1
- package/dist/migrations/ensure-lisa-postinstall.js +16 -0
- package/dist/migrations/ensure-lisa-postinstall.js.map +1 -1
- package/dist/migrations/ensure-pinned-reusable-workflow-refs.d.ts +2 -2
- package/dist/migrations/ensure-pinned-reusable-workflow-refs.d.ts.map +1 -1
- package/dist/migrations/ensure-pinned-reusable-workflow-refs.js +98 -34
- package/dist/migrations/ensure-pinned-reusable-workflow-refs.js.map +1 -1
- package/dist/migrations/ensure-playwright-dedicated-caller.d.ts.map +1 -1
- package/dist/migrations/ensure-playwright-dedicated-caller.js +1 -0
- package/dist/migrations/ensure-playwright-dedicated-caller.js.map +1 -1
- package/dist/opencode/hooks-installer.d.ts.map +1 -1
- package/dist/opencode/hooks-installer.js +1 -0
- package/dist/opencode/hooks-installer.js.map +1 -1
- package/dist/opencode/plugin-templates/block-managed-file-edits.sh +78 -15
- package/dist/opencode/plugin-templates/block-no-verify.sh +21 -0
- package/dist/opencode/plugin-templates/guard-dedupe.bash +337 -0
- package/dist/opencode/plugin-templates/lisa-block-direct-issue-create.ts +242 -55
- package/dist/opencode/plugin-templates/parity-safety-net.sh +29 -1
- package/dist/strategies/package-lisa.d.ts.map +1 -1
- package/dist/strategies/package-lisa.js +2 -1
- package/dist/strategies/package-lisa.js.map +1 -1
- package/dist/utils/path-utils.d.ts +29 -0
- package/dist/utils/path-utils.d.ts.map +1 -1
- package/dist/utils/path-utils.js +41 -0
- package/dist/utils/path-utils.js.map +1 -1
- package/expo/create-only/.github/workflows/ci.yml +21 -0
- package/expo/create-only/.github/workflows/deploy.yml +28 -0
- package/expo/create-only/.github/workflows/maestro-e2e.yml +7 -0
- package/expo/create-only/.github/workflows/nightly-e2e-bypass-reaper.yml +124 -13
- package/expo/create-only/.github/workflows/nightly-e2e-health.yml +7 -0
- package/expo/create-only/.github/workflows/nightly-e2e-report.yml +7 -0
- package/expo/create-only/.github/workflows/nightly-e2e-tracking.yml +8 -0
- package/expo/create-only/.github/workflows/playwright-e2e.yml +7 -0
- package/harper-fabric/copy-overwrite/.github/workflows/ci.yml +7 -0
- package/harper-fabric/create-only/.github/workflows/deploy.yml +14 -0
- package/nestjs/create-only/.github/workflows/ci.yml +14 -0
- package/nestjs/create-only/.github/workflows/deploy.yml +28 -0
- package/package.json +10 -5
- package/phaser/copy-overwrite/.github/workflows/ci.yml +7 -0
- package/plugins/lisa/.claude-plugin/plugin.json +28 -1
- package/plugins/lisa/.codex-plugin/hooks.json +27 -0
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
- package/plugins/lisa/.codex-plugin/skills/lisa-github-build-intake/SKILL.md +27 -4
- package/plugins/lisa/.codex-plugin/skills/lisa-github-prd-intake/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-github-validate-issue/SKILL.md +22 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-github-verify/SKILL.md +14 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-github-write-issue/SKILL.md +51 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-intake/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-build-intake/SKILL.md +27 -4
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-verify/SKILL.md +14 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-write-ticket/SKILL.md +51 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-access/SKILL.md +55 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-build-intake/SKILL.md +27 -4
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-prd-intake/SKILL.md +47 -30
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-to-tracker/SKILL.md +3 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-validate-issue/SKILL.md +22 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-verify/SKILL.md +14 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-write-issue/SKILL.md +54 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-qa-clear/SKILL.md +19 -7
- package/plugins/lisa/.codex-plugin/skills/lisa-qa-fail/SKILL.md +28 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-qa-queue/SKILL.md +28 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-repair-intake/SKILL.md +94 -24
- package/plugins/lisa/.codex-plugin/skills/lisa-rework-triage/SKILL.md +32 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-linear/SKILL.md +4 -4
- package/plugins/lisa/.codex-plugin/skills/lisa-track/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-tracker-build-intake/SKILL.md +9 -0
- package/plugins/lisa/agents/linear-prd-intake.md +8 -8
- package/plugins/lisa/hooks/block-blind-automerge.sh +81 -2
- package/plugins/lisa/hooks/block-direct-issue-create.agy.sh +64 -16
- package/plugins/lisa/hooks/block-direct-issue-create.sh +293 -8
- package/plugins/lisa/hooks/block-instruction-file-edits.sh +21 -0
- package/plugins/lisa/hooks/block-managed-file-edits.sh +78 -15
- package/plugins/lisa/hooks/block-no-verify.sh +21 -0
- package/plugins/lisa/hooks/block-shell-json-parsing.sh +21 -0
- package/plugins/lisa/hooks/failure-signature-index.mjs +240 -5
- package/plugins/lisa/hooks/guard-dedupe.bash +337 -0
- package/plugins/lisa/hooks/inject-rules.sh +13 -5
- package/plugins/lisa/hooks/operational-hazards.mjs +736 -0
- package/plugins/lisa/hooks/operational-hazards.sh +28 -0
- package/plugins/lisa/hooks/parity-safety-net.sh +29 -1
- package/plugins/lisa/hooks/threshold-ratchet-compare.mjs +102 -21
- package/plugins/lisa/hooks/threshold-ratchet-families.mjs +130 -2
- package/plugins/lisa/hooks/threshold-ratchet.mjs +63 -13
- package/plugins/lisa/hooks/worktree-binding-guard.mjs +183 -1
- package/plugins/lisa/hooks/worktree-binding-guard.sh +21 -0
- package/plugins/lisa/rules/eager/00-rule-index.md +1 -0
- package/plugins/lisa/rules/eager/operational-hazards.md +50 -0
- package/plugins/lisa/rules/eager/tracked-work.md +1 -1
- package/plugins/lisa/rules/reference/config-resolution.md +1 -1
- package/plugins/lisa/rules/reference/leaf-only-lifecycle.md +1 -1
- package/plugins/lisa/rules/reference/operational-hazards.md +115 -0
- package/plugins/lisa/rules/reference/work-item-trailer-definition.md +129 -0
- package/plugins/lisa/scripts/intake-blocker-reprobe.mjs +407 -26
- package/plugins/lisa/scripts/qa-signal-lifecycle.mjs +507 -0
- package/plugins/lisa/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
- package/plugins/lisa/skills/lisa-github-build-intake/SKILL.md +27 -4
- package/plugins/lisa/skills/lisa-github-prd-intake/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-github-validate-issue/SKILL.md +22 -0
- package/plugins/lisa/skills/lisa-github-verify/SKILL.md +14 -0
- package/plugins/lisa/skills/lisa-github-write-issue/SKILL.md +51 -0
- package/plugins/lisa/skills/lisa-intake/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-jira-build-intake/SKILL.md +27 -4
- package/plugins/lisa/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
- package/plugins/lisa/skills/lisa-jira-verify/SKILL.md +14 -0
- package/plugins/lisa/skills/lisa-jira-write-ticket/SKILL.md +51 -0
- package/plugins/lisa/skills/lisa-linear-access/SKILL.md +55 -2
- package/plugins/lisa/skills/lisa-linear-build-intake/SKILL.md +27 -4
- package/plugins/lisa/skills/lisa-linear-prd-intake/SKILL.md +48 -31
- package/plugins/lisa/skills/lisa-linear-to-tracker/SKILL.md +3 -3
- package/plugins/lisa/skills/lisa-linear-validate-issue/SKILL.md +22 -0
- package/plugins/lisa/skills/lisa-linear-verify/SKILL.md +14 -0
- package/plugins/lisa/skills/lisa-linear-write-issue/SKILL.md +54 -3
- package/plugins/lisa/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
- package/plugins/lisa/skills/lisa-qa-clear/SKILL.md +19 -7
- package/plugins/lisa/skills/lisa-qa-fail/SKILL.md +28 -3
- package/plugins/lisa/skills/lisa-qa-queue/SKILL.md +28 -3
- package/plugins/lisa/skills/lisa-repair-intake/SKILL.md +94 -24
- package/plugins/lisa/skills/lisa-rework-triage/SKILL.md +32 -2
- package/plugins/lisa/skills/lisa-setup-linear/SKILL.md +5 -5
- package/plugins/lisa/skills/lisa-track/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-tracker-build-intake/SKILL.md +9 -0
- package/plugins/lisa-agy/agents/linear-prd-intake.md +8 -8
- package/plugins/lisa-agy/hooks/block-blind-automerge.sh +81 -2
- package/plugins/lisa-agy/hooks/block-direct-issue-create.agy.sh +64 -16
- package/plugins/lisa-agy/hooks/block-direct-issue-create.sh +293 -8
- package/plugins/lisa-agy/hooks/block-instruction-file-edits.sh +21 -0
- package/plugins/lisa-agy/hooks/block-managed-file-edits.sh +78 -15
- package/plugins/lisa-agy/hooks/block-shell-json-parsing.sh +21 -0
- package/plugins/lisa-agy/hooks/guard-dedupe.bash +337 -0
- package/plugins/lisa-agy/hooks/parity-safety-net.sh +29 -1
- package/plugins/lisa-agy/hooks.json +1 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/scripts/intake-blocker-reprobe.mjs +407 -26
- package/plugins/lisa-agy/scripts/qa-signal-lifecycle.mjs +507 -0
- package/plugins/lisa-agy/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
- package/plugins/lisa-agy/skills/lisa-github-build-intake/SKILL.md +27 -4
- package/plugins/lisa-agy/skills/lisa-github-prd-intake/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-github-validate-issue/SKILL.md +22 -0
- package/plugins/lisa-agy/skills/lisa-github-verify/SKILL.md +14 -0
- package/plugins/lisa-agy/skills/lisa-github-write-issue/SKILL.md +51 -0
- package/plugins/lisa-agy/skills/lisa-intake/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-jira-build-intake/SKILL.md +27 -4
- package/plugins/lisa-agy/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
- package/plugins/lisa-agy/skills/lisa-jira-verify/SKILL.md +14 -0
- package/plugins/lisa-agy/skills/lisa-jira-write-ticket/SKILL.md +51 -0
- package/plugins/lisa-agy/skills/lisa-linear-access/SKILL.md +55 -2
- package/plugins/lisa-agy/skills/lisa-linear-build-intake/SKILL.md +27 -4
- package/plugins/lisa-agy/skills/lisa-linear-prd-intake/SKILL.md +48 -31
- package/plugins/lisa-agy/skills/lisa-linear-to-tracker/SKILL.md +3 -3
- package/plugins/lisa-agy/skills/lisa-linear-validate-issue/SKILL.md +22 -0
- package/plugins/lisa-agy/skills/lisa-linear-verify/SKILL.md +14 -0
- package/plugins/lisa-agy/skills/lisa-linear-write-issue/SKILL.md +54 -3
- package/plugins/lisa-agy/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
- package/plugins/lisa-agy/skills/lisa-qa-clear/SKILL.md +19 -7
- package/plugins/lisa-agy/skills/lisa-qa-fail/SKILL.md +28 -3
- package/plugins/lisa-agy/skills/lisa-qa-queue/SKILL.md +28 -3
- package/plugins/lisa-agy/skills/lisa-repair-intake/SKILL.md +94 -24
- package/plugins/lisa-agy/skills/lisa-rework-triage/SKILL.md +32 -2
- package/plugins/lisa-agy/skills/lisa-setup-linear/SKILL.md +5 -5
- package/plugins/lisa-agy/skills/lisa-track/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-tracker-build-intake/SKILL.md +9 -0
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +19 -1
- package/plugins/lisa-copilot/agents/linear-prd-intake.agent.md +8 -8
- package/plugins/lisa-copilot/hooks/block-blind-automerge.sh +81 -2
- package/plugins/lisa-copilot/hooks/block-direct-issue-create.sh +293 -8
- package/plugins/lisa-copilot/hooks/block-instruction-file-edits.sh +21 -0
- package/plugins/lisa-copilot/hooks/block-managed-file-edits.sh +78 -15
- package/plugins/lisa-copilot/hooks/block-no-verify.sh +21 -0
- package/plugins/lisa-copilot/hooks/block-shell-json-parsing.sh +21 -0
- package/plugins/lisa-copilot/hooks/failure-signature-index.mjs +240 -5
- package/plugins/lisa-copilot/hooks/guard-dedupe.bash +337 -0
- package/plugins/lisa-copilot/hooks/inject-rules.sh +13 -5
- package/plugins/lisa-copilot/hooks/operational-hazards.mjs +736 -0
- package/plugins/lisa-copilot/hooks/operational-hazards.sh +28 -0
- package/plugins/lisa-copilot/hooks/parity-safety-net.sh +29 -1
- package/plugins/lisa-copilot/hooks/threshold-ratchet-compare.mjs +102 -21
- package/plugins/lisa-copilot/hooks/threshold-ratchet-families.mjs +130 -2
- package/plugins/lisa-copilot/hooks/threshold-ratchet.mjs +63 -13
- package/plugins/lisa-copilot/hooks/worktree-binding-guard.mjs +183 -1
- package/plugins/lisa-copilot/hooks/worktree-binding-guard.sh +21 -0
- package/plugins/lisa-copilot/rules/eager/00-rule-index.md +1 -0
- package/plugins/lisa-copilot/rules/eager/operational-hazards.md +50 -0
- package/plugins/lisa-copilot/rules/eager/tracked-work.md +1 -1
- package/plugins/lisa-copilot/rules/reference/config-resolution.md +1 -1
- package/plugins/lisa-copilot/rules/reference/leaf-only-lifecycle.md +1 -1
- package/plugins/lisa-copilot/rules/reference/operational-hazards.md +115 -0
- package/plugins/lisa-copilot/rules/reference/work-item-trailer-definition.md +129 -0
- package/plugins/lisa-copilot/scripts/intake-blocker-reprobe.mjs +407 -26
- package/plugins/lisa-copilot/scripts/qa-signal-lifecycle.mjs +507 -0
- package/plugins/lisa-copilot/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
- package/plugins/lisa-copilot/skills/lisa-github-build-intake/SKILL.md +27 -4
- package/plugins/lisa-copilot/skills/lisa-github-prd-intake/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-github-validate-issue/SKILL.md +22 -0
- package/plugins/lisa-copilot/skills/lisa-github-verify/SKILL.md +14 -0
- package/plugins/lisa-copilot/skills/lisa-github-write-issue/SKILL.md +51 -0
- package/plugins/lisa-copilot/skills/lisa-intake/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-jira-build-intake/SKILL.md +27 -4
- package/plugins/lisa-copilot/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
- package/plugins/lisa-copilot/skills/lisa-jira-verify/SKILL.md +14 -0
- package/plugins/lisa-copilot/skills/lisa-jira-write-ticket/SKILL.md +51 -0
- package/plugins/lisa-copilot/skills/lisa-linear-access/SKILL.md +55 -2
- package/plugins/lisa-copilot/skills/lisa-linear-build-intake/SKILL.md +27 -4
- package/plugins/lisa-copilot/skills/lisa-linear-prd-intake/SKILL.md +48 -31
- package/plugins/lisa-copilot/skills/lisa-linear-to-tracker/SKILL.md +3 -3
- package/plugins/lisa-copilot/skills/lisa-linear-validate-issue/SKILL.md +22 -0
- package/plugins/lisa-copilot/skills/lisa-linear-verify/SKILL.md +14 -0
- package/plugins/lisa-copilot/skills/lisa-linear-write-issue/SKILL.md +54 -3
- package/plugins/lisa-copilot/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
- package/plugins/lisa-copilot/skills/lisa-qa-clear/SKILL.md +19 -7
- package/plugins/lisa-copilot/skills/lisa-qa-fail/SKILL.md +28 -3
- package/plugins/lisa-copilot/skills/lisa-qa-queue/SKILL.md +28 -3
- package/plugins/lisa-copilot/skills/lisa-repair-intake/SKILL.md +94 -24
- package/plugins/lisa-copilot/skills/lisa-rework-triage/SKILL.md +32 -2
- package/plugins/lisa-copilot/skills/lisa-setup-linear/SKILL.md +5 -5
- package/plugins/lisa-copilot/skills/lisa-track/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-tracker-build-intake/SKILL.md +9 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/agents/linear-prd-intake.md +8 -8
- package/plugins/lisa-cursor/hooks/block-blind-automerge.sh +81 -2
- package/plugins/lisa-cursor/hooks/block-direct-issue-create.sh +293 -8
- package/plugins/lisa-cursor/hooks/block-instruction-file-edits.sh +21 -0
- package/plugins/lisa-cursor/hooks/block-managed-file-edits.sh +78 -15
- package/plugins/lisa-cursor/hooks/block-no-verify.sh +21 -0
- package/plugins/lisa-cursor/hooks/block-shell-json-parsing.sh +21 -0
- package/plugins/lisa-cursor/hooks/failure-signature-index.mjs +240 -5
- package/plugins/lisa-cursor/hooks/guard-dedupe.bash +337 -0
- package/plugins/lisa-cursor/hooks/hooks.json +10 -0
- package/plugins/lisa-cursor/hooks/operational-hazards.mjs +736 -0
- package/plugins/lisa-cursor/hooks/operational-hazards.sh +28 -0
- package/plugins/lisa-cursor/hooks/parity-safety-net.sh +29 -1
- package/plugins/lisa-cursor/hooks/threshold-ratchet-compare.mjs +102 -21
- package/plugins/lisa-cursor/hooks/threshold-ratchet-families.mjs +130 -2
- package/plugins/lisa-cursor/hooks/threshold-ratchet.mjs +63 -13
- package/plugins/lisa-cursor/hooks/worktree-binding-guard.mjs +183 -1
- package/plugins/lisa-cursor/hooks/worktree-binding-guard.sh +21 -0
- package/plugins/lisa-cursor/rules/00-rule-index.mdc +1 -0
- package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/leaf-only-lifecycle-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/operational-hazards-reference.mdc +120 -0
- package/plugins/lisa-cursor/rules/operational-hazards.mdc +55 -0
- package/plugins/lisa-cursor/rules/tracked-work.mdc +1 -1
- package/plugins/lisa-cursor/rules/work-item-trailer-definition-reference.mdc +134 -0
- package/plugins/lisa-cursor/scripts/intake-blocker-reprobe.mjs +407 -26
- package/plugins/lisa-cursor/scripts/qa-signal-lifecycle.mjs +507 -0
- package/plugins/lisa-cursor/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
- package/plugins/lisa-cursor/skills/lisa-github-build-intake/SKILL.md +27 -4
- package/plugins/lisa-cursor/skills/lisa-github-prd-intake/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-github-validate-issue/SKILL.md +22 -0
- package/plugins/lisa-cursor/skills/lisa-github-verify/SKILL.md +14 -0
- package/plugins/lisa-cursor/skills/lisa-github-write-issue/SKILL.md +51 -0
- package/plugins/lisa-cursor/skills/lisa-intake/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-jira-build-intake/SKILL.md +27 -4
- package/plugins/lisa-cursor/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
- package/plugins/lisa-cursor/skills/lisa-jira-verify/SKILL.md +14 -0
- package/plugins/lisa-cursor/skills/lisa-jira-write-ticket/SKILL.md +51 -0
- package/plugins/lisa-cursor/skills/lisa-linear-access/SKILL.md +55 -2
- package/plugins/lisa-cursor/skills/lisa-linear-build-intake/SKILL.md +27 -4
- package/plugins/lisa-cursor/skills/lisa-linear-prd-intake/SKILL.md +48 -31
- package/plugins/lisa-cursor/skills/lisa-linear-to-tracker/SKILL.md +3 -3
- package/plugins/lisa-cursor/skills/lisa-linear-validate-issue/SKILL.md +22 -0
- package/plugins/lisa-cursor/skills/lisa-linear-verify/SKILL.md +14 -0
- package/plugins/lisa-cursor/skills/lisa-linear-write-issue/SKILL.md +54 -3
- package/plugins/lisa-cursor/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
- package/plugins/lisa-cursor/skills/lisa-qa-clear/SKILL.md +19 -7
- package/plugins/lisa-cursor/skills/lisa-qa-fail/SKILL.md +28 -3
- package/plugins/lisa-cursor/skills/lisa-qa-queue/SKILL.md +28 -3
- package/plugins/lisa-cursor/skills/lisa-repair-intake/SKILL.md +94 -24
- package/plugins/lisa-cursor/skills/lisa-rework-triage/SKILL.md +32 -2
- package/plugins/lisa-cursor/skills/lisa-setup-linear/SKILL.md +5 -5
- package/plugins/lisa-cursor/skills/lisa-track/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-tracker-build-intake/SKILL.md +9 -0
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/hooks/inject-rules.sh +14 -3
- package/plugins/lisa-harper-fabric/rules/eager/harper-fabric.md +31 -0
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/hooks/inject-rules.sh +14 -3
- package/plugins/lisa-harper-fabric-copilot/rules/eager/harper-fabric.md +31 -0
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/rules/harper-fabric-reference.mdc +57 -0
- package/plugins/lisa-harper-fabric-cursor/rules/harper-fabric.mdc +32 -53
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/hooks/inject-rules.sh +14 -3
- package/plugins/lisa-phaser/rules/eager/phaser.md +44 -0
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/hooks/inject-rules.sh +14 -3
- package/plugins/lisa-phaser-copilot/rules/eager/phaser.md +44 -0
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/rules/phaser-reference.mdc +189 -0
- package/plugins/lisa-phaser-cursor/rules/phaser.mdc +45 -185
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/hooks/inject-rules.sh +14 -4
- package/plugins/lisa-rails/rules/eager/rails-conventions.md +23 -0
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/hooks/inject-rules.sh +14 -4
- package/plugins/lisa-rails-copilot/rules/eager/rails-conventions.md +23 -0
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/rules/rails-conventions-reference.mdc +181 -0
- package/plugins/lisa-rails-cursor/rules/rails-conventions.mdc +24 -177
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/materialized-artifacts.json +1 -0
- package/plugins/src/base/.claude-plugin/plugin.json +27 -0
- package/plugins/src/base/agents/linear-prd-intake.md +8 -8
- package/plugins/src/base/hooks/block-blind-automerge.sh +81 -2
- package/plugins/src/base/hooks/block-direct-issue-create.agy.sh +64 -16
- package/plugins/src/base/hooks/block-direct-issue-create.sh +293 -8
- package/plugins/src/base/hooks/block-instruction-file-edits.sh +21 -0
- package/plugins/src/base/hooks/block-managed-file-edits.sh +78 -15
- package/plugins/src/base/hooks/block-no-verify.sh +21 -0
- package/plugins/src/base/hooks/block-shell-json-parsing.sh +21 -0
- package/plugins/src/base/hooks/failure-signature-index.mjs +240 -5
- package/plugins/src/base/hooks/guard-dedupe.bash +337 -0
- package/plugins/src/base/hooks/inject-rules.sh +13 -5
- package/plugins/src/base/hooks/operational-hazards.mjs +736 -0
- package/plugins/src/base/hooks/operational-hazards.sh +28 -0
- package/plugins/src/base/hooks/parity-safety-net.sh +29 -1
- package/plugins/src/base/hooks/threshold-ratchet-compare.mjs +102 -21
- package/plugins/src/base/hooks/threshold-ratchet-families.mjs +130 -2
- package/plugins/src/base/hooks/threshold-ratchet.mjs +63 -13
- package/plugins/src/base/hooks/worktree-binding-guard.mjs +183 -1
- package/plugins/src/base/hooks/worktree-binding-guard.sh +21 -0
- package/plugins/src/base/rules/eager/00-rule-index.md +1 -0
- package/plugins/src/base/rules/eager/operational-hazards.md +50 -0
- package/plugins/src/base/rules/eager/tracked-work.md +1 -1
- package/plugins/src/base/rules/reference/config-resolution.md +1 -1
- package/plugins/src/base/rules/reference/leaf-only-lifecycle.md +1 -1
- package/plugins/src/base/rules/reference/operational-hazards.md +115 -0
- package/plugins/src/base/rules/reference/work-item-trailer-definition.md +129 -0
- package/plugins/src/base/scripts/intake-blocker-reprobe.mjs +407 -26
- package/plugins/src/base/scripts/qa-signal-lifecycle.mjs +507 -0
- package/plugins/src/base/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
- package/plugins/src/base/skills/lisa-github-build-intake/SKILL.md +27 -4
- package/plugins/src/base/skills/lisa-github-prd-intake/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-github-validate-issue/SKILL.md +22 -0
- package/plugins/src/base/skills/lisa-github-verify/SKILL.md +14 -0
- package/plugins/src/base/skills/lisa-github-write-issue/SKILL.md +51 -0
- package/plugins/src/base/skills/lisa-intake/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-jira-build-intake/SKILL.md +27 -4
- package/plugins/src/base/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
- package/plugins/src/base/skills/lisa-jira-verify/SKILL.md +14 -0
- package/plugins/src/base/skills/lisa-jira-write-ticket/SKILL.md +51 -0
- package/plugins/src/base/skills/lisa-linear-access/SKILL.md +55 -2
- package/plugins/src/base/skills/lisa-linear-build-intake/SKILL.md +27 -4
- package/plugins/src/base/skills/lisa-linear-prd-intake/SKILL.md +48 -31
- package/plugins/src/base/skills/lisa-linear-to-tracker/SKILL.md +3 -3
- package/plugins/src/base/skills/lisa-linear-validate-issue/SKILL.md +22 -0
- package/plugins/src/base/skills/lisa-linear-verify/SKILL.md +14 -0
- package/plugins/src/base/skills/lisa-linear-write-issue/SKILL.md +54 -3
- package/plugins/src/base/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
- package/plugins/src/base/skills/lisa-qa-clear/SKILL.md +19 -7
- package/plugins/src/base/skills/lisa-qa-fail/SKILL.md +28 -3
- package/plugins/src/base/skills/lisa-qa-queue/SKILL.md +28 -3
- package/plugins/src/base/skills/lisa-repair-intake/SKILL.md +94 -24
- package/plugins/src/base/skills/lisa-rework-triage/SKILL.md +32 -2
- package/plugins/src/base/skills/lisa-setup-linear/SKILL.md +5 -5
- package/plugins/src/base/skills/lisa-track/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-tracker-build-intake/SKILL.md +9 -0
- package/plugins/src/harper-fabric/hooks/inject-rules.sh +14 -3
- package/plugins/src/harper-fabric/rules/eager/harper-fabric.md +31 -0
- package/plugins/src/phaser/hooks/inject-rules.sh +14 -3
- package/plugins/src/phaser/rules/eager/phaser.md +44 -0
- package/plugins/src/rails/hooks/inject-rules.sh +14 -4
- package/plugins/src/rails/rules/eager/rails-conventions.md +23 -0
- package/rails/copy-overwrite/scripts/check-threshold-ratchet.mjs +63 -13
- package/rails/copy-overwrite/scripts/threshold-ratchet-compare.mjs +102 -21
- package/rails/copy-overwrite/scripts/threshold-ratchet-families.mjs +130 -2
- package/rails/create-only/.github/workflows/ci.yml +8 -0
- package/rails/create-only/.github/workflows/deploy.yml +22 -0
- package/scripts/build-plugins.sh +2 -1
- package/scripts/check-engine-floor.mjs +361 -0
- package/scripts/check-guard-parity-notes.mjs +709 -0
- package/scripts/check-template-workflow-refs.mjs +9 -0
- package/scripts/check-third-party-action-pins.mjs +17 -1
- package/scripts/check-workflow-contract-assertions.mjs +480 -0
- package/scripts/copy-opencode-plugin-templates.mjs +1 -0
- package/scripts/deployed-guard-advice.mjs +636 -0
- package/scripts/generate-agy-plugin-artifacts.mjs +35 -10
- package/scripts/generate-export-surface.mjs +105 -19
- package/scripts/generate-two-channel-couplings.ts +143 -212
- package/scripts/lisa-enforcement-fallback.sh +31 -0
- package/scripts/measure-scripts-profile-gap.mts +361 -0
- package/scripts/mutation-performance-measure.mjs +21 -5
- package/scripts/reconcile-release-tags.mjs +65 -84
- package/scripts/two-channel-couplings.json +302 -70
- package/scripts/workflow-contract-assertion.sh +47 -0
- package/typescript/copy-overwrite/.lintstagedrc.json +1 -0
- package/typescript/copy-overwrite/eslint.ignore.config.json +1 -0
- package/typescript/copy-overwrite/scripts/check-nightly-e2e-health.mjs +527 -53
- package/typescript/copy-overwrite/scripts/check-skipped-required-checks.mjs +301 -13
- package/typescript/copy-overwrite/scripts/check-threshold-ratchet.mjs +63 -13
- package/typescript/copy-overwrite/scripts/threshold-ratchet-compare.mjs +102 -21
- package/typescript/copy-overwrite/scripts/threshold-ratchet-families.mjs +130 -2
- package/typescript/create-only/.github/workflows/ci.yml +7 -0
- /package/plugins/lisa-harper-fabric/rules/{harper-fabric.md → reference/harper-fabric.md} +0 -0
- /package/plugins/lisa-harper-fabric-copilot/rules/{harper-fabric.md → reference/harper-fabric.md} +0 -0
- /package/plugins/lisa-phaser/rules/{phaser.md → reference/phaser.md} +0 -0
- /package/plugins/lisa-phaser-copilot/rules/{phaser.md → reference/phaser.md} +0 -0
- /package/plugins/lisa-rails/rules/{rails-conventions.md → reference/rails-conventions.md} +0 -0
- /package/plugins/lisa-rails-copilot/rules/{rails-conventions.md → reference/rails-conventions.md} +0 -0
- /package/plugins/src/harper-fabric/rules/{harper-fabric.md → reference/harper-fabric.md} +0 -0
- /package/plugins/src/phaser/rules/{phaser.md → reference/phaser.md} +0 -0
- /package/plugins/src/rails/rules/{rails-conventions.md → reference/rails-conventions.md} +0 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Measure how much weaker the `scripts/` rule profile is than the profile the
|
|
3
|
+
* same file would meet anywhere else, and what that difference costs in
|
|
4
|
+
* findings.
|
|
5
|
+
*
|
|
6
|
+
* WHY THIS EXISTS. `getScriptsFilesOverride()` in `src/configs/eslint/base.ts`
|
|
7
|
+
* relaxes a set of rules for every path matching `scripts/**` or
|
|
8
|
+
* `**\/scripts/**`. Each relaxation is argued in that file and several are
|
|
9
|
+
* plainly right. What nobody could state was the SIZE of the thing: a file
|
|
10
|
+
* written in `scripts/` can carry a defect of a class the wider profile catches
|
|
11
|
+
* and never be told, because in `scripts/` it is not a violation
|
|
12
|
+
* (CodySwannGT/lisa#3773). That is a different failure from a control that did
|
|
13
|
+
* not fire — the control ran, was correct, and its SCOPE excluded the file.
|
|
14
|
+
* The worked example was a `sonarjs/slow-regex` finding that sat unreported in
|
|
15
|
+
* `scripts/` from the day the file was written until the day it was moved into
|
|
16
|
+
* a shipped tree, where a stricter assertion saw it immediately.
|
|
17
|
+
*
|
|
18
|
+
* THIS REPORTS, IT DOES NOT GATE. The exit status is 0 whenever the measurement
|
|
19
|
+
* was made, whatever it found. Aligning the profiles is a policy decision with
|
|
20
|
+
* a large blast radius, and this exists to put a number in front of that
|
|
21
|
+
* decision rather than to take it. The established local shape is
|
|
22
|
+
* `check-engine-floor.mjs`, which prints its strictness backlog and gates on
|
|
23
|
+
* something else entirely.
|
|
24
|
+
*
|
|
25
|
+
* THE DIVERGENT SET IS DERIVED, NEVER LISTED. A hardcoded copy of the
|
|
26
|
+
* override's rule list is a second source of truth that goes stale the first
|
|
27
|
+
* time somebody edits the override. So the set is computed by asking ESLint
|
|
28
|
+
* itself, twice, for the config it resolves for the SAME file extension in two
|
|
29
|
+
* locations — one inside `scripts/`, one outside it — and taking the severity
|
|
30
|
+
* difference. Add a suppression to the override and this measurement grows a
|
|
31
|
+
* row with nobody remembering to update it; delete one and the row disappears.
|
|
32
|
+
*
|
|
33
|
+
* IT ASKS THE REPOSITORY'S OWN CONFIG, not a reconstruction of it. `eslint.
|
|
34
|
+
* config.ts` is imported and used as-is, so `eslint.config.local.ts` — which
|
|
35
|
+
* re-disables one rule for `scripts/**` that a project-local block had silently
|
|
36
|
+
* re-enabled — is part of the answer. A reconstruction assembled from the
|
|
37
|
+
* factory would have missed that, and missing it is the same class of error the
|
|
38
|
+
* measurement is about.
|
|
39
|
+
*
|
|
40
|
+
* WHY THE NODE API RATHER THAN THE `eslint` CLI. Measured on a developer
|
|
41
|
+
* checkout carrying nested worktrees, `npx eslint --format json <90 files>`
|
|
42
|
+
* exhausts an 8 GB heap and returns nothing, because `tsconfig.eslint.json`
|
|
43
|
+
* includes `**\/*.ts` and the typed program grows without bound. The in-process
|
|
44
|
+
* API over the same file set completes. A measurement that cannot run where the
|
|
45
|
+
* code lives is not a measurement, so the slower, working route is the right
|
|
46
|
+
* one; expect several minutes.
|
|
47
|
+
*
|
|
48
|
+
* THE SUBJECT IS THE GIT INDEX, not the disk, for the reason
|
|
49
|
+
* `tests/helpers/shipped-mjs-roster.ts` records at length: untracked scratch
|
|
50
|
+
* files under `scripts/` belong to whoever made them and to no commit, and
|
|
51
|
+
* counting them makes one agent's working tree change another agent's number.
|
|
52
|
+
*
|
|
53
|
+
* IT REFUSES RATHER THAN REPORTING A ZERO IT DID NOT EARN. A config that
|
|
54
|
+
* resolved no rules and an empty subject set both produce "no findings", which
|
|
55
|
+
* is the same shape as a clean tree. Each exits non-zero with the denial first,
|
|
56
|
+
* per `scripts/lib/dependency-tree.mjs`.
|
|
57
|
+
*
|
|
58
|
+
* `--root` exists so the suite can point this at a fixture and prove the
|
|
59
|
+
* derivation follows the config rather than a constant in this file.
|
|
60
|
+
* @module scripts/measure-scripts-profile-gap
|
|
61
|
+
*/
|
|
62
|
+
import { execFileSync } from "node:child_process";
|
|
63
|
+
import { existsSync } from "node:fs";
|
|
64
|
+
import * as path from "node:path";
|
|
65
|
+
import process from "node:process";
|
|
66
|
+
import { fileURLToPath } from "node:url";
|
|
67
|
+
|
|
68
|
+
import { ESLint } from "eslint";
|
|
69
|
+
|
|
70
|
+
import { cannotMeasure } from "./lib/dependency-tree.mjs";
|
|
71
|
+
import { invokedAsScript } from "./lib/invoked-as-script.mjs";
|
|
72
|
+
|
|
73
|
+
const DEFAULT_ROOT = path.resolve(
|
|
74
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
75
|
+
".."
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
/** How this measurement names itself in its report and in its refusals. */
|
|
79
|
+
const REPORT = "scripts-profile-gap";
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The outside-`scripts/` probe path whose resolved config is differenced.
|
|
83
|
+
*
|
|
84
|
+
* Both probes are `.mjs` so that the `**\/*.js` / `**\/*.mjs` override applies
|
|
85
|
+
* to each and cancels: what survives the difference is the `scripts/` override
|
|
86
|
+
* alone, not the JavaScript-versus-TypeScript profile. Neither file exists, and
|
|
87
|
+
* neither needs to — ESLint answers for a path.
|
|
88
|
+
*/
|
|
89
|
+
const PROBE_OUTSIDE = "__scripts-profile-gap-probe__.mjs";
|
|
90
|
+
|
|
91
|
+
/** The inside-`scripts/` half of {@link PROBE_OUTSIDE}. */
|
|
92
|
+
const PROBE_INSIDE = "scripts/__scripts-profile-gap-probe__.mjs";
|
|
93
|
+
|
|
94
|
+
/** Extensions ESLint has an opinion about in this tree. */
|
|
95
|
+
const LINTABLE: readonly string[] = [".mjs", ".mts", ".ts", ".js"];
|
|
96
|
+
|
|
97
|
+
/** Severity names, indexed by the numeric form ESLint resolves rules to. */
|
|
98
|
+
const SEVERITY: readonly string[] = ["off", "warn", "error"];
|
|
99
|
+
|
|
100
|
+
/** One rule whose severity differs between the two probe locations. */
|
|
101
|
+
export type Divergence = {
|
|
102
|
+
readonly rule: string;
|
|
103
|
+
readonly outside: string;
|
|
104
|
+
readonly inside: string;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/** The two-directional difference between the probes, and what was compared. */
|
|
108
|
+
export type Divergences = {
|
|
109
|
+
readonly compared: number;
|
|
110
|
+
readonly weaker: readonly Divergence[];
|
|
111
|
+
readonly stricter: readonly Divergence[];
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Normalise one rule entry to its severity name.
|
|
116
|
+
* @param entry - A rule value: a severity, or `[severity, ...options]`.
|
|
117
|
+
* @returns `"off"`, `"warn"`, or `"error"`.
|
|
118
|
+
*/
|
|
119
|
+
export function severityOf(entry: unknown): string {
|
|
120
|
+
const head = Array.isArray(entry) ? entry[0] : entry;
|
|
121
|
+
if (typeof head === "number") return SEVERITY[head] ?? "off";
|
|
122
|
+
return typeof head === "string" && SEVERITY.includes(head) ? head : "off";
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The rules whose severity differs between two resolved configs.
|
|
127
|
+
*
|
|
128
|
+
* Reported in BOTH directions on purpose. "`scripts/` is weaker" is the claim
|
|
129
|
+
* under examination, and a measurement that could only ever confirm it would be
|
|
130
|
+
* worth nothing. This repository has at least one rule the `scripts/` profile
|
|
131
|
+
* promotes ABOVE the surrounding code, and a report unable to show that is a
|
|
132
|
+
* report nobody should trust about the other direction either.
|
|
133
|
+
* @param outside - Rules resolved for a path outside `scripts/`.
|
|
134
|
+
* @param inside - Rules resolved for the same extension inside `scripts/`.
|
|
135
|
+
* @returns The difference, each side sorted by rule name.
|
|
136
|
+
*/
|
|
137
|
+
export function divergentRules(
|
|
138
|
+
outside: Readonly<Record<string, unknown>>,
|
|
139
|
+
inside: Readonly<Record<string, unknown>>
|
|
140
|
+
): Divergences {
|
|
141
|
+
const names = [...new Set([...Object.keys(outside), ...Object.keys(inside)])];
|
|
142
|
+
const rows = names
|
|
143
|
+
.map(rule => ({
|
|
144
|
+
rule,
|
|
145
|
+
outside: severityOf(outside[rule]),
|
|
146
|
+
inside: severityOf(inside[rule]),
|
|
147
|
+
}))
|
|
148
|
+
.filter(row => row.outside !== row.inside)
|
|
149
|
+
.sort((left, right) => (left.rule < right.rule ? -1 : 1));
|
|
150
|
+
const shift = (row: Divergence): number =>
|
|
151
|
+
SEVERITY.indexOf(row.inside) - SEVERITY.indexOf(row.outside);
|
|
152
|
+
return {
|
|
153
|
+
compared: names.length,
|
|
154
|
+
weaker: rows.filter(row => shift(row) < 0),
|
|
155
|
+
stricter: rows.filter(row => shift(row) > 0),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* The lintable files git tracks under `scripts/`, that also exist on disk.
|
|
161
|
+
*
|
|
162
|
+
* A path staged for deletion is still listed by `git ls-files`, and handing
|
|
163
|
+
* ESLint a path with no file behind it turns a clean tree into an error about
|
|
164
|
+
* the harness.
|
|
165
|
+
* @param root - Repository root.
|
|
166
|
+
* @returns Repo-relative paths.
|
|
167
|
+
*/
|
|
168
|
+
export function trackedScripts(root: string): readonly string[] {
|
|
169
|
+
const listed = execFileSync("git", ["ls-files", "scripts"], {
|
|
170
|
+
cwd: root,
|
|
171
|
+
encoding: "utf-8",
|
|
172
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
173
|
+
});
|
|
174
|
+
return listed
|
|
175
|
+
.split("\n")
|
|
176
|
+
.map(line => line.trim())
|
|
177
|
+
.filter(line => LINTABLE.includes(path.extname(line)))
|
|
178
|
+
.filter(line => existsSync(path.join(root, line)));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** A finding tally, by rule and by file. */
|
|
182
|
+
export type Tally = {
|
|
183
|
+
readonly byRule: ReadonlyMap<string, number>;
|
|
184
|
+
readonly touched: ReadonlySet<string>;
|
|
185
|
+
readonly total: number;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Count findings attributable to a restored rule set.
|
|
190
|
+
*
|
|
191
|
+
* Only messages whose rule is in {@link restored} are counted. Anything else
|
|
192
|
+
* ESLint reports is the tree's existing state under the profile as it stands,
|
|
193
|
+
* which is not what this measures.
|
|
194
|
+
* @param root - Repository root.
|
|
195
|
+
* @param results - ESLint results over the subject files.
|
|
196
|
+
* @param restored - The rule ids that were re-armed.
|
|
197
|
+
* @returns The tally.
|
|
198
|
+
*/
|
|
199
|
+
export function tally(
|
|
200
|
+
root: string,
|
|
201
|
+
results: readonly ESLint.LintResult[],
|
|
202
|
+
restored: ReadonlySet<string>
|
|
203
|
+
): Tally {
|
|
204
|
+
const byRule = new Map<string, number>();
|
|
205
|
+
const touched = new Set<string>();
|
|
206
|
+
for (const result of results) {
|
|
207
|
+
for (const message of result.messages) {
|
|
208
|
+
const id = message.ruleId ?? "";
|
|
209
|
+
if (!restored.has(id)) continue;
|
|
210
|
+
byRule.set(id, (byRule.get(id) ?? 0) + 1);
|
|
211
|
+
touched.add(path.relative(root, result.filePath));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
let total = 0;
|
|
215
|
+
for (const count of byRule.values()) total += count;
|
|
216
|
+
return { byRule, touched, total };
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Build the refusal for a measurement that could not be made.
|
|
221
|
+
* @param because - Why the measurement did not happen.
|
|
222
|
+
* @param remedy - What the reader should do.
|
|
223
|
+
* @returns The four-clause refusal text.
|
|
224
|
+
*/
|
|
225
|
+
function refusal(because: string, remedy: string): string {
|
|
226
|
+
return cannotMeasure({
|
|
227
|
+
gate: REPORT,
|
|
228
|
+
denial: "that the scripts profile costs nothing",
|
|
229
|
+
because,
|
|
230
|
+
remedy,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Print the report and return an exit status.
|
|
236
|
+
* @param root - Repository root.
|
|
237
|
+
* @returns `0` when the measurement was made, `1` when it was not.
|
|
238
|
+
*/
|
|
239
|
+
export async function main(root: string): Promise<number> {
|
|
240
|
+
const { default: config } = (await import(
|
|
241
|
+
path.join(root, "eslint.config.ts")
|
|
242
|
+
)) as { default: unknown };
|
|
243
|
+
const eslint = new ESLint({
|
|
244
|
+
cwd: root,
|
|
245
|
+
overrideConfigFile: true,
|
|
246
|
+
overrideConfig: config as ESLint.Options["overrideConfig"],
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
const outside = await eslint.calculateConfigForFile(
|
|
250
|
+
path.join(root, PROBE_OUTSIDE)
|
|
251
|
+
);
|
|
252
|
+
const inside = await eslint.calculateConfigForFile(
|
|
253
|
+
path.join(root, PROBE_INSIDE)
|
|
254
|
+
);
|
|
255
|
+
const { weaker, stricter, compared } = divergentRules(
|
|
256
|
+
(outside?.rules ?? {}) as Record<string, unknown>,
|
|
257
|
+
(inside?.rules ?? {}) as Record<string, unknown>
|
|
258
|
+
);
|
|
259
|
+
if (compared === 0) {
|
|
260
|
+
console.error(
|
|
261
|
+
refusal(
|
|
262
|
+
"both probe paths resolved to zero rules, which is the same shape as " +
|
|
263
|
+
"two identical profiles and is not evidence of one.",
|
|
264
|
+
"Check that the ESLint config loads at all before reading this report."
|
|
265
|
+
)
|
|
266
|
+
);
|
|
267
|
+
return 1;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const subject = trackedScripts(root);
|
|
271
|
+
if (subject.length === 0) {
|
|
272
|
+
console.error(
|
|
273
|
+
refusal(
|
|
274
|
+
"the git index lists no lintable file under `scripts/`, so a finding " +
|
|
275
|
+
"count of zero would be an empty subject rather than a clean tree.",
|
|
276
|
+
"Run this from a checkout whose index carries the `scripts/` tree."
|
|
277
|
+
)
|
|
278
|
+
);
|
|
279
|
+
return 1;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
console.log(
|
|
283
|
+
`${REPORT}: ${compared} rule(s) resolved for both probes. ` +
|
|
284
|
+
`${weaker.length} are WEAKER inside scripts/, ${stricter.length} ` +
|
|
285
|
+
`STRICTER. Subject: ${subject.length} tracked lintable file(s).`
|
|
286
|
+
);
|
|
287
|
+
for (const row of stricter) {
|
|
288
|
+
console.log(` stricter ${row.rule}: ${row.outside} → ${row.inside}`);
|
|
289
|
+
}
|
|
290
|
+
if (weaker.length === 0) {
|
|
291
|
+
console.log(
|
|
292
|
+
"✅ No rule is weaker inside scripts/ than outside it — the two " +
|
|
293
|
+
"profiles carry the same bar."
|
|
294
|
+
);
|
|
295
|
+
return 0;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const restored = new Set(weaker.map(row => row.rule));
|
|
299
|
+
const armed = new ESLint({
|
|
300
|
+
cwd: root,
|
|
301
|
+
overrideConfigFile: true,
|
|
302
|
+
overrideConfig: [
|
|
303
|
+
...(config as readonly unknown[]),
|
|
304
|
+
{
|
|
305
|
+
files: ["**/*"],
|
|
306
|
+
rules: Object.fromEntries(weaker.map(row => [row.rule, row.outside])),
|
|
307
|
+
},
|
|
308
|
+
] as ESLint.Options["overrideConfig"],
|
|
309
|
+
});
|
|
310
|
+
const found = tally(root, await armed.lintFiles([...subject]), restored);
|
|
311
|
+
|
|
312
|
+
console.log("");
|
|
313
|
+
console.log(
|
|
314
|
+
`Measured backlog (NOT gated): restoring all ${weaker.length} weaker ` +
|
|
315
|
+
`rule(s) reports ${found.total} finding(s) across ` +
|
|
316
|
+
`${found.touched.size} of ${subject.length} file(s).`
|
|
317
|
+
);
|
|
318
|
+
const rows = [...weaker].sort(
|
|
319
|
+
(left, right) =>
|
|
320
|
+
(found.byRule.get(right.rule) ?? 0) - (found.byRule.get(left.rule) ?? 0)
|
|
321
|
+
);
|
|
322
|
+
for (const row of rows) {
|
|
323
|
+
const count = found.byRule.get(row.rule) ?? 0;
|
|
324
|
+
console.log(
|
|
325
|
+
` ${count}\t${row.rule} (${row.inside} here, ${row.outside} elsewhere)`
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
console.log("");
|
|
329
|
+
console.log(
|
|
330
|
+
"A rule reporting 0 is a suppression this tree does not currently need. " +
|
|
331
|
+
"A rule reporting more than 0"
|
|
332
|
+
);
|
|
333
|
+
console.log(
|
|
334
|
+
"is a class of finding that exists in `scripts/` and is not reported " +
|
|
335
|
+
"there. Neither is a failure; see"
|
|
336
|
+
);
|
|
337
|
+
console.log(
|
|
338
|
+
"CodySwannGT/lisa#3773 for the decision these numbers exist to inform."
|
|
339
|
+
);
|
|
340
|
+
return 0;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Resolve the root from `--root <dir>`, defaulting to the repository.
|
|
345
|
+
* @param argv - Arguments after the script name.
|
|
346
|
+
* @returns The absolute root.
|
|
347
|
+
*/
|
|
348
|
+
export function rootFrom(argv: readonly string[]): string {
|
|
349
|
+
const flag = argv.indexOf("--root");
|
|
350
|
+
if (flag === -1) return DEFAULT_ROOT;
|
|
351
|
+
const value = argv[flag + 1];
|
|
352
|
+
if (value === undefined) {
|
|
353
|
+
console.error(`${REPORT}: --root requires a value`);
|
|
354
|
+
process.exit(2);
|
|
355
|
+
}
|
|
356
|
+
return path.resolve(value);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (invokedAsScript(import.meta.url)) {
|
|
360
|
+
process.exit(await main(rootFrom(process.argv.slice(2))));
|
|
361
|
+
}
|
|
@@ -181,6 +181,25 @@ export function overlayMeasurementReporters(source, reporterPath, reportPath) {
|
|
|
181
181
|
};
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
+
/**
|
|
185
|
+
* Whether a resolved path is a root itself or sits beneath it.
|
|
186
|
+
*
|
|
187
|
+
* The separator is the whole point (CodySwannGT/lisa#3808): a bare
|
|
188
|
+
* `candidate.startsWith(root)` answers "inside" for `<root>-sibling`, because
|
|
189
|
+
* the sibling's name string-prefixes the root's. That is the fail-OPEN
|
|
190
|
+
* direction for a containment guard, and this function is used where the
|
|
191
|
+
* answer decides whether an escape is permitted.
|
|
192
|
+
*
|
|
193
|
+
* Both arguments must already be absolute and resolved; this is a boundary
|
|
194
|
+
* comparison, not a normaliser.
|
|
195
|
+
* @param {string} root - Absolute, resolved containing directory
|
|
196
|
+
* @param {string} candidate - Absolute, resolved path being tested
|
|
197
|
+
* @returns {boolean} True when the candidate is the root or lives beneath it
|
|
198
|
+
*/
|
|
199
|
+
function isWithinRoot(root, candidate) {
|
|
200
|
+
return candidate === root || candidate.startsWith(`${root}${path.sep}`);
|
|
201
|
+
}
|
|
202
|
+
|
|
184
203
|
/** Resolve an artifact path beneath its root and refuse links/escapes. */
|
|
185
204
|
export function resolveArtifactPath(root, relative) {
|
|
186
205
|
if (!relative || path.isAbsolute(relative)) {
|
|
@@ -188,16 +207,13 @@ export function resolveArtifactPath(root, relative) {
|
|
|
188
207
|
}
|
|
189
208
|
const resolvedRoot = path.resolve(root);
|
|
190
209
|
const resolved = path.resolve(resolvedRoot, relative);
|
|
191
|
-
if (
|
|
192
|
-
resolved !== resolvedRoot &&
|
|
193
|
-
!resolved.startsWith(`${resolvedRoot}${path.sep}`)
|
|
194
|
-
) {
|
|
210
|
+
if (!isWithinRoot(resolvedRoot, resolved)) {
|
|
195
211
|
throw new Error("artifact path is outside its root");
|
|
196
212
|
}
|
|
197
213
|
const parent = path.dirname(resolved);
|
|
198
214
|
if (
|
|
199
215
|
existsSync(parent) &&
|
|
200
|
-
!realpathSync(
|
|
216
|
+
!isWithinRoot(realpathSync(resolvedRoot), realpathSync(parent))
|
|
201
217
|
) {
|
|
202
218
|
throw new Error("artifact parent resolves outside its root");
|
|
203
219
|
}
|
|
@@ -71,9 +71,28 @@
|
|
|
71
71
|
* no evidence and return `unprovable` — the verdict that means "do not act".
|
|
72
72
|
* Use `attempts/<n>/jobs` or `?filter=all`.
|
|
73
73
|
*
|
|
74
|
+
* ## One probe, not two (issue #3804)
|
|
75
|
+
*
|
|
76
|
+
* The question "did this exact version reach the registry?" has ONE
|
|
77
|
+
* implementation, `verifyPublish` in `check-npm-publish-landed.mjs`, and this
|
|
78
|
+
* sweep calls it. It used to carry its own URL builder, its own fetch and its
|
|
79
|
+
* own body check, written in parallel with that module because the module was
|
|
80
|
+
* unmerged at the time. Two of them agreed only because two people had aligned
|
|
81
|
+
* the semantics by hand — including one real divergence caught in review, where
|
|
82
|
+
* this file accepted any HTTP 200 while the module also required the response
|
|
83
|
+
* body to name the version asked for. Both surfaces reporting `published` about
|
|
84
|
+
* one release have to mean the same thing by it, and that now rests on there
|
|
85
|
+
* being one implementation rather than on an agreement.
|
|
86
|
+
*
|
|
87
|
+
* WHAT DID NOT CONVERGE, DELIBERATELY: the retry policy and the exit code. See
|
|
88
|
+
* `probeVersion` for the first; the second is simply that `main` here returns
|
|
89
|
+
* nothing and the module's exit-1 CLI is never imported.
|
|
90
|
+
*
|
|
74
91
|
* @module scripts/reconcile-release-tags
|
|
75
92
|
*/
|
|
76
93
|
|
|
94
|
+
import { verifyPublish } from "../all/copy-overwrite/scripts/check-npm-publish-landed.mjs";
|
|
95
|
+
|
|
77
96
|
import { invokedAsScript } from "./lib/invoked-as-script.mjs";
|
|
78
97
|
|
|
79
98
|
/**
|
|
@@ -109,21 +128,27 @@ export function parseVersionTag(tag) {
|
|
|
109
128
|
}
|
|
110
129
|
|
|
111
130
|
/**
|
|
112
|
-
* What a single registry probe establishes.
|
|
131
|
+
* What a single registry probe establishes, in this sweep's vocabulary.
|
|
132
|
+
*
|
|
133
|
+
* The probe itself no longer lives here — `verifyPublish` decides which of the
|
|
134
|
+
* three words an HTTP answer earns, and it already applies the rule this
|
|
135
|
+
* function used to apply: only a definitive 404 on the EXACT-VERSION endpoint
|
|
136
|
+
* proves absence, while a 200 naming a different version, an auth failure, a
|
|
137
|
+
* rate limit, a timeout and any transport error all establish nothing.
|
|
113
138
|
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
* @param {
|
|
139
|
+
* This is kept as the sweep's own boundary rather than dropped as a
|
|
140
|
+
* pass-through. It is total: ANY outcome it does not recognise — a fourth
|
|
141
|
+
* verdict added upstream, a malformed result, no result at all — is read as
|
|
142
|
+
* `unprovable`. That default is the safe direction, because the damage this
|
|
143
|
+
* sweep can do is reporting a good release as absent, and an unrecognised word
|
|
144
|
+
* falling through to `missing` is exactly how that would happen.
|
|
145
|
+
* @param {object} outcome The result of one `verifyPublish` call.
|
|
146
|
+
* @param {string|null} [outcome.verdict] The verdict that call settled on.
|
|
121
147
|
* @returns {string} One of the `VERDICT` values.
|
|
122
148
|
*/
|
|
123
|
-
export function classifyProbe({
|
|
124
|
-
if (
|
|
125
|
-
if (
|
|
126
|
-
if (status === 404) return VERDICT.MISSING;
|
|
149
|
+
export function classifyProbe({ verdict = null } = {}) {
|
|
150
|
+
if (verdict === VERDICT.PUBLISHED) return VERDICT.PUBLISHED;
|
|
151
|
+
if (verdict === VERDICT.MISSING) return VERDICT.MISSING;
|
|
127
152
|
return VERDICT.UNPROVABLE;
|
|
128
153
|
}
|
|
129
154
|
|
|
@@ -153,92 +178,48 @@ export function reconcile({ tags, probe }) {
|
|
|
153
178
|
}
|
|
154
179
|
|
|
155
180
|
/**
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
* EXACT VERSION, never `dist-tags.latest`. `latest` lags a successful publish
|
|
159
|
-
* by minutes, so reconciling against it reports a release that shipped moments
|
|
160
|
-
* ago as absent — a false `missing` on the newest and most-scrutinised release.
|
|
161
|
-
* @param {string} pkg The package name.
|
|
162
|
-
* @param {string} version The exact version.
|
|
163
|
-
* @returns {string} The URL to probe.
|
|
164
|
-
*/
|
|
165
|
-
export function registryUrl(pkg, version) {
|
|
166
|
-
return `https://registry.npmjs.org/${pkg.replace("/", "%2f")}/${version}`;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Probe the registry for one version, converting every failure into a shape
|
|
171
|
-
* `classifyProbe` can read rather than throwing.
|
|
172
|
-
* @param {string} pkg The package name.
|
|
173
|
-
* @param {string} version The exact version.
|
|
174
|
-
* @param {typeof fetch} [fetchImpl] Injected for tests.
|
|
175
|
-
* @returns {Promise<{status: number|null, error: string|null}>} The probe result.
|
|
176
|
-
*/
|
|
177
|
-
export async function probeRegistry(pkg, version, fetchImpl = fetch) {
|
|
178
|
-
try {
|
|
179
|
-
const response = await fetchImpl(registryUrl(pkg, version), {
|
|
180
|
-
method: "GET",
|
|
181
|
-
});
|
|
182
|
-
if (response.status !== 200)
|
|
183
|
-
return { status: response.status, error: null };
|
|
184
|
-
// A 200 IS NOT ENOUGH — the body has to name the version that was asked
|
|
185
|
-
// for. This matches the release-time check (#3684) exactly: a 200 whose
|
|
186
|
-
// body names something else, or which does not parse, settles nothing and
|
|
187
|
-
// is `unprovable` rather than `published`. Two surfaces reporting the same
|
|
188
|
-
// word about one release have to mean the same thing by it, or comparing
|
|
189
|
-
// their reports is meaningless.
|
|
190
|
-
let body;
|
|
191
|
-
try {
|
|
192
|
-
body = await response.json();
|
|
193
|
-
} catch (error) {
|
|
194
|
-
return {
|
|
195
|
-
status: null,
|
|
196
|
-
error: `unparseable body: ${String(error?.message ?? error)}`,
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
if (body?.version !== version)
|
|
200
|
-
return {
|
|
201
|
-
status: null,
|
|
202
|
-
error: `registry served version ${String(body?.version)} for ${version}`,
|
|
203
|
-
};
|
|
204
|
-
return { status: 200, error: null };
|
|
205
|
-
} catch (error) {
|
|
206
|
-
// A transport failure is NOT evidence of absence. Returning it as an error
|
|
207
|
-
// is what makes `classifyProbe` answer `unprovable` instead of `missing`.
|
|
208
|
-
return { status: null, error: String(error?.message ?? error) };
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Probe a version, retrying ONLY while the answer is `unprovable`.
|
|
181
|
+
* Probe a version through the shared implementation, retrying ONLY while the
|
|
182
|
+
* answer is `unprovable`.
|
|
214
183
|
*
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
184
|
+
* THE RETRY POLICY IS THIS SWEEP'S, AND IT IS NOT THE MODULE'S. `verifyPublish`
|
|
185
|
+
* retries both non-`published` verdicts, because at release time a version that
|
|
186
|
+
* has just been published can lag the exact-version endpoint for a moment, so
|
|
187
|
+
* re-asking after a 404 is worth the wait. This sweep looks backwards at tags
|
|
188
|
+
* that are often years old, where a 404 is settled: re-asking would be asking
|
|
189
|
+
* the registry to change its mind, and paying for it 1,774 times. So the module
|
|
190
|
+
* is called with `attempts: 1` — one question per call, no internal retry — and
|
|
191
|
+
* the loop that decides whether to ask again lives here, where the timeline it
|
|
192
|
+
* reasons about lives. Same principle, different positions on that timeline;
|
|
193
|
+
* neither caller's behaviour moved when the probe converged.
|
|
219
194
|
*
|
|
220
|
-
*
|
|
221
|
-
* run over 1,699 tags produced two `unprovable` rows,
|
|
222
|
-
* `v2.325.4` — a version that IS published, whose probe
|
|
223
|
-
* flight. Without a re-probe every sweep carries a couple of
|
|
224
|
-
* has to chase by hand, which is how a report starts getting
|
|
195
|
+
* Re-asking an `unprovable` matters at sweep scale rather than for a single
|
|
196
|
+
* release. The first real run over 1,699 tags produced two `unprovable` rows,
|
|
197
|
+
* and one of them was `v2.325.4` — a version that IS published, whose probe
|
|
198
|
+
* simply failed in flight. Without a re-probe every sweep carries a couple of
|
|
199
|
+
* rows an operator has to chase by hand, which is how a report starts getting
|
|
200
|
+
* skimmed.
|
|
225
201
|
* @param {string} pkg The package name.
|
|
226
202
|
* @param {string} version The exact version.
|
|
227
203
|
* @param {object} [options] Retry controls.
|
|
228
204
|
* @param {number} [options.attempts] Total attempts, including the first.
|
|
229
205
|
* @param {() => Promise<void>} [options.pause] Delay between attempts.
|
|
230
206
|
* @param {typeof fetch} [options.fetchImpl] Injected for tests.
|
|
231
|
-
* @returns {Promise<{
|
|
207
|
+
* @returns {Promise<{verdict: string, detail: string}>} The settled outcome.
|
|
232
208
|
*/
|
|
233
|
-
export async function
|
|
209
|
+
export async function probeVersion(
|
|
234
210
|
pkg,
|
|
235
211
|
version,
|
|
236
212
|
{ attempts = 2, pause = async () => {}, fetchImpl = fetch } = {}
|
|
237
213
|
) {
|
|
238
|
-
let last = {
|
|
214
|
+
let last = { verdict: VERDICT.UNPROVABLE, detail: "not attempted" };
|
|
239
215
|
for (let attempt = 0; attempt < Math.max(1, attempts); attempt += 1) {
|
|
240
216
|
if (attempt > 0) await pause();
|
|
241
|
-
last = await
|
|
217
|
+
last = await verifyPublish({
|
|
218
|
+
packageName: pkg,
|
|
219
|
+
version,
|
|
220
|
+
attempts: 1,
|
|
221
|
+
fetchImpl,
|
|
222
|
+
});
|
|
242
223
|
if (classifyProbe(last) !== VERDICT.UNPROVABLE) return last;
|
|
243
224
|
}
|
|
244
225
|
return last;
|
|
@@ -313,7 +294,7 @@ async function main() {
|
|
|
313
294
|
for (const tag of tags) {
|
|
314
295
|
const version = parseVersionTag(tag);
|
|
315
296
|
if (version === null || probes.has(version)) continue;
|
|
316
|
-
probes.set(version, await
|
|
297
|
+
probes.set(version, await probeVersion(pkg, version));
|
|
317
298
|
}
|
|
318
299
|
|
|
319
300
|
const result = reconcile({
|