@codyswann/lisa 4.49.0 → 4.50.1
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 +121 -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 +110 -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
|
@@ -1,5 +1,60 @@
|
|
|
1
1
|
# Reset/Seed Coverage & State Classification
|
|
2
2
|
|
|
3
|
+
> Demoted from the always-on eager tier by CodySwannGT/lisa#3992. The
|
|
4
|
+
> section below is the former eager head, preserved verbatim; the full
|
|
5
|
+
> contract follows it. Reachable on demand via `rules/eager/00-rule-index.md`.
|
|
6
|
+
|
|
7
|
+
## Reset/Seed Coverage & State Classification (load-bearing)
|
|
8
|
+
|
|
9
|
+
**Every persistent entity a project owns carries exactly one declared reset policy, and a work item that adds or changes persistent state is not done until the reset/seed contract covers it.** An entity the running system holds but the contract does not classify is a contract violation, and the check that finds it **fails closed** — an unclassified entity is never treated as safe to keep and never treated as safe to delete.
|
|
10
|
+
|
|
11
|
+
**One vendor-neutral contract, cited by** `lisa-research`, `lisa-acceptance-criteria`, `lisa-task-decomposition`, `lisa-test-strategy`, `lisa-implement`, `lisa-codify-verification`, `lisa-verification-lifecycle`, and `lisa-verify` (the `leaf-only-lifecycle` / `repo-scope-split` precedent: one shared slug, never divergent per-skill prose). It never names a database engine, cloud, identity provider, or test runner — those are project configuration.
|
|
12
|
+
|
|
13
|
+
## Why this exists
|
|
14
|
+
|
|
15
|
+
Test suites create state. Without a contract, cleanup is a habit: a flow creates a uniquely-marked record and deletes it on the happy path only, so every early failure leaks one more. Nothing sweeps it, nothing complains, and the suite degrades until someone reads a flake as a product bug. The observed shape is always the same — leaked records accumulate silently until a list view, a uniqueness constraint, or a count assertion breaks. Per-flow self-cleanup is not coverage; it is the failure mode.
|
|
16
|
+
|
|
17
|
+
## Membership
|
|
18
|
+
|
|
19
|
+
Membership is **state, not repo name, ticket label, or storage engine**: an entity is in scope the moment something the project writes **outlives the process that wrote it**. That includes rows and tables, but rows are only one kind of state. Also in scope: identity-provider objects (accounts, groups, memberships, sessions), object storage (buckets, prefixes, uploaded files), search indexes, queues and topics (in-flight and dead-letter), caches with a persistence tier, materialized/derived views and projections, feature-flag and configuration overrides, scheduled jobs created at runtime, and analytics or third-party side effects a run leaves behind. Out of scope: values that live only in memory for the duration of one process.
|
|
20
|
+
|
|
21
|
+
A work item is in scope when it **adds** such an entity, **changes what owns or writes** one, or **changes the lifetime** of one. Renames count as changes, not as new entities plus deletions.
|
|
22
|
+
|
|
23
|
+
## The four policies
|
|
24
|
+
|
|
25
|
+
Every entity is classified as exactly one of:
|
|
26
|
+
|
|
27
|
+
- **`fixture-owned`** — the reset may create, mutate, and delete rows here, but only those it owns. Ownership is declared as a predicate (a reserved id shape, a reserved account or namespace, a marker attribute), never as "everything in this entity."
|
|
28
|
+
- **`preserve`** — the reset must leave this untouched. Catalog, reference data, anything a migration seeds, anything expensive or impossible to rebuild, and any state belonging to someone who is not the fixture.
|
|
29
|
+
- **`derived-rebuild`** — not authored by anyone; recomputed from its sources after the reset (projections, materialized views, search indexes, caches). The reset rebuilds it rather than clearing or preserving it, and proves it converged.
|
|
30
|
+
- **`forbidden`** — the reset must be structurally unable to touch it: ledgers, payments, wallets, withdrawals, audit trails, anything irreversible. Declaring `forbidden` obliges naming the enforcement outside the script — a least-privilege role, a revoked grant, a separate account or credential boundary — because a script-only promise dies in the refactor that drops the safe caller.
|
|
31
|
+
|
|
32
|
+
**Classify by provenance, never by observation.** "The table looked empty" is not evidence it is unused; runtime statistics are approximations, and a table that has never been analyzed reports rows it holds. Ask who writes it and whether anything can put it back.
|
|
33
|
+
|
|
34
|
+
## Keep-lists are a detector, not the safety model
|
|
35
|
+
|
|
36
|
+
Deriving the clear-list by subtracting a keep-list from a schema listing is a **useful detector** — run it, and let it flag entities the contract has not seen. It is not the boundary. It cannot model renames, multiple schemas, framework-generated entities, views, partitions, row-level ownership, or any of the non-DB state above, and "new entities are cleared unless exempted" erases unrelated data the first time something adds one. The boundary is the classification; subtraction only tells you the classification is stale.
|
|
37
|
+
|
|
38
|
+
## Required assurances
|
|
39
|
+
|
|
40
|
+
A reset contract is not complete until it declares, and points at evidence for, all of: non-fixture and catalog data survive; reserved fixture identifiers are rejected on collision with anything real; foreign references into preserved entities are refused rather than cascaded; every write is **acknowledged** (a mutation reported by the caller and not confirmed by the system is a failure, not a success); a second apply converges to the same state and reports no further change; the post-state is verified by **exact expected counts**, not "at least one"; and production fails closed with no override of any kind. Where the platform can enforce a boundary with roles, grants, or constraints, it does — the in-process guard remains as defense in depth, never as the primary control.
|
|
41
|
+
|
|
42
|
+
## Definition of done
|
|
43
|
+
|
|
44
|
+
A work item that adds or changes persistent state is not done until: (1) every entity it introduces or changes is classified in the project's state contract with a reason and an owner; (2) `fixture-owned` entities it introduces declare their ownership predicate **and** are actually swept by the reset — an entity a suite creates but nothing removes is the leak this rule exists to prevent; (3) `preserve`/`forbidden` entities it introduces name their enforcement; (4) any seed the suite depends on covers the new state, with the verify step asserting exact counts; and (5) the state-classification check passes in the same PR. **A missing or stale classification is a verification failure, not a warning** — it blocks completion exactly as a `PARTIAL` spec-conformance verdict does, and is never demoted to "optional", "if cheap", or a follow-up without a linked build-ready ticket created before merge.
|
|
45
|
+
|
|
46
|
+
## Bootstrap, noop, and degradation
|
|
47
|
+
|
|
48
|
+
A project with no state contract yet is not exempt: the first work item touching persistent state scaffolds the minimum (the contract file, its own entities classified, the check wired into CI) and covers **its own** state. It never backfills the whole system — pre-existing unclassified entities are recorded as burndown with an owner and a date, not treated as this item's blocker. A project that genuinely holds no persistent state declares a **machine-readable noop** (`mode: "declared-noop"` with reason, owner, and a capability-manifest reference) which the check verifies against the repo rather than taking on faith — a bare exit 0 is indistinguishable from a successful destructive run and never satisfies this rule. A project that cannot produce a runtime inventory records the checked sources and the absence, exactly as the regression-spec absence path in `verification` does; a silent skip is never one of the exits.
|
|
49
|
+
|
|
50
|
+
## Command envelope
|
|
51
|
+
|
|
52
|
+
Every reset, seed, verify, inventory, and classification adapter answers the **same interface** — one validated JSON result on stdout, exit 0 only when the operation both completed **and** verified, `--dry-run` mandatory before anything destructive, and a requested stage always re-checked against server-resolved identity rather than trusted. The envelope is specified in the reference body and is what makes "every repo answers the same question the same way" checkable instead of aspirational.
|
|
53
|
+
|
|
54
|
+
**The production arm of this is executable, not advisory.** `scripts/lisa-destructive-guard.mjs` ships to every adopter and is wired into the envelope itself: a destructive run against a production-resolved — or unresolvable — environment has **no representable success envelope**, so it can never exit 0, and there is no parameter, field, or environment variable anywhere in the guard that changes the answer. A dry run is not an exemption, and `--dry-run` is the default rather than an opt-in. Read this as what it is: a check at the one interface every adapter passes through, which an adapter that misreports its own environment still defeats. The end state is a destructive capability that is **not deployed to production at all** — non-existence beats refusal — and that design, with the failure modes it must still close, is recorded in `docs/design/reset-production-absence.md`.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
3
58
|
Automated suites create state, and the state outlives them. The historical pattern is per-flow
|
|
4
59
|
self-cleanup: a flow creates a uniquely-marked record at the top and deletes it as its last step, so
|
|
5
60
|
the record survives every failure that happens in between. Nothing sweeps the survivors and nothing
|
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# Usage Accounting
|
|
2
2
|
|
|
3
|
+
> Demoted from the always-on eager tier by CodySwannGT/lisa#3992. The
|
|
4
|
+
> section below is the former eager head, preserved verbatim; the full
|
|
5
|
+
> contract follows it. Reachable on demand via `rules/eager/00-rule-index.md`.
|
|
6
|
+
|
|
7
|
+
## Usage Accounting (load-bearing)
|
|
8
|
+
|
|
9
|
+
Lisa attaches AI usage and cost telemetry to every artifact it creates/updates. The format is a single canonical managed section.
|
|
10
|
+
|
|
11
|
+
## Managed section
|
|
12
|
+
|
|
13
|
+
Every artifact with inline body content gets exactly one section:
|
|
14
|
+
|
|
15
|
+
```markdown
|
|
16
|
+
## Lisa Usage
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Canonical. Rewrite in place; never append a second usage section.** If the host can't safely edit body, write the same section in a comment and treat that comment as the managed artifact for future rewrites.
|
|
20
|
+
|
|
21
|
+
## Required field semantics
|
|
22
|
+
|
|
23
|
+
Each direct entry records ONE logical Lisa run on ONE artifact. `entry_id` is the stable dedupe key — rewriting the same logical run with the same `entry_id` updates in place; a different run gets a different `entry_id`.
|
|
24
|
+
|
|
25
|
+
- **`source`**: `observed` (runtime supplied) / `estimated` (derived from trustworthy metadata + pricing contract) / `measured-subset` (a trustworthy subtotal exists, but the complete run total is unknown) / `unavailable`.
|
|
26
|
+
- **`measured_subset_tokens`**: optional measured subtotal for `measured-subset` entries only. Omission is normalized to `null` for backward-compatible callers. Keep `total_tokens = null` so rollups do not treat a subset as a complete total.
|
|
27
|
+
- **`pricing_status`**: same trinary plus `missing` (cost not known but should be).
|
|
28
|
+
- **Absence ≠ zero.** `null` means unknown; `0` means explicitly zero. Always write the entry — never silently omit.
|
|
29
|
+
- Do NOT replace observed counts with estimates.
|
|
30
|
+
|
|
31
|
+
## Rollup
|
|
32
|
+
|
|
33
|
+
Container artifacts (Epic, PRD, etc.) roll up usage from their direct children. Roll-up is recursive — a parent's `## Lisa Usage` aggregates its descendants' direct entries. Re-writes are idempotent: re-running an intake or lifecycle skill must not duplicate entries.
|
|
34
|
+
|
|
35
|
+
Measured-child incompleteness is durable across ordinary rewrites through the optional
|
|
36
|
+
`lisa:usage-rollup-token-status` extension. Do not infer completeness merely because a rewrite did
|
|
37
|
+
not re-fetch child ledgers.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
3
41
|
Lisa usage accounting is a vendor-neutral contract for attaching AI usage and cost telemetry to the artifacts Lisa creates or updates. It governs the section shape, machine-readable tokens, source/pricing semantics, rollup behavior, and idempotent rewrite rules. Writer skills and lifecycle skills attach telemetry by following this contract; they do not invent artifact-local formats.
|
|
4
42
|
|
|
5
43
|
## Managed section
|
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# Work-Item Definition of Ready — Reference
|
|
2
2
|
|
|
3
|
+
> Demoted from the always-on eager tier by CodySwannGT/lisa#3992. The
|
|
4
|
+
> section below is the former eager head, preserved verbatim; the full
|
|
5
|
+
> contract follows it. Reachable on demand via `rules/eager/00-rule-index.md`.
|
|
6
|
+
|
|
7
|
+
## Work-Item Definition of Ready (type-keyed)
|
|
8
|
+
|
|
9
|
+
**`ready` means: a stateless agent can claim this item and drive it to its terminal state with zero human clarification.** Every readiness gate exists because its absence forces a question; the bar is type-keyed because different work types break autonomy differently.
|
|
10
|
+
|
|
11
|
+
## Shared core (every leaf, enforced by `*-validate-*` S1–S16)
|
|
12
|
+
|
|
13
|
+
Three-audience description · Gherkin acceptance criteria (except Spike) · Out of Scope · single-repo scope · relationship search · typed evidence manifest on runtime changes · Source Requirement traceability when PRD-sourced · provable read access to every named external surface · declared reachability for any **existing** test the item pins as a red-before-green control (`control-reachability`, gate S20 — `N/A` when the item introduces a new test instead).
|
|
14
|
+
|
|
15
|
+
## Per-type additions
|
|
16
|
+
|
|
17
|
+
| Type | Must additionally carry | Terminal state is |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| **Bug** | agent-executable reproduction (or a failing test) · expected-vs-actual with the *source* of "expected" · environment + version observed, last-known-good when known · reproducibility rate (always / intermittent + frequency) · at least one occurrence evidence link | the same repro/test fails before the fix and passes after |
|
|
20
|
+
| **Story** | user-visible AC; SHOULD name non-happy-path states (error / empty / loading) for UI stories · design source precedence when artifacts exist | every AC exercised on the running product |
|
|
21
|
+
| **Task** | an observable done-state and the command/check that proves it | the verification check passes |
|
|
22
|
+
| **Improvement** | metric + measurement method the agent can run · measured baseline value · numeric target | measured value meets the target |
|
|
23
|
+
| **Spike** | the question · the decision it enables + options weighed · a timebox · deliverable format **and location** | a deliverable at the named location that answers the question — findings, decision, options weighed |
|
|
24
|
+
| **Sub-task** | a parent, always · otherwise the bar of its own type | per its type |
|
|
25
|
+
| **Epic / container** | never build-ready (`leaf-only-lifecycle`) · judged on decomposition completeness, not buildability | all required children terminal |
|
|
26
|
+
|
|
27
|
+
## The stateless-pickup test (S18)
|
|
28
|
+
|
|
29
|
+
The final gate checks the property itself instead of proxies for it: read the item as a fresh agent with no session context, using only the item and what it links to. List every question you would have to ask a human before starting, choosing between materially different implementations, or declaring done. **Ready = zero questions.** Each question found is the FAIL remediation, verbatim.
|
|
30
|
+
|
|
31
|
+
Sources this bar distills: INVEST (stories) · ISTQB / IEEE 1044 defect anatomy and minimal-reproducible-example practice (bugs) · SMART (improvements) · Scrum's Definition of Ready (the framing) · TASC AC8.6 (the normative criterion).
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
3
35
|
The eager head carries the table; this reference carries the reasoning, the
|
|
4
36
|
failure modes each requirement prevents, and worked examples. The enforcing
|
|
5
37
|
gates live in the three tracker validators (`lisa-jira-validate-ticket`,
|
|
@@ -37,6 +37,143 @@ function normalizeLabels(labels) {
|
|
|
37
37
|
.filter(label => label.length > 0);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Decoration a declaration may sit behind, and nothing more.
|
|
42
|
+
*
|
|
43
|
+
* Whitespace, blockquote arrows, list bullets or numbers, emphasis, a comment
|
|
44
|
+
* hash, and an opening HTML comment. `#` is there on EVIDENCE rather than
|
|
45
|
+
* taste: a shell script declares a hold as `# <marker> reason=…` — a bare
|
|
46
|
+
* marker behind a SHELL comment, with no HTML comment anywhere — and that form
|
|
47
|
+
* is in this repository's own guard fixtures. A single character class plus two
|
|
48
|
+
* optional groups —
|
|
49
|
+
* deliberately not a nested-quantifier pattern, because this runs over
|
|
50
|
+
* untrusted work-item bodies and `sonarjs/slow-regex` is right about that
|
|
51
|
+
* shape.
|
|
52
|
+
*/
|
|
53
|
+
const LEADING_DECORATION =
|
|
54
|
+
/^[ \t>*_+#-]*(?:\d+[.)][ \t]*)?(?:<!--[ \t]*)?[ \t]*/u;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The body with fenced blocks and inline code spans removed.
|
|
58
|
+
*
|
|
59
|
+
* Documentation of a declaration is not a declaration, and a fenced example is
|
|
60
|
+
* how the declaration form gets written ABOUT. The same move
|
|
61
|
+
* `pr-arming-sweep.mjs` makes for the auto-merge-off marker
|
|
62
|
+
* (CodySwannGT/lisa#3986), and the same one `block-direct-issue-create.sh`
|
|
63
|
+
* makes when it strips heredoc bodies before tokenising argv.
|
|
64
|
+
* @param {unknown} body - The raw item body
|
|
65
|
+
* @returns {string} The body with quoted and code regions blanked out
|
|
66
|
+
*/
|
|
67
|
+
function declarativeText(body) {
|
|
68
|
+
return String(body ?? "")
|
|
69
|
+
.replace(/```[\s\S]*?```/gu, "")
|
|
70
|
+
.replace(/`[^`\n]*`/gu, "");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Whether an HTML comment on this line carries the marker.
|
|
75
|
+
*
|
|
76
|
+
* The second declaration form, and it is not a concession: an HTML comment is
|
|
77
|
+
* invisible when the body is rendered, so nobody writes one to TALK about the
|
|
78
|
+
* marker — a quotation has to be visible to be a quotation. Prose about the
|
|
79
|
+
* marker is written in sentences, code spans and fenced examples, all of which
|
|
80
|
+
* this rule already excludes.
|
|
81
|
+
*
|
|
82
|
+
* Scanned rather than pattern-matched. A regular expression with two lazy
|
|
83
|
+
* `[^\n]*?` runs around a literal is the ambiguous shape this module avoids
|
|
84
|
+
* elsewhere, and an indexOf walk is both linear and easier to be sure about.
|
|
85
|
+
* @param {string} line - One line of the body
|
|
86
|
+
* @returns {boolean} True when a comment on this line contains the marker
|
|
87
|
+
*/
|
|
88
|
+
function commentCarriesMarker(line) {
|
|
89
|
+
const segments = line.split("<!--");
|
|
90
|
+
for (let index = 1; index < segments.length; index += 1) {
|
|
91
|
+
const close = segments[index].indexOf("-->");
|
|
92
|
+
const inside =
|
|
93
|
+
close === -1 ? segments[index] : segments[index].slice(0, close);
|
|
94
|
+
if (inside.includes(HUMAN_GATE_MARKER)) return true;
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Whether one line DECLARES a hold rather than mentioning one.
|
|
101
|
+
*
|
|
102
|
+
* Two accepted forms, and the corpus says they cost nothing to combine:
|
|
103
|
+
* measured over this repository's 42 matching bodies, line-leading alone and
|
|
104
|
+
* line-leading-or-comment hold the SAME 29. The comment branch exists because
|
|
105
|
+
* the filing guard reads inputs that are not markdown documents — a one-line
|
|
106
|
+
* `--body` string and a shell script — where a declaration legitimately sits
|
|
107
|
+
* after other text on its line.
|
|
108
|
+
* @param {string} line - One line of the body
|
|
109
|
+
* @returns {boolean} True when the line declares a hold
|
|
110
|
+
*/
|
|
111
|
+
function declaresOnLine(line) {
|
|
112
|
+
if (line.replace(LEADING_DECORATION, "").startsWith(HUMAN_GATE_MARKER)) {
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
return commentCarriesMarker(line);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Count what a body says about the hold marker, split by what it MEANS.
|
|
120
|
+
*
|
|
121
|
+
* ## Why the substring test had to go
|
|
122
|
+
*
|
|
123
|
+
* `isHumanGated` asked one question — does the body contain the marker
|
|
124
|
+
* anywhere — and that form was deliberate, documented, and correct when it was
|
|
125
|
+
* written: *"it is a marker with no other meaning, so its presence IS the
|
|
126
|
+
* declaration."* The premise expired. The marker acquired a second meaning —
|
|
127
|
+
* **being discussed** — the moment the feature became something people file
|
|
128
|
+
* tickets about, so a sentence quoting it, a Gherkin line naming it, and a
|
|
129
|
+
* ticket about it all read as declared holds (CodySwannGT/lisa#3815).
|
|
130
|
+
*
|
|
131
|
+
* The consequence is not one skipped cycle. `planHumanGateReconciliation`
|
|
132
|
+
* removes the ready role and applies the human-needed label, so a false
|
|
133
|
+
* positive rewrites the item's own metadata and every later sweep agrees with
|
|
134
|
+
* the first. And the affected population GROWS with the documentation: 38
|
|
135
|
+
* matching bodies measured 2026-09-04, 42 on 2026-09-05.
|
|
136
|
+
*
|
|
137
|
+
* ## The rule
|
|
138
|
+
*
|
|
139
|
+
* A declaration is POSITIONAL — the marker leads its line, behind at most
|
|
140
|
+
* blockquote, list, emphasis or opening-comment decoration — evaluated after
|
|
141
|
+
* code regions are removed. Measured over this repository's 42 matching bodies:
|
|
142
|
+
* this holds 29 of them and correctly releases the two live ready-lane items
|
|
143
|
+
* that only discussed the marker.
|
|
144
|
+
*
|
|
145
|
+
* The stricter HTML-comment-only rule was measured and REJECTED: it holds only
|
|
146
|
+
* 18, dropping declarations that carry `human-needed` today. Losing a real hold
|
|
147
|
+
* is the unsafe direction and would need a retrofit of the tracker.
|
|
148
|
+
*
|
|
149
|
+
* ## Why it counts rather than answering yes or no
|
|
150
|
+
*
|
|
151
|
+
* A heuristic that silently drops candidates is indistinguishable from one that
|
|
152
|
+
* found none. Every occurrence this rule declines to honour is counted and
|
|
153
|
+
* reported, so "the rule demoted 33 mentions" is a fact an operator can read
|
|
154
|
+
* rather than an absence they must infer.
|
|
155
|
+
* @param {unknown} body - The item body
|
|
156
|
+
* @returns {{ total: number, declared: number, demoted: number }} Occurrences,
|
|
157
|
+
* those that declare a hold, and those demoted to mentions
|
|
158
|
+
*/
|
|
159
|
+
export function humanGateMentions(body) {
|
|
160
|
+
const raw = String(body ?? "");
|
|
161
|
+
const total = raw.split(HUMAN_GATE_MARKER).length - 1;
|
|
162
|
+
const declared = declarativeText(raw)
|
|
163
|
+
.split("\n")
|
|
164
|
+
.filter(line => declaresOnLine(line)).length;
|
|
165
|
+
return { total, declared, demoted: total - declared };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Whether a body declares a hold, as opposed to discussing one.
|
|
170
|
+
* @param {unknown} body - The item body
|
|
171
|
+
* @returns {boolean} True when at least one line declares a hold
|
|
172
|
+
*/
|
|
173
|
+
export function bodyDeclaresHold(body) {
|
|
174
|
+
return humanGateMentions(body).declared > 0;
|
|
175
|
+
}
|
|
176
|
+
|
|
40
177
|
/**
|
|
41
178
|
* Whether an item is held for a person, on either surface.
|
|
42
179
|
*
|
|
@@ -64,7 +201,7 @@ export function isHumanGated(input) {
|
|
|
64
201
|
return true;
|
|
65
202
|
}
|
|
66
203
|
|
|
67
|
-
return
|
|
204
|
+
return bodyDeclaresHold(input.body);
|
|
68
205
|
}
|
|
69
206
|
|
|
70
207
|
/**
|
|
@@ -432,6 +569,26 @@ export function formatHumanGateNote() {
|
|
|
432
569
|
* @param {readonly unknown[]} held - Item references held this cycle
|
|
433
570
|
* @returns {string} One summary line
|
|
434
571
|
*/
|
|
572
|
+
/**
|
|
573
|
+
* Render the cycle-summary line naming what the precision rule DEMOTED.
|
|
574
|
+
*
|
|
575
|
+
* The counterpart of {@link summarizeHumanGateHolds}, and it exists for the
|
|
576
|
+
* same reason: a mutation nobody can see afterwards is the problem this whole
|
|
577
|
+
* area keeps having. A rule that quietly declines to honour half the marker
|
|
578
|
+
* occurrences it sees reads exactly like a rule that saw none, so the count is
|
|
579
|
+
* printed even when it is zero — measured across this repository's matching
|
|
580
|
+
* bodies, 33 of 62 occurrences are mentions rather than declarations.
|
|
581
|
+
* @param {unknown} demoted - How many occurrences were mentions, not declarations
|
|
582
|
+
* @returns {string} One summary line
|
|
583
|
+
*/
|
|
584
|
+
export function summarizeHumanGateMentions(demoted = 0) {
|
|
585
|
+
const count = Number.isFinite(Number(demoted))
|
|
586
|
+
? Math.trunc(Number(demoted))
|
|
587
|
+
: 0;
|
|
588
|
+
const shown = count > 0 ? String(count) : "none";
|
|
589
|
+
return `Marker mentions demoted (not declarations): ${shown}.`;
|
|
590
|
+
}
|
|
591
|
+
|
|
435
592
|
export function summarizeHumanGateHolds(held = []) {
|
|
436
593
|
const names = (Array.isArray(held) ? held : [])
|
|
437
594
|
.map(item => trimmedString(item))
|
|
@@ -1136,10 +1136,26 @@ not there, and a lifecycle step performed by hand is one nothing can verify
|
|
|
1136
1136
|
happened.
|
|
1137
1137
|
|
|
1138
1138
|
**The backstop, for the ones that still slip:** `lisa-work-item.mjs sweep`
|
|
1139
|
-
reports every
|
|
1139
|
+
reports every open item — in the **ready** lane as well as the **claimed** one —
|
|
1140
|
+
that a commit on a deploy branch DECLARES via its `Work-Item:` trailer, and
|
|
1140
1141
|
`--apply` completes them. Reporting is the default deliberately — a sweep that
|
|
1141
1142
|
closes things as a side effect of being run is not one anyone runs twice.
|
|
1142
1143
|
|
|
1144
|
+
You do not need to run it. The `🧹 Lifecycle Drift Sweep` workflow runs it
|
|
1145
|
+
daily and files a deduplicated issue when drift exists, because a report with
|
|
1146
|
+
no consumer and no report are the same thing with different amounts of code.
|
|
1147
|
+
|
|
1148
|
+
Two properties worth knowing, because both were once the other way round:
|
|
1149
|
+
|
|
1150
|
+
- **Credit requires a declaration, never a mention.** Evidence is a commit
|
|
1151
|
+
reachable from a deploy branch whose own trailer names the item. It used to
|
|
1152
|
+
be a timeline cross-reference, which any pull request that so much as *named*
|
|
1153
|
+
`#N` created — so one merged land-stack pull request made every sibling in
|
|
1154
|
+
its batch look shipped. Measured: 8 of 53 reported items had no commit
|
|
1155
|
+
declaring them, and every one of the 8 had live work open against it.
|
|
1156
|
+
- **A clean result names the lanes it examined.** "No drift" over a subject
|
|
1157
|
+
list that excluded the ready lane was true and unusable.
|
|
1158
|
+
|
|
1143
1159
|
## 4. Terminal states
|
|
1144
1160
|
|
|
1145
1161
|
Loop until one of:
|
|
@@ -332,6 +332,10 @@ other gate's verdict — however conclusive — may promote an item a person par
|
|
|
332
332
|
4. **Name it in the cycle summary** via `summarizeHumanGateHolds([...])`, so the record
|
|
333
333
|
distinguishes "nothing was eligible" from "something eligible was held for a person". A lane
|
|
334
334
|
mutation nobody can see afterwards is the same class of problem this gate exists to fix.
|
|
335
|
+
Report alongside it what the precision rule SKIPPED, via `summarizeHumanGateMentions(n)`
|
|
336
|
+
— the marker occurrences that were mentions rather than declarations (CodySwannGT/lisa#3815).
|
|
337
|
+
A rule that quietly declines to honour half the occurrences it sees reads exactly like a
|
|
338
|
+
rule that saw none, so the count is printed even when it is zero.
|
|
335
339
|
5. **Continue to the next candidate.** A held item does not end the cycle.
|
|
336
340
|
|
|
337
341
|
|
|
@@ -237,6 +237,10 @@ other gate's verdict — however conclusive — may promote an item a person par
|
|
|
237
237
|
4. **Name it in the cycle summary** via `summarizeHumanGateHolds([...])`, so the record
|
|
238
238
|
distinguishes "nothing was eligible" from "something eligible was held for a person". A lane
|
|
239
239
|
mutation nobody can see afterwards is the same class of problem this gate exists to fix.
|
|
240
|
+
Report alongside it what the precision rule SKIPPED, via `summarizeHumanGateMentions(n)`
|
|
241
|
+
— the marker occurrences that were mentions rather than declarations (CodySwannGT/lisa#3815).
|
|
242
|
+
A rule that quietly declines to honour half the occurrences it sees reads exactly like a
|
|
243
|
+
rule that saw none, so the count is printed even when it is zero.
|
|
240
244
|
5. **Continue to the next candidate.** A held item does not end the cycle.
|
|
241
245
|
|
|
242
246
|
|
|
@@ -231,6 +231,10 @@ other gate's verdict — however conclusive — may promote an item a person par
|
|
|
231
231
|
4. **Name it in the cycle summary** via `summarizeHumanGateHolds([...])`, so the record
|
|
232
232
|
distinguishes "nothing was eligible" from "something eligible was held for a person". A lane
|
|
233
233
|
mutation nobody can see afterwards is the same class of problem this gate exists to fix.
|
|
234
|
+
Report alongside it what the precision rule SKIPPED, via `summarizeHumanGateMentions(n)`
|
|
235
|
+
— the marker occurrences that were mentions rather than declarations (CodySwannGT/lisa#3815).
|
|
236
|
+
A rule that quietly declines to honour half the occurrences it sees reads exactly like a
|
|
237
|
+
rule that saw none, so the count is printed even when it is zero.
|
|
234
238
|
5. **Continue to the next candidate.** A held item does not end the cycle.
|
|
235
239
|
|
|
236
240
|
|
|
@@ -994,7 +994,10 @@ with labels like `build-ready`, or with no Lisa status label at all, that are in
|
|
|
994
994
|
instead — it says that nothing was changed and how a person resumes the item, which is the
|
|
995
995
|
opposite of what the reconciliation note says and must not be substituted for it. A promotion
|
|
996
996
|
declined silently is invisible, which is the quieter version of the failure this step exists to
|
|
997
|
-
prevent, so name every held item in the cycle summary via `summarizeHumanGateHolds([...])`.
|
|
997
|
+
prevent, so name every held item in the cycle summary via `summarizeHumanGateHolds([...])`. Report what the
|
|
998
|
+
precision rule skipped beside it via `summarizeHumanGateMentions(n)`: the marker
|
|
999
|
+
occurrences that were mentions rather than declarations (CodySwannGT/lisa#3815),
|
|
1000
|
+
because a rule that silently drops candidates reads like one that found none.
|
|
998
1001
|
|
|
999
1002
|
### Ungated non-ready filing → surface as an incomplete handoff (read-only)
|
|
1000
1003
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# This file is managed by Lisa and IS replaced on each `lisa` run.
|
|
2
|
+
# Do not edit directly — durable changes belong upstream in Lisa.
|
|
3
|
+
|
|
4
|
+
id: no-day-truncating-file-age-predicate
|
|
5
|
+
language: bash
|
|
6
|
+
severity: error
|
|
7
|
+
message: |
|
|
8
|
+
`-mtime`, `-atime` and `-ctime` do NOT mean "older than N days".
|
|
9
|
+
|
|
10
|
+
`find` divides the file's age by 86400 and DISCARDS the remainder, so
|
|
11
|
+
`-mtime +N` matches only files older than N+1 days. Measured on this
|
|
12
|
+
repository's own platform with fixtures stamped at known ages:
|
|
13
|
+
|
|
14
|
+
age -mtime +2 -mmin +2880
|
|
15
|
+
47h no no
|
|
16
|
+
49h NO yes <- the whole disagreement
|
|
17
|
+
71h NO yes
|
|
18
|
+
73h yes yes
|
|
19
|
+
|
|
20
|
+
The 49h-71h band is silently dropped. The failure direction is what makes
|
|
21
|
+
this worth a rule: the under-selection reports "nothing to do", and nobody
|
|
22
|
+
questions an empty result from a cleanup predicate, so it prevents needed
|
|
23
|
+
work instead of causing visible harm. Reported once as a broken `find`; it
|
|
24
|
+
is not. `find` is answering a different question than the flag appears to
|
|
25
|
+
ask, which is exactly why it survives review.
|
|
26
|
+
|
|
27
|
+
Write the question you mean:
|
|
28
|
+
|
|
29
|
+
older than N days -> -mmin +$((N * 1440))
|
|
30
|
+
older than N days -> ! -newermt "<an ISO timestamp N days back>"
|
|
31
|
+
older than N hours -> -mmin +$((N * 60))
|
|
32
|
+
|
|
33
|
+
`-mmin` is minute-granularity and is not truncated to days, so it means
|
|
34
|
+
what it reads. `-newermt` is exact but needs a timestamp, and the two
|
|
35
|
+
`date` implementations disagree on how to produce one: `date -v-2d` is
|
|
36
|
+
BSD/macOS and `date -d '2 days ago'` is GNU/Linux. Prefer `-mmin` in any
|
|
37
|
+
script that runs on both.
|
|
38
|
+
note: |
|
|
39
|
+
This rule matches the flag itself rather than a `find` invocation, so it
|
|
40
|
+
still fires through `/usr/bin/find`, `$FIND`, and `xargs find`. There are
|
|
41
|
+
no legitimate uses in this repository: every shipped age predicate already
|
|
42
|
+
uses `-mmin`, and this rule exists to keep it that way, because `-mtime` is
|
|
43
|
+
the form a maintainer reaches for by reflex and the shell hooks that carry
|
|
44
|
+
these predicates are copied across five per-agent plugin variants, so one
|
|
45
|
+
wrong edit lands in eight files at once.
|
|
46
|
+
|
|
47
|
+
See CodySwannGT/lisa#3905.
|
|
48
|
+
rule:
|
|
49
|
+
kind: word
|
|
50
|
+
regex: ^-(mtime|atime|ctime)$
|
|
@@ -75,6 +75,18 @@ export const FAMILIES = [
|
|
|
75
75
|
match: /(^|\/)\.github\/k6\/thresholds\/[^/]+\.json$/,
|
|
76
76
|
kind: "k6",
|
|
77
77
|
},
|
|
78
|
+
{
|
|
79
|
+
// #3992. The always-on eager rule tier is concatenated into every session
|
|
80
|
+
// AND every subagent start, so its byte ceiling is a quality threshold like
|
|
81
|
+
// any other. `max` because the number is a ceiling: lowering it tightens the
|
|
82
|
+
// gate and is free, raising it re-authorises context the cleanup removed and
|
|
83
|
+
// is the change a human has to see. The tier was cut to 26,540 bytes once
|
|
84
|
+
// and regrew to 201,083 in fourteen weeks with nothing watching it.
|
|
85
|
+
id: "eager-rules",
|
|
86
|
+
match: /(^|\/)eager-rules\.thresholds\.json$/,
|
|
87
|
+
kind: "json-num",
|
|
88
|
+
direction: "max",
|
|
89
|
+
},
|
|
78
90
|
{
|
|
79
91
|
id: "lisa-config",
|
|
80
92
|
match: /(^|\/)\.lisa\.config\.json$/,
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CI gate on the total size of the always-on eager rule tier.
|
|
4
|
+
*
|
|
5
|
+
* WHY THIS EXISTS, AND WHY IT IS A GATE RATHER THAN A REPORT.
|
|
6
|
+
*
|
|
7
|
+
* `plugins/<plugin>/rules/eager/*.md` is concatenated into `additionalContext`
|
|
8
|
+
* by `inject-rules.sh` on **SessionStart and SubagentStart**, mirrored into
|
|
9
|
+
* `.codex/lisa-rules/eager/` and `.opencode/lisa-rules/`, and flattened to
|
|
10
|
+
* Cursor `.mdc` files with `alwaysApply: true`. Every session in every adopting
|
|
11
|
+
* project pays for it before its first tool call, and so does every subagent —
|
|
12
|
+
* a lead plus ten teammates pays it eleven times.
|
|
13
|
+
*
|
|
14
|
+
* This tier has already been cleaned up once and grown back. Measured from git:
|
|
15
|
+
*
|
|
16
|
+
* pre-split (flat rules/) 12 files 190,237 bytes
|
|
17
|
+
* 2026-05-28 after a820527 13 files 26,540 bytes ("~86% smaller")
|
|
18
|
+
* 2026-07-15 19 files 49,488 bytes
|
|
19
|
+
* 2026-07-30 36 files 105,198 bytes
|
|
20
|
+
* 2026-09-05 56 files 201,083 bytes
|
|
21
|
+
*
|
|
22
|
+
* Fourteen weeks took it to 7.6x the post-cleanup figure and 5.7% PAST the
|
|
23
|
+
* payload the cleanup was created to remove. Nothing measured it, so nobody saw
|
|
24
|
+
* it happen — every individual addition was defensible. A fix performed once
|
|
25
|
+
* and undone by accretion needs a guard, not a second tidy (#3992).
|
|
26
|
+
*
|
|
27
|
+
* TOTAL BYTES, NOT PER FILE. A per-file cap is defeated by splitting one rule
|
|
28
|
+
* into two, and the file count is exactly what already moved: 13 -> 56. The
|
|
29
|
+
* cost to a session is the sum, so the sum is what is measured.
|
|
30
|
+
*
|
|
31
|
+
* THE CEILING lives in `eager-rules.thresholds.json` (root, beside the other threshold files) and
|
|
32
|
+
* is registered as a `max`-direction family in `threshold-ratchet-families.mjs`,
|
|
33
|
+
* so it may only tighten: lowering it is free, raising it is a weakening that
|
|
34
|
+
* needs a `thresholdRatchet.allow` entry with a written reason. See
|
|
35
|
+
* `threshold-ratchet.mjs` for why an allow entry is read only from the baseline.
|
|
36
|
+
*
|
|
37
|
+
* ALL THREE ROOTS are checked, not just the source. `plugins/lisa/` and
|
|
38
|
+
* `plugins/lisa-copilot/` are generated copies that ship to Claude and Copilot;
|
|
39
|
+
* checking only `plugins/src/base` would gate the input to a generator while the
|
|
40
|
+
* artifacts that actually reach a session went unmeasured.
|
|
41
|
+
*
|
|
42
|
+
* It refuses to pass an inspection it did not perform: zero roots or zero rule
|
|
43
|
+
* files is exit 2, not a passing zero. Every gate in the `plugins-sync` job
|
|
44
|
+
* takes that stance, and it is why they are trustworthy — a scan that finds
|
|
45
|
+
* nothing and a tree that is clean otherwise print the same tick.
|
|
46
|
+
* @module scripts/check-eager-rule-budget
|
|
47
|
+
*/
|
|
48
|
+
import { readdirSync, readFileSync, statSync, existsSync } from "node:fs";
|
|
49
|
+
import * as path from "node:path";
|
|
50
|
+
import { fileURLToPath } from "node:url";
|
|
51
|
+
|
|
52
|
+
const REPO_ROOT = path.resolve(
|
|
53
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
54
|
+
".."
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
/** Plugin roots whose eager tier ships to a session. */
|
|
58
|
+
const CHECKED_PLUGINS = ["src/base", "lisa", "lisa-copilot"];
|
|
59
|
+
|
|
60
|
+
/** Committed ceiling, ratcheted by the `eager-rules` threshold family. */
|
|
61
|
+
const THRESHOLDS_FILE = path.join(REPO_ROOT, "eager-rules.thresholds.json");
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Total the `.md` payload of one plugin's eager rules directory.
|
|
65
|
+
* @param {string} pluginRelPath - Plugin path relative to `plugins/`.
|
|
66
|
+
* @returns {{ root: string, files: number, bytes: number } | undefined} The
|
|
67
|
+
* measurement, or undefined when the plugin has no eager directory.
|
|
68
|
+
*/
|
|
69
|
+
function measureEagerTier(pluginRelPath) {
|
|
70
|
+
const dir = path.join(REPO_ROOT, "plugins", pluginRelPath, "rules", "eager");
|
|
71
|
+
if (!existsSync(dir) || !statSync(dir).isDirectory()) return undefined;
|
|
72
|
+
const files = readdirSync(dir).filter(name => name.endsWith(".md"));
|
|
73
|
+
const bytes = files.reduce(
|
|
74
|
+
(total, name) =>
|
|
75
|
+
total + Buffer.byteLength(readFileSync(path.join(dir, name))),
|
|
76
|
+
0
|
|
77
|
+
);
|
|
78
|
+
return {
|
|
79
|
+
root: `plugins/${pluginRelPath}/rules/eager`,
|
|
80
|
+
files: files.length,
|
|
81
|
+
bytes,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Read the committed byte ceiling.
|
|
87
|
+
* @returns {number} The ceiling in bytes.
|
|
88
|
+
*/
|
|
89
|
+
function readCeiling() {
|
|
90
|
+
if (!existsSync(THRESHOLDS_FILE)) {
|
|
91
|
+
process.stderr.write(
|
|
92
|
+
`✗ Missing ${path.relative(REPO_ROOT, THRESHOLDS_FILE)} — the eager budget ` +
|
|
93
|
+
`cannot be checked without its committed ceiling.\n`
|
|
94
|
+
);
|
|
95
|
+
process.exit(2);
|
|
96
|
+
}
|
|
97
|
+
const parsed = JSON.parse(readFileSync(THRESHOLDS_FILE, "utf8"));
|
|
98
|
+
const ceiling = parsed.totalBytes;
|
|
99
|
+
if (
|
|
100
|
+
typeof ceiling !== "number" ||
|
|
101
|
+
!Number.isFinite(ceiling) ||
|
|
102
|
+
ceiling <= 0
|
|
103
|
+
) {
|
|
104
|
+
process.stderr.write(
|
|
105
|
+
`✗ ${path.relative(REPO_ROOT, THRESHOLDS_FILE)} has no usable ` +
|
|
106
|
+
`"totalBytes" number; refusing to check against an unreadable ceiling.\n`
|
|
107
|
+
);
|
|
108
|
+
process.exit(2);
|
|
109
|
+
}
|
|
110
|
+
return ceiling;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const ceiling = readCeiling();
|
|
114
|
+
const measured = CHECKED_PLUGINS.map(measureEagerTier).filter(Boolean);
|
|
115
|
+
|
|
116
|
+
// VACUITY GUARD. A tree with no eager rules and a tree this script failed to
|
|
117
|
+
// read produce the same zero, and the passing branch cannot tell them apart.
|
|
118
|
+
// Exit 2 keeps "I could not measure" distinct from "it is within budget".
|
|
119
|
+
if (measured.length === 0) {
|
|
120
|
+
process.stderr.write(
|
|
121
|
+
`✗ No eager rules directory found under any of ${CHECKED_PLUGINS.join(", ")}. ` +
|
|
122
|
+
`Refusing to report a passing zero for a measurement that did not happen.\n`
|
|
123
|
+
);
|
|
124
|
+
process.exit(2);
|
|
125
|
+
}
|
|
126
|
+
const empty = measured.filter(m => m.files === 0);
|
|
127
|
+
if (empty.length > 0) {
|
|
128
|
+
process.stderr.write(
|
|
129
|
+
`✗ Eager rules directories with zero .md files: ${empty
|
|
130
|
+
.map(m => m.root)
|
|
131
|
+
.join(", ")}. Refusing to report a passing zero.\n`
|
|
132
|
+
);
|
|
133
|
+
process.exit(2);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const over = measured.filter(m => m.bytes > ceiling);
|
|
137
|
+
for (const m of measured) {
|
|
138
|
+
const margin =
|
|
139
|
+
m.bytes > ceiling
|
|
140
|
+
? `over by ${m.bytes - ceiling}`
|
|
141
|
+
: `${ceiling - m.bytes} to spare`;
|
|
142
|
+
process.stdout.write(
|
|
143
|
+
`${(m.bytes > ceiling ? "OVER" : "ok").padEnd(4)} ${m.root}: ${m.files} ` +
|
|
144
|
+
`files, ${m.bytes} bytes (ceiling ${ceiling}, ${margin})\n`
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (over.length > 0) {
|
|
149
|
+
process.stderr.write("\n");
|
|
150
|
+
for (const m of over) {
|
|
151
|
+
process.stderr.write(
|
|
152
|
+
`✗ ${m.root} is ${m.bytes - ceiling} bytes over the eager budget ` +
|
|
153
|
+
`(${m.bytes} measured, ceiling ${ceiling}).\n`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
process.stderr.write(
|
|
157
|
+
"\n Every session AND every subagent in every adopting project pays this " +
|
|
158
|
+
"payload\n before its first tool call. Fix it by demoting, not by raising " +
|
|
159
|
+
"the ceiling:\n\n" +
|
|
160
|
+
" 1. Move the rule's body to plugins/src/base/rules/reference/<slug>.md\n" +
|
|
161
|
+
" 2. Add a one-line entry to plugins/src/base/rules/eager/00-rule-index.md\n" +
|
|
162
|
+
" 3. Delete the eager head and re-run this gate\n\n" +
|
|
163
|
+
" A rule stays eager only when an agent behaves WRONGLY without it in a " +
|
|
164
|
+
"session\n that never asks for it. Being true, well-argued, and cited by " +
|
|
165
|
+
"ten skills is not\n that bar — those rules belong in the index.\n\n" +
|
|
166
|
+
" Raising the ceiling in eager-rules.thresholds.json is a weakening: the " +
|
|
167
|
+
"threshold\n ratchet refuses it without a thresholdRatchet.allow entry " +
|
|
168
|
+
"naming a reason.\n"
|
|
169
|
+
);
|
|
170
|
+
process.exit(1);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
process.stdout.write(
|
|
174
|
+
`\n✓ Eager rule budget passed across ${measured.length} plugin roots ` +
|
|
175
|
+
`(ceiling ${ceiling} bytes).\n`
|
|
176
|
+
);
|