@codyswann/lisa 4.49.0 → 4.50.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-overwrite/scripts/check-conflict-markers.mjs +4 -1
- package/all/copy-overwrite/scripts/lib/gate-failure-diagnosis.mjs +163 -0
- package/all/copy-overwrite/scripts/lisa-enforcement-fallback.sh +178 -1
- package/all/copy-overwrite/scripts/lisa-hooks/block-direct-issue-create.sh +89 -10
- package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +44 -0
- package/all/copy-overwrite/scripts/lisa-lint-staged-preflight.mjs +1 -1
- package/all/copy-overwrite/scripts/lisa-run-gates.mjs +24 -24
- package/all/copy-overwrite/scripts/lisa-work-item.mjs +490 -81
- package/cdk/package-lisa/package.lisa.json +45 -15
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +76 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +120 -227
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/opencode/plugin-templates/lisa-block-direct-issue-create.ts +45 -1
- package/dist/opencode/plugin-templates/parity-safety-net.sh +44 -0
- package/expo/package-lisa/package.lisa.json +83 -53
- package/harper-fabric/package-lisa/package.lisa.json +62 -32
- package/nestjs/package-lisa/package.lisa.json +57 -27
- package/npm-package/package-lisa/package.lisa.json +43 -7
- package/package.json +11 -7
- package/phaser/copy-overwrite/ast-grep/rules/no-day-truncating-file-age-predicate.yml +50 -0
- package/phaser/package-lisa/package.lisa.json +62 -32
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-drive-pr-to-merge/SKILL.md +17 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-github-build-intake/SKILL.md +4 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-build-intake/SKILL.md +4 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-build-intake/SKILL.md +4 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-repair-intake/SKILL.md +4 -1
- package/plugins/lisa/hooks/block-direct-issue-create.sh +89 -10
- package/plugins/lisa/hooks/discharge-work-item-gates.sh +7 -1
- package/plugins/lisa/hooks/parity-safety-net.sh +44 -0
- package/plugins/lisa/hooks/threshold-ratchet-families.mjs +12 -0
- package/plugins/lisa/rules/eager/00-rule-index.md +79 -0
- package/plugins/lisa/rules/eager/base-rules.md +10 -0
- package/plugins/lisa/rules/eager/config-resolution.md +1 -88
- package/plugins/lisa/rules/reference/automation-runbook-contract.md +97 -0
- package/plugins/lisa/rules/reference/bdd-e2e-coverage.md +56 -0
- package/plugins/lisa/rules/reference/blocker-containment.md +58 -0
- package/plugins/lisa/rules/reference/claim-archaeology.md +42 -0
- package/plugins/lisa/rules/reference/claim-evidence-mapping.md +83 -0
- package/plugins/lisa/rules/reference/claim-time-guards.md +37 -0
- package/plugins/lisa/rules/reference/config-resolution.md +97 -0
- package/plugins/lisa/rules/reference/control-reachability.md +44 -0
- package/plugins/lisa/rules/reference/convergent-review.md +31 -0
- package/plugins/lisa/rules/reference/credential-substrate-precedence.md +57 -0
- package/plugins/lisa/rules/reference/dependency-decision-records.md +37 -0
- package/plugins/lisa/rules/reference/dependency-internalization-kit.md +47 -0
- package/plugins/lisa/rules/reference/dependency-trust-classes.md +48 -0
- package/plugins/lisa/rules/reference/deployed-state-readback.md +52 -0
- package/plugins/lisa/rules/reference/derived-branch-plan.md +52 -0
- package/plugins/lisa/rules/reference/design-source-of-truth.md +43 -0
- package/plugins/lisa/rules/reference/design-value-binding.md +103 -0
- package/plugins/lisa/rules/reference/documentation-source-paths.md +18 -0
- package/plugins/lisa/rules/reference/leaf-only-lifecycle.md +75 -0
- package/plugins/lisa/rules/reference/observability-audit.md +20 -0
- package/plugins/lisa/rules/reference/prd-definition-of-ready.md +37 -0
- package/plugins/lisa/rules/reference/prd-lifecycle-rollup.md +36 -0
- package/plugins/lisa/rules/reference/pre-flight-autofill.md +88 -0
- package/plugins/lisa/rules/reference/promotion-contract.md +21 -0
- package/plugins/lisa/rules/reference/readiness-rubric.md +63 -0
- package/plugins/lisa/rules/reference/ready-role-filing.md +34 -0
- package/plugins/lisa/rules/reference/rejection-detection.md +69 -0
- package/plugins/lisa/rules/reference/repo-scope-split.md +48 -0
- package/plugins/lisa/rules/reference/reset-seed-coverage.md +55 -0
- package/plugins/lisa/rules/reference/usage-accounting.md +38 -0
- package/plugins/lisa/rules/reference/work-item-definition-of-ready.md +32 -0
- package/plugins/lisa/scripts/intake-blocker-reprobe.mjs +158 -1
- package/plugins/lisa/skills/lisa-drive-pr-to-merge/SKILL.md +17 -1
- package/plugins/lisa/skills/lisa-github-build-intake/SKILL.md +4 -0
- package/plugins/lisa/skills/lisa-jira-build-intake/SKILL.md +4 -0
- package/plugins/lisa/skills/lisa-linear-build-intake/SKILL.md +4 -0
- package/plugins/lisa/skills/lisa-repair-intake/SKILL.md +4 -1
- package/plugins/lisa-agy/hooks/block-direct-issue-create.sh +89 -10
- package/plugins/lisa-agy/hooks/parity-safety-net.sh +44 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/scripts/intake-blocker-reprobe.mjs +158 -1
- package/plugins/lisa-agy/skills/lisa-drive-pr-to-merge/SKILL.md +17 -1
- package/plugins/lisa-agy/skills/lisa-github-build-intake/SKILL.md +4 -0
- package/plugins/lisa-agy/skills/lisa-jira-build-intake/SKILL.md +4 -0
- package/plugins/lisa-agy/skills/lisa-linear-build-intake/SKILL.md +4 -0
- package/plugins/lisa-agy/skills/lisa-repair-intake/SKILL.md +4 -1
- 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 +1 -1
- package/plugins/lisa-copilot/hooks/block-direct-issue-create.sh +89 -10
- package/plugins/lisa-copilot/hooks/discharge-work-item-gates.sh +7 -1
- package/plugins/lisa-copilot/hooks/parity-safety-net.sh +44 -0
- package/plugins/lisa-copilot/hooks/threshold-ratchet-families.mjs +12 -0
- package/plugins/lisa-copilot/rules/eager/00-rule-index.md +79 -0
- package/plugins/lisa-copilot/rules/eager/base-rules.md +10 -0
- package/plugins/lisa-copilot/rules/eager/config-resolution.md +1 -88
- package/plugins/lisa-copilot/rules/reference/automation-runbook-contract.md +97 -0
- package/plugins/lisa-copilot/rules/reference/bdd-e2e-coverage.md +56 -0
- package/plugins/lisa-copilot/rules/reference/blocker-containment.md +58 -0
- package/plugins/lisa-copilot/rules/reference/claim-archaeology.md +42 -0
- package/plugins/lisa-copilot/rules/reference/claim-evidence-mapping.md +83 -0
- package/plugins/lisa-copilot/rules/reference/claim-time-guards.md +37 -0
- package/plugins/lisa-copilot/rules/reference/config-resolution.md +97 -0
- package/plugins/lisa-copilot/rules/reference/control-reachability.md +44 -0
- package/plugins/lisa-copilot/rules/reference/convergent-review.md +31 -0
- package/plugins/lisa-copilot/rules/reference/credential-substrate-precedence.md +57 -0
- package/plugins/lisa-copilot/rules/reference/dependency-decision-records.md +37 -0
- package/plugins/lisa-copilot/rules/reference/dependency-internalization-kit.md +47 -0
- package/plugins/lisa-copilot/rules/reference/dependency-trust-classes.md +48 -0
- package/plugins/lisa-copilot/rules/reference/deployed-state-readback.md +52 -0
- package/plugins/lisa-copilot/rules/reference/derived-branch-plan.md +52 -0
- package/plugins/lisa-copilot/rules/reference/design-source-of-truth.md +43 -0
- package/plugins/lisa-copilot/rules/reference/design-value-binding.md +103 -0
- package/plugins/lisa-copilot/rules/reference/documentation-source-paths.md +18 -0
- package/plugins/lisa-copilot/rules/reference/leaf-only-lifecycle.md +75 -0
- package/plugins/lisa-copilot/rules/reference/observability-audit.md +20 -0
- package/plugins/lisa-copilot/rules/reference/prd-definition-of-ready.md +37 -0
- package/plugins/lisa-copilot/rules/reference/prd-lifecycle-rollup.md +36 -0
- package/plugins/lisa-copilot/rules/reference/pre-flight-autofill.md +88 -0
- package/plugins/lisa-copilot/rules/reference/promotion-contract.md +21 -0
- package/plugins/lisa-copilot/rules/reference/readiness-rubric.md +63 -0
- package/plugins/lisa-copilot/rules/reference/ready-role-filing.md +34 -0
- package/plugins/lisa-copilot/rules/reference/rejection-detection.md +69 -0
- package/plugins/lisa-copilot/rules/reference/repo-scope-split.md +48 -0
- package/plugins/lisa-copilot/rules/reference/reset-seed-coverage.md +55 -0
- package/plugins/lisa-copilot/rules/reference/usage-accounting.md +38 -0
- package/plugins/lisa-copilot/rules/reference/work-item-definition-of-ready.md +32 -0
- package/plugins/lisa-copilot/scripts/intake-blocker-reprobe.mjs +158 -1
- package/plugins/lisa-copilot/skills/lisa-drive-pr-to-merge/SKILL.md +17 -1
- package/plugins/lisa-copilot/skills/lisa-github-build-intake/SKILL.md +4 -0
- package/plugins/lisa-copilot/skills/lisa-jira-build-intake/SKILL.md +4 -0
- package/plugins/lisa-copilot/skills/lisa-linear-build-intake/SKILL.md +4 -0
- package/plugins/lisa-copilot/skills/lisa-repair-intake/SKILL.md +4 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/block-direct-issue-create.sh +89 -10
- package/plugins/lisa-cursor/hooks/discharge-work-item-gates.sh +7 -1
- package/plugins/lisa-cursor/hooks/parity-safety-net.sh +44 -0
- package/plugins/lisa-cursor/hooks/threshold-ratchet-families.mjs +12 -0
- package/plugins/lisa-cursor/rules/00-rule-index.mdc +84 -0
- package/plugins/lisa-cursor/rules/automation-runbook-contract-reference.mdc +97 -0
- package/plugins/lisa-cursor/rules/base-rules.mdc +10 -0
- package/plugins/lisa-cursor/rules/bdd-e2e-coverage-reference.mdc +56 -0
- package/plugins/lisa-cursor/rules/blocker-containment-reference.mdc +58 -0
- package/plugins/lisa-cursor/rules/claim-archaeology-reference.mdc +42 -0
- package/plugins/lisa-cursor/rules/claim-evidence-mapping-reference.mdc +83 -0
- package/plugins/lisa-cursor/rules/claim-time-guards-reference.mdc +37 -0
- package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +97 -0
- package/plugins/lisa-cursor/rules/config-resolution.mdc +1 -88
- package/plugins/lisa-cursor/rules/control-reachability-reference.mdc +44 -0
- package/plugins/lisa-cursor/rules/convergent-review-reference.mdc +31 -0
- package/plugins/lisa-cursor/rules/credential-substrate-precedence-reference.mdc +57 -0
- package/plugins/lisa-cursor/rules/dependency-decision-records-reference.mdc +37 -0
- package/plugins/lisa-cursor/rules/dependency-internalization-kit-reference.mdc +47 -0
- package/plugins/lisa-cursor/rules/dependency-trust-classes-reference.mdc +48 -0
- package/plugins/lisa-cursor/rules/deployed-state-readback-reference.mdc +52 -0
- package/plugins/lisa-cursor/rules/derived-branch-plan-reference.mdc +52 -0
- package/plugins/lisa-cursor/rules/design-source-of-truth-reference.mdc +43 -0
- package/plugins/lisa-cursor/rules/design-value-binding-reference.mdc +103 -0
- package/plugins/lisa-cursor/rules/documentation-source-paths-reference.mdc +18 -0
- package/plugins/lisa-cursor/rules/leaf-only-lifecycle-reference.mdc +75 -0
- package/plugins/lisa-cursor/rules/observability-audit-reference.mdc +20 -0
- package/plugins/lisa-cursor/rules/prd-definition-of-ready-reference.mdc +37 -0
- package/plugins/lisa-cursor/rules/prd-lifecycle-rollup-reference.mdc +36 -0
- package/plugins/lisa-cursor/rules/pre-flight-autofill-reference.mdc +88 -0
- package/plugins/lisa-cursor/rules/promotion-contract-reference.mdc +21 -0
- package/plugins/lisa-cursor/rules/readiness-rubric-reference.mdc +63 -0
- package/plugins/lisa-cursor/rules/ready-role-filing-reference.mdc +34 -0
- package/plugins/lisa-cursor/rules/rejection-detection-reference.mdc +69 -0
- package/plugins/lisa-cursor/rules/repo-scope-split-reference.mdc +48 -0
- package/plugins/lisa-cursor/rules/reset-seed-coverage-reference.mdc +55 -0
- package/plugins/lisa-cursor/rules/usage-accounting-reference.mdc +38 -0
- package/plugins/lisa-cursor/rules/work-item-definition-of-ready-reference.mdc +32 -0
- package/plugins/lisa-cursor/scripts/intake-blocker-reprobe.mjs +158 -1
- package/plugins/lisa-cursor/skills/lisa-drive-pr-to-merge/SKILL.md +17 -1
- package/plugins/lisa-cursor/skills/lisa-github-build-intake/SKILL.md +4 -0
- package/plugins/lisa-cursor/skills/lisa-jira-build-intake/SKILL.md +4 -0
- package/plugins/lisa-cursor/skills/lisa-linear-build-intake/SKILL.md +4 -0
- package/plugins/lisa-cursor/skills/lisa-repair-intake/SKILL.md +4 -1
- 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-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- 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-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- 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/src/base/hooks/block-direct-issue-create.sh +89 -10
- package/plugins/src/base/hooks/discharge-work-item-gates.sh +7 -1
- package/plugins/src/base/hooks/parity-safety-net.sh +44 -0
- package/plugins/src/base/hooks/threshold-ratchet-families.mjs +12 -0
- package/plugins/src/base/rules/eager/00-rule-index.md +79 -0
- package/plugins/src/base/rules/eager/base-rules.md +10 -0
- package/plugins/src/base/rules/eager/config-resolution.md +1 -88
- package/plugins/src/base/rules/reference/automation-runbook-contract.md +97 -0
- package/plugins/src/base/rules/reference/bdd-e2e-coverage.md +56 -0
- package/plugins/src/base/rules/reference/blocker-containment.md +58 -0
- package/plugins/src/base/rules/reference/claim-archaeology.md +42 -0
- package/plugins/src/base/rules/reference/claim-evidence-mapping.md +83 -0
- package/plugins/src/base/rules/reference/claim-time-guards.md +37 -0
- package/plugins/src/base/rules/reference/config-resolution.md +97 -0
- package/plugins/src/base/rules/reference/control-reachability.md +44 -0
- package/plugins/src/base/rules/reference/convergent-review.md +31 -0
- package/plugins/src/base/rules/reference/credential-substrate-precedence.md +57 -0
- package/plugins/src/base/rules/reference/dependency-decision-records.md +37 -0
- package/plugins/src/base/rules/reference/dependency-internalization-kit.md +47 -0
- package/plugins/src/base/rules/reference/dependency-trust-classes.md +48 -0
- package/plugins/src/base/rules/reference/deployed-state-readback.md +52 -0
- package/plugins/src/base/rules/reference/derived-branch-plan.md +52 -0
- package/plugins/src/base/rules/reference/design-source-of-truth.md +43 -0
- package/plugins/src/base/rules/reference/design-value-binding.md +103 -0
- package/plugins/src/base/rules/reference/documentation-source-paths.md +18 -0
- package/plugins/src/base/rules/reference/leaf-only-lifecycle.md +75 -0
- package/plugins/src/base/rules/reference/observability-audit.md +20 -0
- package/plugins/src/base/rules/reference/prd-definition-of-ready.md +37 -0
- package/plugins/src/base/rules/reference/prd-lifecycle-rollup.md +36 -0
- package/plugins/src/base/rules/reference/pre-flight-autofill.md +88 -0
- package/plugins/src/base/rules/reference/promotion-contract.md +21 -0
- package/plugins/src/base/rules/reference/readiness-rubric.md +63 -0
- package/plugins/src/base/rules/reference/ready-role-filing.md +34 -0
- package/plugins/src/base/rules/reference/rejection-detection.md +69 -0
- package/plugins/src/base/rules/reference/repo-scope-split.md +48 -0
- package/plugins/src/base/rules/reference/reset-seed-coverage.md +55 -0
- package/plugins/src/base/rules/reference/usage-accounting.md +38 -0
- package/plugins/src/base/rules/reference/work-item-definition-of-ready.md +32 -0
- package/plugins/src/base/scripts/intake-blocker-reprobe.mjs +158 -1
- package/plugins/src/base/skills/lisa-drive-pr-to-merge/SKILL.md +17 -1
- package/plugins/src/base/skills/lisa-github-build-intake/SKILL.md +4 -0
- package/plugins/src/base/skills/lisa-jira-build-intake/SKILL.md +4 -0
- package/plugins/src/base/skills/lisa-linear-build-intake/SKILL.md +4 -0
- package/plugins/src/base/skills/lisa-repair-intake/SKILL.md +4 -1
- package/rails/copy-overwrite/ast-grep/rules/no-day-truncating-file-age-predicate.yml +50 -0
- package/rails/copy-overwrite/scripts/threshold-ratchet-families.mjs +12 -0
- package/scripts/check-eager-rule-budget.mjs +176 -0
- package/scripts/check-generated-artifact-merge-coverage.mjs +7 -0
- package/scripts/check-rules-pairing.sh +45 -14
- package/scripts/check-shipped-surface-removals.mjs +851 -0
- package/scripts/generate-export-surface.mjs +318 -0
- package/scripts/lib/shipped-surface.mjs +212 -0
- package/scripts/lisa-enforcement-fallback.sh +178 -1
- package/scripts/remedy-conformance.mjs +435 -0
- package/scripts/two-channel-couplings.json +2 -2
- package/typescript/copy-contents/.husky/pre-push +147 -2
- package/typescript/copy-overwrite/ast-grep/rules/no-day-truncating-file-age-predicate.yml +50 -0
- package/typescript/copy-overwrite/audit.ignore.config.json +0 -10
- package/typescript/copy-overwrite/scripts/lisa-mutation.mjs +180 -29
- package/typescript/copy-overwrite/scripts/threshold-ratchet-families.mjs +12 -0
- package/typescript/package-lisa/package.lisa.json +72 -42
- package/plugins/lisa/rules/eager/automation-runbook-contract.md +0 -92
- package/plugins/lisa/rules/eager/bdd-e2e-coverage.md +0 -51
- package/plugins/lisa/rules/eager/blocker-containment.md +0 -53
- package/plugins/lisa/rules/eager/claim-archaeology.md +0 -37
- package/plugins/lisa/rules/eager/claim-evidence-mapping.md +0 -78
- package/plugins/lisa/rules/eager/claim-time-guards.md +0 -32
- package/plugins/lisa/rules/eager/control-reachability.md +0 -39
- package/plugins/lisa/rules/eager/convergent-review.md +0 -24
- package/plugins/lisa/rules/eager/credential-substrate-precedence.md +0 -54
- package/plugins/lisa/rules/eager/dependency-decision-records.md +0 -32
- package/plugins/lisa/rules/eager/dependency-internalization-kit.md +0 -42
- package/plugins/lisa/rules/eager/dependency-trust-classes.md +0 -43
- package/plugins/lisa/rules/eager/deployed-state-readback.md +0 -47
- package/plugins/lisa/rules/eager/derived-branch-plan.md +0 -47
- package/plugins/lisa/rules/eager/design-source-of-truth.md +0 -38
- package/plugins/lisa/rules/eager/design-value-binding.md +0 -98
- package/plugins/lisa/rules/eager/documentation-source-paths.md +0 -13
- package/plugins/lisa/rules/eager/factory-model.md +0 -41
- package/plugins/lisa/rules/eager/leaf-only-lifecycle.md +0 -70
- package/plugins/lisa/rules/eager/observability-audit.md +0 -15
- package/plugins/lisa/rules/eager/prd-definition-of-ready.md +0 -32
- package/plugins/lisa/rules/eager/prd-lifecycle-rollup.md +0 -31
- package/plugins/lisa/rules/eager/pre-flight-autofill.md +0 -84
- package/plugins/lisa/rules/eager/project-learnings.md +0 -22
- package/plugins/lisa/rules/eager/promotion-contract.md +0 -18
- package/plugins/lisa/rules/eager/readiness-rubric.md +0 -58
- package/plugins/lisa/rules/eager/ready-role-filing.md +0 -29
- package/plugins/lisa/rules/eager/rejection-detection.md +0 -64
- package/plugins/lisa/rules/eager/repo-scope-split.md +0 -43
- package/plugins/lisa/rules/eager/reset-seed-coverage.md +0 -50
- package/plugins/lisa/rules/eager/usage-accounting.md +0 -33
- package/plugins/lisa/rules/eager/work-item-definition-of-ready.md +0 -27
- package/plugins/lisa-copilot/rules/eager/automation-runbook-contract.md +0 -92
- package/plugins/lisa-copilot/rules/eager/bdd-e2e-coverage.md +0 -51
- package/plugins/lisa-copilot/rules/eager/blocker-containment.md +0 -53
- package/plugins/lisa-copilot/rules/eager/claim-archaeology.md +0 -37
- package/plugins/lisa-copilot/rules/eager/claim-evidence-mapping.md +0 -78
- package/plugins/lisa-copilot/rules/eager/claim-time-guards.md +0 -32
- package/plugins/lisa-copilot/rules/eager/control-reachability.md +0 -39
- package/plugins/lisa-copilot/rules/eager/convergent-review.md +0 -24
- package/plugins/lisa-copilot/rules/eager/credential-substrate-precedence.md +0 -54
- package/plugins/lisa-copilot/rules/eager/dependency-decision-records.md +0 -32
- package/plugins/lisa-copilot/rules/eager/dependency-internalization-kit.md +0 -42
- package/plugins/lisa-copilot/rules/eager/dependency-trust-classes.md +0 -43
- package/plugins/lisa-copilot/rules/eager/deployed-state-readback.md +0 -47
- package/plugins/lisa-copilot/rules/eager/derived-branch-plan.md +0 -47
- package/plugins/lisa-copilot/rules/eager/design-source-of-truth.md +0 -38
- package/plugins/lisa-copilot/rules/eager/design-value-binding.md +0 -98
- package/plugins/lisa-copilot/rules/eager/documentation-source-paths.md +0 -13
- package/plugins/lisa-copilot/rules/eager/factory-model.md +0 -41
- package/plugins/lisa-copilot/rules/eager/leaf-only-lifecycle.md +0 -70
- package/plugins/lisa-copilot/rules/eager/observability-audit.md +0 -15
- package/plugins/lisa-copilot/rules/eager/prd-definition-of-ready.md +0 -32
- package/plugins/lisa-copilot/rules/eager/prd-lifecycle-rollup.md +0 -31
- package/plugins/lisa-copilot/rules/eager/pre-flight-autofill.md +0 -84
- package/plugins/lisa-copilot/rules/eager/project-learnings.md +0 -22
- package/plugins/lisa-copilot/rules/eager/promotion-contract.md +0 -18
- package/plugins/lisa-copilot/rules/eager/readiness-rubric.md +0 -58
- package/plugins/lisa-copilot/rules/eager/ready-role-filing.md +0 -29
- package/plugins/lisa-copilot/rules/eager/rejection-detection.md +0 -64
- package/plugins/lisa-copilot/rules/eager/repo-scope-split.md +0 -43
- package/plugins/lisa-copilot/rules/eager/reset-seed-coverage.md +0 -50
- package/plugins/lisa-copilot/rules/eager/usage-accounting.md +0 -33
- package/plugins/lisa-copilot/rules/eager/work-item-definition-of-ready.md +0 -27
- package/plugins/lisa-cursor/rules/automation-runbook-contract.mdc +0 -97
- package/plugins/lisa-cursor/rules/bdd-e2e-coverage.mdc +0 -56
- package/plugins/lisa-cursor/rules/blocker-containment.mdc +0 -58
- package/plugins/lisa-cursor/rules/claim-archaeology.mdc +0 -42
- package/plugins/lisa-cursor/rules/claim-evidence-mapping.mdc +0 -83
- package/plugins/lisa-cursor/rules/claim-time-guards.mdc +0 -37
- package/plugins/lisa-cursor/rules/control-reachability.mdc +0 -44
- package/plugins/lisa-cursor/rules/convergent-review.mdc +0 -29
- package/plugins/lisa-cursor/rules/credential-substrate-precedence.mdc +0 -59
- package/plugins/lisa-cursor/rules/dependency-decision-records.mdc +0 -37
- package/plugins/lisa-cursor/rules/dependency-internalization-kit.mdc +0 -47
- package/plugins/lisa-cursor/rules/dependency-trust-classes.mdc +0 -48
- package/plugins/lisa-cursor/rules/deployed-state-readback.mdc +0 -52
- package/plugins/lisa-cursor/rules/derived-branch-plan.mdc +0 -52
- package/plugins/lisa-cursor/rules/design-source-of-truth.mdc +0 -43
- package/plugins/lisa-cursor/rules/design-value-binding.mdc +0 -103
- package/plugins/lisa-cursor/rules/documentation-source-paths.mdc +0 -18
- package/plugins/lisa-cursor/rules/factory-model.mdc +0 -46
- package/plugins/lisa-cursor/rules/leaf-only-lifecycle.mdc +0 -75
- package/plugins/lisa-cursor/rules/observability-audit.mdc +0 -20
- package/plugins/lisa-cursor/rules/prd-definition-of-ready.mdc +0 -37
- package/plugins/lisa-cursor/rules/prd-lifecycle-rollup.mdc +0 -36
- package/plugins/lisa-cursor/rules/pre-flight-autofill.mdc +0 -89
- package/plugins/lisa-cursor/rules/project-learnings.mdc +0 -27
- package/plugins/lisa-cursor/rules/promotion-contract.mdc +0 -23
- package/plugins/lisa-cursor/rules/readiness-rubric.mdc +0 -63
- package/plugins/lisa-cursor/rules/ready-role-filing.mdc +0 -34
- package/plugins/lisa-cursor/rules/rejection-detection.mdc +0 -69
- package/plugins/lisa-cursor/rules/repo-scope-split.mdc +0 -48
- package/plugins/lisa-cursor/rules/reset-seed-coverage.mdc +0 -55
- package/plugins/lisa-cursor/rules/usage-accounting.mdc +0 -38
- package/plugins/lisa-cursor/rules/work-item-definition-of-ready.mdc +0 -32
- package/plugins/src/base/rules/eager/automation-runbook-contract.md +0 -92
- package/plugins/src/base/rules/eager/bdd-e2e-coverage.md +0 -51
- package/plugins/src/base/rules/eager/blocker-containment.md +0 -53
- package/plugins/src/base/rules/eager/claim-archaeology.md +0 -37
- package/plugins/src/base/rules/eager/claim-evidence-mapping.md +0 -78
- package/plugins/src/base/rules/eager/claim-time-guards.md +0 -32
- package/plugins/src/base/rules/eager/control-reachability.md +0 -39
- package/plugins/src/base/rules/eager/convergent-review.md +0 -24
- package/plugins/src/base/rules/eager/credential-substrate-precedence.md +0 -54
- package/plugins/src/base/rules/eager/dependency-decision-records.md +0 -32
- package/plugins/src/base/rules/eager/dependency-internalization-kit.md +0 -42
- package/plugins/src/base/rules/eager/dependency-trust-classes.md +0 -43
- package/plugins/src/base/rules/eager/deployed-state-readback.md +0 -47
- package/plugins/src/base/rules/eager/derived-branch-plan.md +0 -47
- package/plugins/src/base/rules/eager/design-source-of-truth.md +0 -38
- package/plugins/src/base/rules/eager/design-value-binding.md +0 -98
- package/plugins/src/base/rules/eager/documentation-source-paths.md +0 -13
- package/plugins/src/base/rules/eager/factory-model.md +0 -41
- package/plugins/src/base/rules/eager/leaf-only-lifecycle.md +0 -70
- package/plugins/src/base/rules/eager/observability-audit.md +0 -15
- package/plugins/src/base/rules/eager/prd-definition-of-ready.md +0 -32
- package/plugins/src/base/rules/eager/prd-lifecycle-rollup.md +0 -31
- package/plugins/src/base/rules/eager/pre-flight-autofill.md +0 -84
- package/plugins/src/base/rules/eager/project-learnings.md +0 -22
- package/plugins/src/base/rules/eager/promotion-contract.md +0 -18
- package/plugins/src/base/rules/eager/readiness-rubric.md +0 -58
- package/plugins/src/base/rules/eager/ready-role-filing.md +0 -29
- package/plugins/src/base/rules/eager/rejection-detection.md +0 -64
- package/plugins/src/base/rules/eager/repo-scope-split.md +0 -43
- package/plugins/src/base/rules/eager/reset-seed-coverage.md +0 -50
- package/plugins/src/base/rules/eager/usage-accounting.md +0 -33
- package/plugins/src/base/rules/eager/work-item-definition-of-ready.md +0 -27
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Record the public export surface this package ships, so a removal is an
|
|
4
|
+
* observation rather than an inference.
|
|
5
|
+
*
|
|
6
|
+
* ## The defect this exists against
|
|
7
|
+
*
|
|
8
|
+
* A release shipped a changelog declaring ZERO breaking changes across a range
|
|
9
|
+
* whose diff had removed a public export. The changelog was accurate about its
|
|
10
|
+
* own contents and useless as a safety signal: a downstream reader did the
|
|
11
|
+
* right check, carefully, with a positive control, and got a confident wrong
|
|
12
|
+
* answer. Two consumer repositories were about to upgrade on the strength of it
|
|
13
|
+
* (CodySwannGT/lisa#3718).
|
|
14
|
+
*
|
|
15
|
+
* The mechanism is that the attestation is **derived from something adjacent**.
|
|
16
|
+
* "No breaking changes" is computed from commit-message conventions — whether
|
|
17
|
+
* anyone wrote `!` or a `BREAKING CHANGE:` footer — and a template rewrite that
|
|
18
|
+
* drops an exported symbol carries neither. So the claim is about what authors
|
|
19
|
+
* typed, and it is read as a claim about what happened to the exported surface.
|
|
20
|
+
*
|
|
21
|
+
* A claim and an observation are different things. `"no breaking changes"` is a
|
|
22
|
+
* claim. `"these names were exported before and are exported now"` is an
|
|
23
|
+
* observation, and it is the one a consumer actually needs.
|
|
24
|
+
*
|
|
25
|
+
* ## Why the surface is recorded rather than computed at release time
|
|
26
|
+
*
|
|
27
|
+
* A committed artifact makes the removal **visible in the pull request that
|
|
28
|
+
* causes it**, not only in the release notes afterwards. The diff is the signal.
|
|
29
|
+
* It also means the comparison is a pure git operation with no network — which
|
|
30
|
+
* matters, because the release path already depends on registry availability
|
|
31
|
+
* that has failed under us.
|
|
32
|
+
*
|
|
33
|
+
* ## What it covers, and what it does not
|
|
34
|
+
*
|
|
35
|
+
* The shipped `.mjs` scripts under the `files` allowlist. That is deliberately
|
|
36
|
+
* where the incident happened: a `copy-overwrite` template is installed into a
|
|
37
|
+
* consumer's own `scripts/` directory, and the consumer's repo-owned code
|
|
38
|
+
* imports from it. Those files are invisible to an import graph on either side
|
|
39
|
+
* — Lisa cannot see the consumer's importer, and the consumer has no dependency
|
|
40
|
+
* to declare — so the exported names are the only shared contract there is.
|
|
41
|
+
*
|
|
42
|
+
* It reads exports **statically**, by their declaration form. It does not
|
|
43
|
+
* execute the modules, and it therefore cannot see a name attached at runtime.
|
|
44
|
+
* That limit is deliberate: importing 130 shipped scripts to enumerate them
|
|
45
|
+
* would run their top-level code during a build.
|
|
46
|
+
* @module scripts/generate-export-surface
|
|
47
|
+
*/
|
|
48
|
+
import { execFileSync } from "node:child_process";
|
|
49
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
50
|
+
import path from "node:path";
|
|
51
|
+
|
|
52
|
+
/** Directories inside the `files` allowlist that ship executable `.mjs`. */
|
|
53
|
+
const SHIPPED_ROOTS = [
|
|
54
|
+
"all",
|
|
55
|
+
"typescript",
|
|
56
|
+
"rails",
|
|
57
|
+
"expo",
|
|
58
|
+
"nestjs",
|
|
59
|
+
"cdk",
|
|
60
|
+
"phaser",
|
|
61
|
+
"harper-fabric",
|
|
62
|
+
"scripts",
|
|
63
|
+
"npm-package",
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Where the recorded surface lives.
|
|
68
|
+
*
|
|
69
|
+
* JSON rather than a TypeScript module, and the reason is the diff. One
|
|
70
|
+
* exported name per line is what makes a removal show up as a DELETED line in
|
|
71
|
+
* the pull request that causes it — which is the entire point of recording the
|
|
72
|
+
* surface. At 974 names that is well past the 300-line ceiling `max-lines`
|
|
73
|
+
* applies to source, and the sibling generated artifacts buy their way past it
|
|
74
|
+
* with a file-wide `eslint-disable`. Data does not need to be source: as JSON
|
|
75
|
+
* the artifact keeps the per-name diff, needs no waiver, and its reader becomes
|
|
76
|
+
* `JSON.parse` instead of a hand-rolled line parser that could silently
|
|
77
|
+
* disagree with the renderer.
|
|
78
|
+
*/
|
|
79
|
+
const ARTIFACT = "src/core/export-surface.json";
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Declaration forms that introduce a named export.
|
|
83
|
+
*
|
|
84
|
+
* Anchored at line start, because an `export` inside a string or a comment is
|
|
85
|
+
* not a declaration and counting it would make the artifact disagree with the
|
|
86
|
+
* module. The corpus was surveyed before this list was written: 613 `function`,
|
|
87
|
+
* 306 `const`, 38 `async function`, 14 `class`, 4 brace-lists.
|
|
88
|
+
*/
|
|
89
|
+
const DECLARATION =
|
|
90
|
+
/^export\s+(?:async\s+)?(?:function|const|let|var|class)\s+([A-Za-z_$][\w$]*)/;
|
|
91
|
+
|
|
92
|
+
/** `export { a, b as c }` — the re-export form. */
|
|
93
|
+
const BRACE_LIST = /^export\s*\{([^}]*)\}/;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Every tracked, shipped `.mjs` path.
|
|
97
|
+
* @param {string} cwd Repository root.
|
|
98
|
+
* @returns {string[]} Repo-relative paths, sorted.
|
|
99
|
+
*/
|
|
100
|
+
export function shippedScripts(cwd = process.cwd()) {
|
|
101
|
+
const listed = execFileSync("git", ["ls-files"], {
|
|
102
|
+
cwd,
|
|
103
|
+
encoding: "utf8",
|
|
104
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
105
|
+
}).split("\n");
|
|
106
|
+
return listed
|
|
107
|
+
.filter(file => file.endsWith(".mjs"))
|
|
108
|
+
.filter(file => SHIPPED_ROOTS.includes(file.split("/")[0]))
|
|
109
|
+
.sort((left, right) => (left < right ? -1 : left > right ? 1 : 0));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The names one module exports.
|
|
114
|
+
*
|
|
115
|
+
* `export default` is deliberately excluded: it has no name to compare, so
|
|
116
|
+
* recording it would add a row that can never change meaningfully.
|
|
117
|
+
* @param {string} source Module source.
|
|
118
|
+
* @returns {string[]} Exported names, sorted and deduplicated.
|
|
119
|
+
*/
|
|
120
|
+
export function exportedNames(source) {
|
|
121
|
+
const names = new Set();
|
|
122
|
+
for (const line of source.split("\n")) {
|
|
123
|
+
const declared = DECLARATION.exec(line);
|
|
124
|
+
if (declared) {
|
|
125
|
+
names.add(declared[1]);
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const braced = BRACE_LIST.exec(line);
|
|
129
|
+
if (!braced) continue;
|
|
130
|
+
for (const clause of braced[1].split(",")) {
|
|
131
|
+
// `a as b` exports b; the local name is not part of the surface.
|
|
132
|
+
const parts = clause.trim().split(/\s+as\s+/);
|
|
133
|
+
const exported = (parts.at(-1) ?? "").trim();
|
|
134
|
+
if (exported && exported !== "default") names.add(exported);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return [...names].sort((left, right) =>
|
|
138
|
+
left < right ? -1 : left > right ? 1 : 0
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* The surface as it stands in the working tree.
|
|
144
|
+
* @param {string} cwd Repository root.
|
|
145
|
+
* @returns {Record<string, string[]>} Path to exported names.
|
|
146
|
+
*/
|
|
147
|
+
export function currentSurface(cwd = process.cwd()) {
|
|
148
|
+
const surface = {};
|
|
149
|
+
for (const file of shippedScripts(cwd)) {
|
|
150
|
+
const names = exportedNames(readFileSync(path.join(cwd, file), "utf8"));
|
|
151
|
+
if (names.length > 0) surface[file] = names;
|
|
152
|
+
}
|
|
153
|
+
return surface;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Render the artifact.
|
|
158
|
+
* @param {Record<string, string[]>} surface The recorded surface.
|
|
159
|
+
* @returns {string} Artifact JSON, newline-terminated.
|
|
160
|
+
*/
|
|
161
|
+
export function renderArtifact(surface) {
|
|
162
|
+
const total = Object.values(surface).reduce(
|
|
163
|
+
(sum, names) => sum + names.length,
|
|
164
|
+
0
|
|
165
|
+
);
|
|
166
|
+
return `${JSON.stringify(
|
|
167
|
+
{
|
|
168
|
+
$generatedBy: "scripts/generate-export-surface.mjs",
|
|
169
|
+
$doNotEdit:
|
|
170
|
+
"The public export surface this package ships, recorded so a removal is an OBSERVATION rather than an inference from commit-message conventions. This file's diff is what makes a removed export visible in the pull request that causes it (CodySwannGT/lisa#3718).",
|
|
171
|
+
$modules: Object.keys(surface).length,
|
|
172
|
+
$names: total,
|
|
173
|
+
surface,
|
|
174
|
+
},
|
|
175
|
+
null,
|
|
176
|
+
2
|
|
177
|
+
)}\n`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Read the recorded surface out of an artifact's source.
|
|
182
|
+
*
|
|
183
|
+
* Parsed from text rather than imported, so the same reader works on a
|
|
184
|
+
* `git show` of an older revision — which is what comparing against a previous
|
|
185
|
+
* release requires.
|
|
186
|
+
* @param {string} source Artifact source.
|
|
187
|
+
* @returns {Record<string, string[]>} Path to exported names.
|
|
188
|
+
*/
|
|
189
|
+
export function parseArtifact(source) {
|
|
190
|
+
const parsed = JSON.parse(source);
|
|
191
|
+
return parsed.surface ?? {};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Names present in `before` and absent from `after`.
|
|
196
|
+
* @param {Record<string, string[]>} before Earlier surface.
|
|
197
|
+
* @param {Record<string, string[]>} after Later surface.
|
|
198
|
+
* @returns {Array<{file: string, name: string}>} Removals, sorted.
|
|
199
|
+
*/
|
|
200
|
+
export function removedExports(before, after) {
|
|
201
|
+
const gone = [];
|
|
202
|
+
for (const [file, names] of Object.entries(before)) {
|
|
203
|
+
const kept = new Set(after[file] ?? []);
|
|
204
|
+
for (const name of names) if (!kept.has(name)) gone.push({ file, name });
|
|
205
|
+
}
|
|
206
|
+
return gone.sort((left, right) =>
|
|
207
|
+
left.file === right.file
|
|
208
|
+
? left.name < right.name
|
|
209
|
+
? -1
|
|
210
|
+
: 1
|
|
211
|
+
: left.file < right.file
|
|
212
|
+
? -1
|
|
213
|
+
: 1
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* The changelog section a release carries when it removes exports.
|
|
219
|
+
*
|
|
220
|
+
* Emits nothing when nothing was removed — a release that removed no export
|
|
221
|
+
* should not carry a paragraph saying so, or the section becomes noise that
|
|
222
|
+
* readers learn to skip, which is the failure this whole file exists against.
|
|
223
|
+
* @param {Array<{file: string, name: string}>} removals From {@link removedExports}.
|
|
224
|
+
* @returns {string} Markdown, or an empty string.
|
|
225
|
+
*/
|
|
226
|
+
export function changelogSection(removals) {
|
|
227
|
+
if (removals.length === 0) return "";
|
|
228
|
+
const rows = removals
|
|
229
|
+
.map(({ file, name }) => `- \`${name}\` — was exported by \`${file}\``)
|
|
230
|
+
.join("\n");
|
|
231
|
+
return `### ⚠ Removed public exports\n\n${String(removals.length)} exported name(s) present in the previous release are absent from this one. A consumer importing any of them will break on upgrade.\n\n${rows}\n`;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Read the artifact as it stood at a git revision.
|
|
236
|
+
* @param {string} ref Git revision.
|
|
237
|
+
* @param {string} cwd Repository root.
|
|
238
|
+
* @returns {Record<string, string[]>|null} The surface, or null when absent.
|
|
239
|
+
*/
|
|
240
|
+
function surfaceAt(ref, cwd) {
|
|
241
|
+
try {
|
|
242
|
+
const source = execFileSync("git", ["show", `${ref}:${ARTIFACT}`], {
|
|
243
|
+
cwd,
|
|
244
|
+
encoding: "utf8",
|
|
245
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
246
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
247
|
+
});
|
|
248
|
+
return parseArtifact(source);
|
|
249
|
+
} catch {
|
|
250
|
+
// The artifact did not exist at that revision. That is not "no exports
|
|
251
|
+
// were removed" — it is "no comparison is possible", and the two must not
|
|
252
|
+
// render the same, so the caller is told rather than shown an empty list.
|
|
253
|
+
//
|
|
254
|
+
// probe-direction: fail-closed — `main` renders this null as exit 2 and
|
|
255
|
+
// says so in words, which any caller treating non-zero as a block reads
|
|
256
|
+
// as a refusal. An unreadable revision costs a false block, never a
|
|
257
|
+
// release that reports zero removals because it could not look.
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* CLI.
|
|
264
|
+
* @returns {number} Exit code.
|
|
265
|
+
*/
|
|
266
|
+
function main() {
|
|
267
|
+
const cwd = process.cwd();
|
|
268
|
+
const argv = process.argv.slice(2);
|
|
269
|
+
const since = argv.includes("--removed-since")
|
|
270
|
+
? argv[argv.indexOf("--removed-since") + 1]
|
|
271
|
+
: null;
|
|
272
|
+
|
|
273
|
+
if (since) {
|
|
274
|
+
const before = surfaceAt(since, cwd);
|
|
275
|
+
if (before === null) {
|
|
276
|
+
// Exit 2, not 0. "I could not compare" and "nothing was removed" are
|
|
277
|
+
// opposite facts, and this whole file exists because a release reported
|
|
278
|
+
// the second while meaning something closer to the first. A caller that
|
|
279
|
+
// treats any non-zero as a block gets the safe reading by default; one
|
|
280
|
+
// that wants to allow the very first release — before any surface has
|
|
281
|
+
// been recorded — can distinguish 2 from 1 deliberately.
|
|
282
|
+
process.stdout.write(
|
|
283
|
+
`No export surface recorded at ${since}; nothing to compare. This is NOT a clean result — it is an unanswered question.\n`
|
|
284
|
+
);
|
|
285
|
+
return 2;
|
|
286
|
+
}
|
|
287
|
+
const removals = removedExports(before, currentSurface(cwd));
|
|
288
|
+
const section = changelogSection(removals);
|
|
289
|
+
process.stdout.write(
|
|
290
|
+
section === ""
|
|
291
|
+
? `No public exports removed since ${since}. ${String(Object.values(before).reduce((sum, names) => sum + names.length, 0))} names compared.\n`
|
|
292
|
+
: section
|
|
293
|
+
);
|
|
294
|
+
return removals.length > 0 ? 1 : 0;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const rendered = renderArtifact(currentSurface(cwd));
|
|
298
|
+
const target = path.join(cwd, ARTIFACT);
|
|
299
|
+
if (argv.includes("--check")) {
|
|
300
|
+
let existing = "";
|
|
301
|
+
try {
|
|
302
|
+
existing = readFileSync(target, "utf8");
|
|
303
|
+
} catch {
|
|
304
|
+
existing = "";
|
|
305
|
+
}
|
|
306
|
+
if (existing === rendered) return 0;
|
|
307
|
+
process.stderr.write(
|
|
308
|
+
`${ARTIFACT} is stale. Regenerate it in this commit:\n bun run build:export-surface\n`
|
|
309
|
+
);
|
|
310
|
+
return 1;
|
|
311
|
+
}
|
|
312
|
+
writeFileSync(target, rendered);
|
|
313
|
+
return 0;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
317
|
+
process.exit(main());
|
|
318
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure parsing and classification helpers for the shipped-surface removal gate
|
|
3
|
+
* (`scripts/check-shipped-surface-removals.mjs`, CodySwannGT/lisa#3849).
|
|
4
|
+
*
|
|
5
|
+
* Everything here is a total function over strings and plain objects: no git,
|
|
6
|
+
* no filesystem, no clock. The gate keeps its I/O in one file so these can be
|
|
7
|
+
* mutation-tested by hand without a repository fixture, which matters because
|
|
8
|
+
* `stryker.conf.json`'s `mutate` is a ten-file allowlist that a new `scripts/`
|
|
9
|
+
* module does not join - an automated run over this file reports
|
|
10
|
+
* `nothing-to-mutate` and says nothing at all.
|
|
11
|
+
*
|
|
12
|
+
* @module scripts/lib/shipped-surface
|
|
13
|
+
*/
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Extensions a consumer can put behind a `package.json` script entry.
|
|
18
|
+
*
|
|
19
|
+
* The list is deliberately broader than "things Lisa runs itself": the
|
|
20
|
+
* question this gate asks is not what upstream invokes, it is what a HOST
|
|
21
|
+
* could have wired. See `isConsumerBindable`.
|
|
22
|
+
*/
|
|
23
|
+
export const EXECUTABLE_EXTENSIONS = Object.freeze([
|
|
24
|
+
".cjs",
|
|
25
|
+
".js",
|
|
26
|
+
".mjs",
|
|
27
|
+
".py",
|
|
28
|
+
".sh",
|
|
29
|
+
".ts",
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
/** Destination directory whose contents a host routinely names in scripts. */
|
|
33
|
+
export const BINDABLE_ROOT = "scripts/";
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Whether a destination path is one a consumer may have bound into their own
|
|
37
|
+
* `package.json` - and therefore one that must NOT be propagated as a deletion
|
|
38
|
+
* by default (#3849, revised scenario 3).
|
|
39
|
+
*
|
|
40
|
+
* The rule is the ticket's, stated as a question about the host rather than
|
|
41
|
+
* about upstream: not "does upstream still need it" but "could a consumer have
|
|
42
|
+
* wired this into their own scripts?" For an executable under `scripts/` the
|
|
43
|
+
* answer is usually yes, and the reported instance is exactly that shape - an
|
|
44
|
+
* importer with three npm script entries, a workflow, and a test in the host.
|
|
45
|
+
*
|
|
46
|
+
* @param {string} destination - path in the consumer's tree.
|
|
47
|
+
* @returns {boolean} true when a host may have bound it.
|
|
48
|
+
*/
|
|
49
|
+
export function isConsumerBindable(destination) {
|
|
50
|
+
if (!destination.startsWith(BINDABLE_ROOT)) return false;
|
|
51
|
+
return EXECUTABLE_EXTENSIONS.includes(path.posix.extname(destination));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Declaration forms that name exactly one exported binding. */
|
|
55
|
+
const DECLARED_EXPORT_PATTERNS = Object.freeze([
|
|
56
|
+
/^\s*export\s+(?:async\s+)?function\s*\*?\s*([A-Za-z_$][\w$]*)/gm,
|
|
57
|
+
/^\s*export\s+(?:const|let|var)\s+([A-Za-z_$][\w$]*)/gm,
|
|
58
|
+
/^\s*export\s+class\s+([A-Za-z_$][\w$]*)/gm,
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
/** `export { a, b as c }`, with or without a `from` clause. */
|
|
62
|
+
const EXPORT_CLAUSE = /^\s*export\s*\{([^}]*)\}/gm;
|
|
63
|
+
|
|
64
|
+
/** `export * from "./x.mjs"` - a re-export whose names live in the target. */
|
|
65
|
+
const EXPORT_STAR = /^\s*export\s*\*\s*from\s*["']([^"']+)["']/gm;
|
|
66
|
+
|
|
67
|
+
/** `import { a, b as c } from "./x.mjs"` - named imports only. */
|
|
68
|
+
const IMPORT_CLAUSE = /^\s*import\s*\{([^}]*)\}\s*from\s*["']([^"']+)["']/gm;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The exported name a `{ ... }` clause member introduces: the alias when one
|
|
72
|
+
* is written, the original otherwise. `default` members are dropped - a
|
|
73
|
+
* default export is not a named one and the gate never asks about it.
|
|
74
|
+
*
|
|
75
|
+
* @param {string} member - one comma-separated clause member.
|
|
76
|
+
* @returns {string | null} the exported name, or null when there is none.
|
|
77
|
+
*/
|
|
78
|
+
export function clauseExportedName(member) {
|
|
79
|
+
const trimmed = member.trim().replace(/^type\s+/, "");
|
|
80
|
+
if (trimmed === "") return null;
|
|
81
|
+
const parts = trimmed.split(/\s+as\s+/);
|
|
82
|
+
const name = (parts.length > 1 ? parts[1] : parts[0]).trim();
|
|
83
|
+
return name === "" || name === "default" ? null : name;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The imported local binding a `{ ... }` clause member requests from the
|
|
88
|
+
* target module: always the name BEFORE any `as`, because that is the symbol
|
|
89
|
+
* the target must actually export.
|
|
90
|
+
*
|
|
91
|
+
* @param {string} member - one comma-separated clause member.
|
|
92
|
+
* @returns {string | null} the requested export name, or null when there is none.
|
|
93
|
+
*/
|
|
94
|
+
export function clauseImportedName(member) {
|
|
95
|
+
const trimmed = member.trim().replace(/^type\s+/, "");
|
|
96
|
+
if (trimmed === "") return null;
|
|
97
|
+
const name = trimmed.split(/\s+as\s+/)[0].trim();
|
|
98
|
+
return name === "" || name === "default" ? null : name;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Every named export a module source declares directly, plus the relative
|
|
103
|
+
* specifiers it re-exports wholesale.
|
|
104
|
+
*
|
|
105
|
+
* `export *` is returned rather than resolved: resolution needs the delivery
|
|
106
|
+
* view, which lives in the gate. Doing it here would make this module read the
|
|
107
|
+
* filesystem.
|
|
108
|
+
*
|
|
109
|
+
* @param {string} source - module source text.
|
|
110
|
+
* @returns {{ names: Set<string>, starFrom: string[] }} declared names and
|
|
111
|
+
* the relative specifiers whose names are inherited.
|
|
112
|
+
*/
|
|
113
|
+
export function parseNamedExports(source) {
|
|
114
|
+
const names = new Set();
|
|
115
|
+
for (const pattern of DECLARED_EXPORT_PATTERNS) {
|
|
116
|
+
for (const match of source.matchAll(pattern)) names.add(match[1]);
|
|
117
|
+
}
|
|
118
|
+
for (const match of source.matchAll(EXPORT_CLAUSE)) {
|
|
119
|
+
for (const member of match[1].split(",")) {
|
|
120
|
+
const name = clauseExportedName(member);
|
|
121
|
+
if (name !== null) names.add(name);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const starFrom = [...source.matchAll(EXPORT_STAR)]
|
|
125
|
+
.map(match => match[1])
|
|
126
|
+
.filter(specifier => specifier.startsWith("."));
|
|
127
|
+
return { names, starFrom };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Every named import a module source requests from a RELATIVE specifier.
|
|
132
|
+
*
|
|
133
|
+
* Bare specifiers are skipped: they resolve through `node_modules` in the
|
|
134
|
+
* consumer's tree, which this repository cannot see and does not govern.
|
|
135
|
+
*
|
|
136
|
+
* @param {string} source - module source text.
|
|
137
|
+
* @returns {Array<{ specifier: string, names: string[] }>} one entry per
|
|
138
|
+
* relative named-import statement, in source order.
|
|
139
|
+
*/
|
|
140
|
+
export function parseRelativeNamedImports(source) {
|
|
141
|
+
const requests = [];
|
|
142
|
+
for (const match of source.matchAll(IMPORT_CLAUSE)) {
|
|
143
|
+
const specifier = match[2];
|
|
144
|
+
if (!specifier.startsWith(".")) continue;
|
|
145
|
+
const names = match[1]
|
|
146
|
+
.split(",")
|
|
147
|
+
.map(member => clauseImportedName(member))
|
|
148
|
+
.filter(name => name !== null);
|
|
149
|
+
if (names.length > 0) requests.push({ names, specifier });
|
|
150
|
+
}
|
|
151
|
+
return requests;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* The destination a relative specifier resolves to, given the destination
|
|
156
|
+
* path of the importing module.
|
|
157
|
+
*
|
|
158
|
+
* @param {string} importerDestination - the importer's path in the consumer tree.
|
|
159
|
+
* @param {string} specifier - a relative specifier from that importer.
|
|
160
|
+
* @returns {string} the resolved destination path.
|
|
161
|
+
*/
|
|
162
|
+
export function resolveRelative(importerDestination, specifier) {
|
|
163
|
+
return path.posix.normalize(
|
|
164
|
+
path.posix.join(path.posix.dirname(importerDestination), specifier)
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* The ledger key for one governed surface.
|
|
170
|
+
*
|
|
171
|
+
* A whole-file removal is keyed by path alone; an export removal is keyed
|
|
172
|
+
* `path symbol`. A shipped path never contains a space, so the two key spaces
|
|
173
|
+
* cannot collide.
|
|
174
|
+
*
|
|
175
|
+
* @param {string} shippedPath - repo-relative path inside a delivery lane.
|
|
176
|
+
* @param {string} [symbol] - the exported name, when a symbol rather than a file.
|
|
177
|
+
* @returns {string} the key.
|
|
178
|
+
*/
|
|
179
|
+
export function removalKey(shippedPath, symbol) {
|
|
180
|
+
return symbol === undefined ? shippedPath : `${shippedPath} ${symbol}`;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Index a removal ledger by the surface each entry governs.
|
|
185
|
+
*
|
|
186
|
+
* @param {ReadonlyArray<{ path: string, export?: string, note?: string }>} removals
|
|
187
|
+
* ledger entries.
|
|
188
|
+
* @returns {Map<string, { path: string, export?: string, note?: string }>} the index.
|
|
189
|
+
*/
|
|
190
|
+
export function indexRemovals(removals) {
|
|
191
|
+
return new Map(
|
|
192
|
+
removals.map(entry => [removalKey(entry.path, entry.export), entry])
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Whether a ledger entry actually records something an operator can act on.
|
|
198
|
+
*
|
|
199
|
+
* An entry with a blank note is treated as absent. A note that says nothing is
|
|
200
|
+
* the same silence the gate exists to break, and accepting it would let the
|
|
201
|
+
* ledger be satisfied by a placeholder.
|
|
202
|
+
*
|
|
203
|
+
* @param {{ note?: unknown } | undefined} entry - the indexed ledger entry.
|
|
204
|
+
* @returns {boolean} true when the entry carries a usable note.
|
|
205
|
+
*/
|
|
206
|
+
export function hasUsableNote(entry) {
|
|
207
|
+
return (
|
|
208
|
+
entry !== undefined &&
|
|
209
|
+
typeof entry.note === "string" &&
|
|
210
|
+
entry.note.trim() !== ""
|
|
211
|
+
);
|
|
212
|
+
}
|