@codyswann/lisa 4.54.13 → 4.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/all/copy-contents/.gitattributes +8 -0
- package/all/copy-overwrite/scripts/check-orphaned-branches.mjs +78 -9
- package/all/copy-overwrite/scripts/check-third-party-action-pins.mjs +17 -1
- package/all/copy-overwrite/scripts/lib/process-tree-runner.mjs +194 -7
- package/all/copy-overwrite/scripts/lisa-enforcement-fallback.sh +31 -0
- package/all/copy-overwrite/scripts/lisa-hooks/block-blind-automerge.sh +81 -2
- package/all/copy-overwrite/scripts/lisa-hooks/block-direct-issue-create.sh +293 -8
- package/all/copy-overwrite/scripts/lisa-hooks/block-instruction-file-edits.sh +21 -0
- package/all/copy-overwrite/scripts/lisa-hooks/block-managed-file-edits.sh +78 -15
- package/all/copy-overwrite/scripts/lisa-hooks/block-no-verify.sh +21 -0
- package/all/copy-overwrite/scripts/lisa-hooks/block-shell-json-parsing.sh +21 -0
- package/all/copy-overwrite/scripts/lisa-hooks/guard-dedupe.bash +340 -0
- package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +29 -1
- package/all/copy-overwrite/scripts/lisa-hooks/worktree-binding-guard.mjs +183 -1
- package/all/copy-overwrite/scripts/lisa-hooks/worktree-binding-guard.sh +21 -0
- package/all/copy-overwrite/scripts/lisa-postinstall.mjs +21 -1
- package/all/copy-overwrite/scripts/lisa-run-gates.mjs +100 -5
- package/all/copy-overwrite/scripts/lisa-work-item.mjs +1085 -80
- package/all/create-only/.github/workflows/continuous-gates.yml +7 -0
- package/cdk/create-only/.github/workflows/ci.yml +7 -0
- package/cdk/create-only/.github/workflows/deploy.yml +21 -0
- package/dist/cli/apply.d.ts.map +1 -1
- package/dist/cli/apply.js +9 -1
- package/dist/cli/apply.js.map +1 -1
- package/dist/cli/doctor-apply-deletions.d.ts +61 -0
- package/dist/cli/doctor-apply-deletions.d.ts.map +1 -0
- package/dist/cli/doctor-apply-deletions.js +137 -0
- package/dist/cli/doctor-apply-deletions.js.map +1 -0
- package/dist/cli/doctor-cdk-preset-adoption.d.ts +15 -0
- package/dist/cli/doctor-cdk-preset-adoption.d.ts.map +1 -0
- package/dist/cli/doctor-cdk-preset-adoption.js +316 -0
- package/dist/cli/doctor-cdk-preset-adoption.js.map +1 -0
- package/dist/cli/doctor-config-shadowing.d.ts +22 -0
- package/dist/cli/doctor-config-shadowing.d.ts.map +1 -0
- package/dist/cli/doctor-config-shadowing.js +123 -0
- package/dist/cli/doctor-config-shadowing.js.map +1 -0
- package/dist/cli/doctor-rails-deploy-intent.d.ts +15 -0
- package/dist/cli/doctor-rails-deploy-intent.d.ts.map +1 -0
- package/dist/cli/doctor-rails-deploy-intent.js +197 -0
- package/dist/cli/doctor-rails-deploy-intent.js.map +1 -0
- package/dist/cli/doctor-seeded-artifacts.d.ts +12 -0
- package/dist/cli/doctor-seeded-artifacts.d.ts.map +1 -0
- package/dist/cli/doctor-seeded-artifacts.js +54 -0
- package/dist/cli/doctor-seeded-artifacts.js.map +1 -0
- package/dist/cli/doctor-stale-banner-scan.d.ts +44 -0
- package/dist/cli/doctor-stale-banner-scan.d.ts.map +1 -0
- package/dist/cli/doctor-stale-banner-scan.js +152 -0
- package/dist/cli/doctor-stale-banner-scan.js.map +1 -0
- package/dist/cli/doctor-stale-managed-banner.d.ts +19 -0
- package/dist/cli/doctor-stale-managed-banner.d.ts.map +1 -0
- package/dist/cli/doctor-stale-managed-banner.js +198 -0
- package/dist/cli/doctor-stale-managed-banner.js.map +1 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +16 -0
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/ui-detected-stacks.d.ts.map +1 -1
- package/dist/cli/ui-detected-stacks.js +3 -2
- package/dist/cli/ui-detected-stacks.js.map +1 -1
- package/dist/cli/update-check.d.ts.map +1 -1
- package/dist/cli/update-check.js +14 -0
- package/dist/cli/update-check.js.map +1 -1
- package/dist/cli/worktree-liveness.d.ts +5 -0
- package/dist/cli/worktree-liveness.d.ts.map +1 -1
- package/dist/cli/worktree-liveness.js +7 -6
- package/dist/cli/worktree-liveness.js.map +1 -1
- package/dist/cli/worktree-ownership.d.ts +13 -0
- package/dist/cli/worktree-ownership.d.ts.map +1 -1
- package/dist/cli/worktree-ownership.js +14 -0
- package/dist/cli/worktree-ownership.js.map +1 -1
- package/dist/configs/eslint/base.d.ts +34 -0
- package/dist/configs/eslint/base.d.ts.map +1 -1
- package/dist/configs/eslint/base.js +34 -0
- package/dist/configs/eslint/base.js.map +1 -1
- package/dist/core/apply-receipt.d.ts +65 -0
- package/dist/core/apply-receipt.d.ts.map +1 -1
- package/dist/core/apply-receipt.js +62 -2
- package/dist/core/apply-receipt.js.map +1 -1
- package/dist/core/cdk-preset-adoption.d.ts +136 -0
- package/dist/core/cdk-preset-adoption.d.ts.map +1 -0
- package/dist/core/cdk-preset-adoption.js +135 -0
- package/dist/core/cdk-preset-adoption.js.map +1 -0
- package/dist/core/config-shadowing.d.ts +36 -0
- package/dist/core/config-shadowing.d.ts.map +1 -1
- package/dist/core/config-shadowing.js +38 -0
- package/dist/core/config-shadowing.js.map +1 -1
- package/dist/core/config.d.ts +11 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js.map +1 -1
- package/dist/core/learnings-merge-driver.d.ts +10 -0
- package/dist/core/learnings-merge-driver.d.ts.map +1 -1
- package/dist/core/learnings-merge-driver.js +18 -0
- package/dist/core/learnings-merge-driver.js.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +136 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/lisa.d.ts.map +1 -1
- package/dist/core/lisa.js +9 -1
- package/dist/core/lisa.js.map +1 -1
- package/dist/core/nightly-e2e-guard-behavior-certificate.d.ts +5 -5
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +7 -7
- package/dist/core/nightly-e2e-guard-behavior-certificate.js.map +1 -1
- package/dist/core/ownership-header.d.ts +74 -0
- package/dist/core/ownership-header.d.ts.map +1 -0
- package/dist/core/ownership-header.js +76 -0
- package/dist/core/ownership-header.js.map +1 -0
- package/dist/core/rails-deploy-production-intent.d.ts +36 -0
- package/dist/core/rails-deploy-production-intent.d.ts.map +1 -0
- package/dist/core/rails-deploy-production-intent.js +79 -0
- package/dist/core/rails-deploy-production-intent.js.map +1 -0
- package/dist/core/stale-managed-banner.d.ts +78 -0
- package/dist/core/stale-managed-banner.d.ts.map +1 -0
- package/dist/core/stale-managed-banner.js +118 -0
- package/dist/core/stale-managed-banner.js.map +1 -0
- package/dist/core/two-channel-delivery-scan.d.ts +33 -1
- package/dist/core/two-channel-delivery-scan.d.ts.map +1 -1
- package/dist/core/two-channel-delivery-scan.js +133 -6
- package/dist/core/two-channel-delivery-scan.js.map +1 -1
- package/dist/core/two-channel-delivery.d.ts +101 -4
- package/dist/core/two-channel-delivery.d.ts.map +1 -1
- package/dist/core/two-channel-delivery.js +121 -13
- package/dist/core/two-channel-delivery.js.map +1 -1
- package/dist/core/two-channel-staleness.d.ts +137 -0
- package/dist/core/two-channel-staleness.d.ts.map +1 -0
- package/dist/core/two-channel-staleness.js +268 -0
- package/dist/core/two-channel-staleness.js.map +1 -0
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +265 -121
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/core/workflow-deletion-ownership.d.ts +6 -24
- package/dist/core/workflow-deletion-ownership.d.ts.map +1 -1
- package/dist/core/workflow-deletion-ownership.js +22 -42
- package/dist/core/workflow-deletion-ownership.js.map +1 -1
- package/dist/detection/detectors/cdk.d.ts.map +1 -1
- package/dist/detection/detectors/cdk.js +2 -1
- package/dist/detection/detectors/cdk.js.map +1 -1
- package/dist/health/template-inspection.d.ts.map +1 -1
- package/dist/health/template-inspection.js +2 -1
- package/dist/health/template-inspection.js.map +1 -1
- package/dist/migrations/ensure-lisa-postinstall.d.ts +16 -0
- package/dist/migrations/ensure-lisa-postinstall.d.ts.map +1 -1
- package/dist/migrations/ensure-lisa-postinstall.js +16 -0
- package/dist/migrations/ensure-lisa-postinstall.js.map +1 -1
- package/dist/migrations/ensure-pinned-reusable-workflow-refs.d.ts +2 -2
- package/dist/migrations/ensure-pinned-reusable-workflow-refs.d.ts.map +1 -1
- package/dist/migrations/ensure-pinned-reusable-workflow-refs.js +98 -34
- package/dist/migrations/ensure-pinned-reusable-workflow-refs.js.map +1 -1
- package/dist/migrations/ensure-playwright-dedicated-caller.d.ts.map +1 -1
- package/dist/migrations/ensure-playwright-dedicated-caller.js +1 -0
- package/dist/migrations/ensure-playwright-dedicated-caller.js.map +1 -1
- package/dist/opencode/hooks-installer.d.ts.map +1 -1
- package/dist/opencode/hooks-installer.js +1 -0
- package/dist/opencode/hooks-installer.js.map +1 -1
- package/dist/opencode/plugin-templates/block-managed-file-edits.sh +78 -15
- package/dist/opencode/plugin-templates/block-no-verify.sh +21 -0
- package/dist/opencode/plugin-templates/guard-dedupe.bash +337 -0
- package/dist/opencode/plugin-templates/lisa-block-direct-issue-create.ts +242 -55
- package/dist/opencode/plugin-templates/parity-safety-net.sh +29 -1
- package/dist/strategies/package-lisa.d.ts.map +1 -1
- package/dist/strategies/package-lisa.js +2 -1
- package/dist/strategies/package-lisa.js.map +1 -1
- package/dist/utils/path-utils.d.ts +29 -0
- package/dist/utils/path-utils.d.ts.map +1 -1
- package/dist/utils/path-utils.js +41 -0
- package/dist/utils/path-utils.js.map +1 -1
- package/expo/create-only/.github/workflows/ci.yml +21 -0
- package/expo/create-only/.github/workflows/deploy.yml +28 -0
- package/expo/create-only/.github/workflows/maestro-e2e.yml +7 -0
- package/expo/create-only/.github/workflows/nightly-e2e-bypass-reaper.yml +124 -13
- package/expo/create-only/.github/workflows/nightly-e2e-health.yml +7 -0
- package/expo/create-only/.github/workflows/nightly-e2e-report.yml +7 -0
- package/expo/create-only/.github/workflows/nightly-e2e-tracking.yml +8 -0
- package/expo/create-only/.github/workflows/playwright-e2e.yml +7 -0
- package/harper-fabric/copy-overwrite/.github/workflows/ci.yml +7 -0
- package/harper-fabric/create-only/.github/workflows/deploy.yml +14 -0
- package/nestjs/create-only/.github/workflows/ci.yml +14 -0
- package/nestjs/create-only/.github/workflows/deploy.yml +28 -0
- package/package.json +10 -5
- package/phaser/copy-overwrite/.github/workflows/ci.yml +7 -0
- package/plugins/lisa/.claude-plugin/plugin.json +28 -1
- package/plugins/lisa/.codex-plugin/hooks.json +27 -0
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
- package/plugins/lisa/.codex-plugin/skills/lisa-github-build-intake/SKILL.md +27 -4
- package/plugins/lisa/.codex-plugin/skills/lisa-github-prd-intake/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-github-validate-issue/SKILL.md +22 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-github-verify/SKILL.md +14 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-github-write-issue/SKILL.md +51 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-intake/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-build-intake/SKILL.md +27 -4
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-verify/SKILL.md +14 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-write-ticket/SKILL.md +51 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-access/SKILL.md +55 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-build-intake/SKILL.md +27 -4
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-prd-intake/SKILL.md +47 -30
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-to-tracker/SKILL.md +3 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-validate-issue/SKILL.md +22 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-verify/SKILL.md +14 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-write-issue/SKILL.md +54 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-qa-clear/SKILL.md +19 -7
- package/plugins/lisa/.codex-plugin/skills/lisa-qa-fail/SKILL.md +28 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-qa-queue/SKILL.md +28 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-repair-intake/SKILL.md +94 -24
- package/plugins/lisa/.codex-plugin/skills/lisa-rework-triage/SKILL.md +32 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-linear/SKILL.md +4 -4
- package/plugins/lisa/.codex-plugin/skills/lisa-track/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-tracker-build-intake/SKILL.md +9 -0
- package/plugins/lisa/agents/linear-prd-intake.md +8 -8
- package/plugins/lisa/hooks/block-blind-automerge.sh +81 -2
- package/plugins/lisa/hooks/block-direct-issue-create.agy.sh +64 -16
- package/plugins/lisa/hooks/block-direct-issue-create.sh +293 -8
- package/plugins/lisa/hooks/block-instruction-file-edits.sh +21 -0
- package/plugins/lisa/hooks/block-managed-file-edits.sh +78 -15
- package/plugins/lisa/hooks/block-no-verify.sh +21 -0
- package/plugins/lisa/hooks/block-shell-json-parsing.sh +21 -0
- package/plugins/lisa/hooks/failure-signature-index.mjs +240 -5
- package/plugins/lisa/hooks/guard-dedupe.bash +337 -0
- package/plugins/lisa/hooks/inject-rules.sh +13 -5
- package/plugins/lisa/hooks/operational-hazards.mjs +736 -0
- package/plugins/lisa/hooks/operational-hazards.sh +28 -0
- package/plugins/lisa/hooks/parity-safety-net.sh +29 -1
- package/plugins/lisa/hooks/threshold-ratchet-compare.mjs +102 -21
- package/plugins/lisa/hooks/threshold-ratchet-families.mjs +130 -2
- package/plugins/lisa/hooks/threshold-ratchet.mjs +63 -13
- package/plugins/lisa/hooks/worktree-binding-guard.mjs +183 -1
- package/plugins/lisa/hooks/worktree-binding-guard.sh +21 -0
- package/plugins/lisa/rules/eager/00-rule-index.md +1 -0
- package/plugins/lisa/rules/eager/operational-hazards.md +50 -0
- package/plugins/lisa/rules/eager/tracked-work.md +1 -1
- package/plugins/lisa/rules/reference/config-resolution.md +1 -1
- package/plugins/lisa/rules/reference/leaf-only-lifecycle.md +1 -1
- package/plugins/lisa/rules/reference/operational-hazards.md +115 -0
- package/plugins/lisa/rules/reference/work-item-trailer-definition.md +129 -0
- package/plugins/lisa/scripts/intake-blocker-reprobe.mjs +407 -26
- package/plugins/lisa/scripts/qa-signal-lifecycle.mjs +507 -0
- package/plugins/lisa/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
- package/plugins/lisa/skills/lisa-github-build-intake/SKILL.md +27 -4
- package/plugins/lisa/skills/lisa-github-prd-intake/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-github-validate-issue/SKILL.md +22 -0
- package/plugins/lisa/skills/lisa-github-verify/SKILL.md +14 -0
- package/plugins/lisa/skills/lisa-github-write-issue/SKILL.md +51 -0
- package/plugins/lisa/skills/lisa-intake/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-jira-build-intake/SKILL.md +27 -4
- package/plugins/lisa/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
- package/plugins/lisa/skills/lisa-jira-verify/SKILL.md +14 -0
- package/plugins/lisa/skills/lisa-jira-write-ticket/SKILL.md +51 -0
- package/plugins/lisa/skills/lisa-linear-access/SKILL.md +55 -2
- package/plugins/lisa/skills/lisa-linear-build-intake/SKILL.md +27 -4
- package/plugins/lisa/skills/lisa-linear-prd-intake/SKILL.md +48 -31
- package/plugins/lisa/skills/lisa-linear-to-tracker/SKILL.md +3 -3
- package/plugins/lisa/skills/lisa-linear-validate-issue/SKILL.md +22 -0
- package/plugins/lisa/skills/lisa-linear-verify/SKILL.md +14 -0
- package/plugins/lisa/skills/lisa-linear-write-issue/SKILL.md +54 -3
- package/plugins/lisa/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
- package/plugins/lisa/skills/lisa-qa-clear/SKILL.md +19 -7
- package/plugins/lisa/skills/lisa-qa-fail/SKILL.md +28 -3
- package/plugins/lisa/skills/lisa-qa-queue/SKILL.md +28 -3
- package/plugins/lisa/skills/lisa-repair-intake/SKILL.md +94 -24
- package/plugins/lisa/skills/lisa-rework-triage/SKILL.md +32 -2
- package/plugins/lisa/skills/lisa-setup-linear/SKILL.md +5 -5
- package/plugins/lisa/skills/lisa-track/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-tracker-build-intake/SKILL.md +9 -0
- package/plugins/lisa-agy/agents/linear-prd-intake.md +8 -8
- package/plugins/lisa-agy/hooks/block-blind-automerge.sh +81 -2
- package/plugins/lisa-agy/hooks/block-direct-issue-create.agy.sh +64 -16
- package/plugins/lisa-agy/hooks/block-direct-issue-create.sh +293 -8
- package/plugins/lisa-agy/hooks/block-instruction-file-edits.sh +21 -0
- package/plugins/lisa-agy/hooks/block-managed-file-edits.sh +78 -15
- package/plugins/lisa-agy/hooks/block-shell-json-parsing.sh +21 -0
- package/plugins/lisa-agy/hooks/guard-dedupe.bash +337 -0
- package/plugins/lisa-agy/hooks/parity-safety-net.sh +29 -1
- package/plugins/lisa-agy/hooks.json +1 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/scripts/intake-blocker-reprobe.mjs +407 -26
- package/plugins/lisa-agy/scripts/qa-signal-lifecycle.mjs +507 -0
- package/plugins/lisa-agy/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
- package/plugins/lisa-agy/skills/lisa-github-build-intake/SKILL.md +27 -4
- package/plugins/lisa-agy/skills/lisa-github-prd-intake/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-github-validate-issue/SKILL.md +22 -0
- package/plugins/lisa-agy/skills/lisa-github-verify/SKILL.md +14 -0
- package/plugins/lisa-agy/skills/lisa-github-write-issue/SKILL.md +51 -0
- package/plugins/lisa-agy/skills/lisa-intake/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-jira-build-intake/SKILL.md +27 -4
- package/plugins/lisa-agy/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
- package/plugins/lisa-agy/skills/lisa-jira-verify/SKILL.md +14 -0
- package/plugins/lisa-agy/skills/lisa-jira-write-ticket/SKILL.md +51 -0
- package/plugins/lisa-agy/skills/lisa-linear-access/SKILL.md +55 -2
- package/plugins/lisa-agy/skills/lisa-linear-build-intake/SKILL.md +27 -4
- package/plugins/lisa-agy/skills/lisa-linear-prd-intake/SKILL.md +48 -31
- package/plugins/lisa-agy/skills/lisa-linear-to-tracker/SKILL.md +3 -3
- package/plugins/lisa-agy/skills/lisa-linear-validate-issue/SKILL.md +22 -0
- package/plugins/lisa-agy/skills/lisa-linear-verify/SKILL.md +14 -0
- package/plugins/lisa-agy/skills/lisa-linear-write-issue/SKILL.md +54 -3
- package/plugins/lisa-agy/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
- package/plugins/lisa-agy/skills/lisa-qa-clear/SKILL.md +19 -7
- package/plugins/lisa-agy/skills/lisa-qa-fail/SKILL.md +28 -3
- package/plugins/lisa-agy/skills/lisa-qa-queue/SKILL.md +28 -3
- package/plugins/lisa-agy/skills/lisa-repair-intake/SKILL.md +94 -24
- package/plugins/lisa-agy/skills/lisa-rework-triage/SKILL.md +32 -2
- package/plugins/lisa-agy/skills/lisa-setup-linear/SKILL.md +5 -5
- package/plugins/lisa-agy/skills/lisa-track/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-tracker-build-intake/SKILL.md +9 -0
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +19 -1
- package/plugins/lisa-copilot/agents/linear-prd-intake.agent.md +8 -8
- package/plugins/lisa-copilot/hooks/block-blind-automerge.sh +81 -2
- package/plugins/lisa-copilot/hooks/block-direct-issue-create.sh +293 -8
- package/plugins/lisa-copilot/hooks/block-instruction-file-edits.sh +21 -0
- package/plugins/lisa-copilot/hooks/block-managed-file-edits.sh +78 -15
- package/plugins/lisa-copilot/hooks/block-no-verify.sh +21 -0
- package/plugins/lisa-copilot/hooks/block-shell-json-parsing.sh +21 -0
- package/plugins/lisa-copilot/hooks/failure-signature-index.mjs +240 -5
- package/plugins/lisa-copilot/hooks/guard-dedupe.bash +337 -0
- package/plugins/lisa-copilot/hooks/inject-rules.sh +13 -5
- package/plugins/lisa-copilot/hooks/operational-hazards.mjs +736 -0
- package/plugins/lisa-copilot/hooks/operational-hazards.sh +28 -0
- package/plugins/lisa-copilot/hooks/parity-safety-net.sh +29 -1
- package/plugins/lisa-copilot/hooks/threshold-ratchet-compare.mjs +102 -21
- package/plugins/lisa-copilot/hooks/threshold-ratchet-families.mjs +130 -2
- package/plugins/lisa-copilot/hooks/threshold-ratchet.mjs +63 -13
- package/plugins/lisa-copilot/hooks/worktree-binding-guard.mjs +183 -1
- package/plugins/lisa-copilot/hooks/worktree-binding-guard.sh +21 -0
- package/plugins/lisa-copilot/rules/eager/00-rule-index.md +1 -0
- package/plugins/lisa-copilot/rules/eager/operational-hazards.md +50 -0
- package/plugins/lisa-copilot/rules/eager/tracked-work.md +1 -1
- package/plugins/lisa-copilot/rules/reference/config-resolution.md +1 -1
- package/plugins/lisa-copilot/rules/reference/leaf-only-lifecycle.md +1 -1
- package/plugins/lisa-copilot/rules/reference/operational-hazards.md +115 -0
- package/plugins/lisa-copilot/rules/reference/work-item-trailer-definition.md +129 -0
- package/plugins/lisa-copilot/scripts/intake-blocker-reprobe.mjs +407 -26
- package/plugins/lisa-copilot/scripts/qa-signal-lifecycle.mjs +507 -0
- package/plugins/lisa-copilot/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
- package/plugins/lisa-copilot/skills/lisa-github-build-intake/SKILL.md +27 -4
- package/plugins/lisa-copilot/skills/lisa-github-prd-intake/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-github-validate-issue/SKILL.md +22 -0
- package/plugins/lisa-copilot/skills/lisa-github-verify/SKILL.md +14 -0
- package/plugins/lisa-copilot/skills/lisa-github-write-issue/SKILL.md +51 -0
- package/plugins/lisa-copilot/skills/lisa-intake/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-jira-build-intake/SKILL.md +27 -4
- package/plugins/lisa-copilot/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
- package/plugins/lisa-copilot/skills/lisa-jira-verify/SKILL.md +14 -0
- package/plugins/lisa-copilot/skills/lisa-jira-write-ticket/SKILL.md +51 -0
- package/plugins/lisa-copilot/skills/lisa-linear-access/SKILL.md +55 -2
- package/plugins/lisa-copilot/skills/lisa-linear-build-intake/SKILL.md +27 -4
- package/plugins/lisa-copilot/skills/lisa-linear-prd-intake/SKILL.md +48 -31
- package/plugins/lisa-copilot/skills/lisa-linear-to-tracker/SKILL.md +3 -3
- package/plugins/lisa-copilot/skills/lisa-linear-validate-issue/SKILL.md +22 -0
- package/plugins/lisa-copilot/skills/lisa-linear-verify/SKILL.md +14 -0
- package/plugins/lisa-copilot/skills/lisa-linear-write-issue/SKILL.md +54 -3
- package/plugins/lisa-copilot/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
- package/plugins/lisa-copilot/skills/lisa-qa-clear/SKILL.md +19 -7
- package/plugins/lisa-copilot/skills/lisa-qa-fail/SKILL.md +28 -3
- package/plugins/lisa-copilot/skills/lisa-qa-queue/SKILL.md +28 -3
- package/plugins/lisa-copilot/skills/lisa-repair-intake/SKILL.md +94 -24
- package/plugins/lisa-copilot/skills/lisa-rework-triage/SKILL.md +32 -2
- package/plugins/lisa-copilot/skills/lisa-setup-linear/SKILL.md +5 -5
- package/plugins/lisa-copilot/skills/lisa-track/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-tracker-build-intake/SKILL.md +9 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/agents/linear-prd-intake.md +8 -8
- package/plugins/lisa-cursor/hooks/block-blind-automerge.sh +81 -2
- package/plugins/lisa-cursor/hooks/block-direct-issue-create.sh +293 -8
- package/plugins/lisa-cursor/hooks/block-instruction-file-edits.sh +21 -0
- package/plugins/lisa-cursor/hooks/block-managed-file-edits.sh +78 -15
- package/plugins/lisa-cursor/hooks/block-no-verify.sh +21 -0
- package/plugins/lisa-cursor/hooks/block-shell-json-parsing.sh +21 -0
- package/plugins/lisa-cursor/hooks/failure-signature-index.mjs +240 -5
- package/plugins/lisa-cursor/hooks/guard-dedupe.bash +337 -0
- package/plugins/lisa-cursor/hooks/hooks.json +10 -0
- package/plugins/lisa-cursor/hooks/operational-hazards.mjs +736 -0
- package/plugins/lisa-cursor/hooks/operational-hazards.sh +28 -0
- package/plugins/lisa-cursor/hooks/parity-safety-net.sh +29 -1
- package/plugins/lisa-cursor/hooks/threshold-ratchet-compare.mjs +102 -21
- package/plugins/lisa-cursor/hooks/threshold-ratchet-families.mjs +130 -2
- package/plugins/lisa-cursor/hooks/threshold-ratchet.mjs +63 -13
- package/plugins/lisa-cursor/hooks/worktree-binding-guard.mjs +183 -1
- package/plugins/lisa-cursor/hooks/worktree-binding-guard.sh +21 -0
- package/plugins/lisa-cursor/rules/00-rule-index.mdc +1 -0
- package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/leaf-only-lifecycle-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/operational-hazards-reference.mdc +120 -0
- package/plugins/lisa-cursor/rules/operational-hazards.mdc +55 -0
- package/plugins/lisa-cursor/rules/tracked-work.mdc +1 -1
- package/plugins/lisa-cursor/rules/work-item-trailer-definition-reference.mdc +134 -0
- package/plugins/lisa-cursor/scripts/intake-blocker-reprobe.mjs +407 -26
- package/plugins/lisa-cursor/scripts/qa-signal-lifecycle.mjs +507 -0
- package/plugins/lisa-cursor/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
- package/plugins/lisa-cursor/skills/lisa-github-build-intake/SKILL.md +27 -4
- package/plugins/lisa-cursor/skills/lisa-github-prd-intake/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-github-validate-issue/SKILL.md +22 -0
- package/plugins/lisa-cursor/skills/lisa-github-verify/SKILL.md +14 -0
- package/plugins/lisa-cursor/skills/lisa-github-write-issue/SKILL.md +51 -0
- package/plugins/lisa-cursor/skills/lisa-intake/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-jira-build-intake/SKILL.md +27 -4
- package/plugins/lisa-cursor/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
- package/plugins/lisa-cursor/skills/lisa-jira-verify/SKILL.md +14 -0
- package/plugins/lisa-cursor/skills/lisa-jira-write-ticket/SKILL.md +51 -0
- package/plugins/lisa-cursor/skills/lisa-linear-access/SKILL.md +55 -2
- package/plugins/lisa-cursor/skills/lisa-linear-build-intake/SKILL.md +27 -4
- package/plugins/lisa-cursor/skills/lisa-linear-prd-intake/SKILL.md +48 -31
- package/plugins/lisa-cursor/skills/lisa-linear-to-tracker/SKILL.md +3 -3
- package/plugins/lisa-cursor/skills/lisa-linear-validate-issue/SKILL.md +22 -0
- package/plugins/lisa-cursor/skills/lisa-linear-verify/SKILL.md +14 -0
- package/plugins/lisa-cursor/skills/lisa-linear-write-issue/SKILL.md +54 -3
- package/plugins/lisa-cursor/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
- package/plugins/lisa-cursor/skills/lisa-qa-clear/SKILL.md +19 -7
- package/plugins/lisa-cursor/skills/lisa-qa-fail/SKILL.md +28 -3
- package/plugins/lisa-cursor/skills/lisa-qa-queue/SKILL.md +28 -3
- package/plugins/lisa-cursor/skills/lisa-repair-intake/SKILL.md +94 -24
- package/plugins/lisa-cursor/skills/lisa-rework-triage/SKILL.md +32 -2
- package/plugins/lisa-cursor/skills/lisa-setup-linear/SKILL.md +5 -5
- package/plugins/lisa-cursor/skills/lisa-track/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-tracker-build-intake/SKILL.md +9 -0
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/hooks/inject-rules.sh +14 -3
- package/plugins/lisa-harper-fabric/rules/eager/harper-fabric.md +31 -0
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/hooks/inject-rules.sh +14 -3
- package/plugins/lisa-harper-fabric-copilot/rules/eager/harper-fabric.md +31 -0
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/rules/harper-fabric-reference.mdc +57 -0
- package/plugins/lisa-harper-fabric-cursor/rules/harper-fabric.mdc +32 -53
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/hooks/inject-rules.sh +14 -3
- package/plugins/lisa-phaser/rules/eager/phaser.md +44 -0
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/hooks/inject-rules.sh +14 -3
- package/plugins/lisa-phaser-copilot/rules/eager/phaser.md +44 -0
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/rules/phaser-reference.mdc +189 -0
- package/plugins/lisa-phaser-cursor/rules/phaser.mdc +45 -185
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/hooks/inject-rules.sh +14 -4
- package/plugins/lisa-rails/rules/eager/rails-conventions.md +23 -0
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/hooks/inject-rules.sh +14 -4
- package/plugins/lisa-rails-copilot/rules/eager/rails-conventions.md +23 -0
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/rules/rails-conventions-reference.mdc +181 -0
- package/plugins/lisa-rails-cursor/rules/rails-conventions.mdc +24 -177
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/materialized-artifacts.json +1 -0
- package/plugins/src/base/.claude-plugin/plugin.json +27 -0
- package/plugins/src/base/agents/linear-prd-intake.md +8 -8
- package/plugins/src/base/hooks/block-blind-automerge.sh +81 -2
- package/plugins/src/base/hooks/block-direct-issue-create.agy.sh +64 -16
- package/plugins/src/base/hooks/block-direct-issue-create.sh +293 -8
- package/plugins/src/base/hooks/block-instruction-file-edits.sh +21 -0
- package/plugins/src/base/hooks/block-managed-file-edits.sh +78 -15
- package/plugins/src/base/hooks/block-no-verify.sh +21 -0
- package/plugins/src/base/hooks/block-shell-json-parsing.sh +21 -0
- package/plugins/src/base/hooks/failure-signature-index.mjs +240 -5
- package/plugins/src/base/hooks/guard-dedupe.bash +337 -0
- package/plugins/src/base/hooks/inject-rules.sh +13 -5
- package/plugins/src/base/hooks/operational-hazards.mjs +736 -0
- package/plugins/src/base/hooks/operational-hazards.sh +28 -0
- package/plugins/src/base/hooks/parity-safety-net.sh +29 -1
- package/plugins/src/base/hooks/threshold-ratchet-compare.mjs +102 -21
- package/plugins/src/base/hooks/threshold-ratchet-families.mjs +130 -2
- package/plugins/src/base/hooks/threshold-ratchet.mjs +63 -13
- package/plugins/src/base/hooks/worktree-binding-guard.mjs +183 -1
- package/plugins/src/base/hooks/worktree-binding-guard.sh +21 -0
- package/plugins/src/base/rules/eager/00-rule-index.md +1 -0
- package/plugins/src/base/rules/eager/operational-hazards.md +50 -0
- package/plugins/src/base/rules/eager/tracked-work.md +1 -1
- package/plugins/src/base/rules/reference/config-resolution.md +1 -1
- package/plugins/src/base/rules/reference/leaf-only-lifecycle.md +1 -1
- package/plugins/src/base/rules/reference/operational-hazards.md +115 -0
- package/plugins/src/base/rules/reference/work-item-trailer-definition.md +129 -0
- package/plugins/src/base/scripts/intake-blocker-reprobe.mjs +407 -26
- package/plugins/src/base/scripts/qa-signal-lifecycle.mjs +507 -0
- package/plugins/src/base/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
- package/plugins/src/base/skills/lisa-github-build-intake/SKILL.md +27 -4
- package/plugins/src/base/skills/lisa-github-prd-intake/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-github-validate-issue/SKILL.md +22 -0
- package/plugins/src/base/skills/lisa-github-verify/SKILL.md +14 -0
- package/plugins/src/base/skills/lisa-github-write-issue/SKILL.md +51 -0
- package/plugins/src/base/skills/lisa-intake/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-jira-build-intake/SKILL.md +27 -4
- package/plugins/src/base/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
- package/plugins/src/base/skills/lisa-jira-verify/SKILL.md +14 -0
- package/plugins/src/base/skills/lisa-jira-write-ticket/SKILL.md +51 -0
- package/plugins/src/base/skills/lisa-linear-access/SKILL.md +55 -2
- package/plugins/src/base/skills/lisa-linear-build-intake/SKILL.md +27 -4
- package/plugins/src/base/skills/lisa-linear-prd-intake/SKILL.md +48 -31
- package/plugins/src/base/skills/lisa-linear-to-tracker/SKILL.md +3 -3
- package/plugins/src/base/skills/lisa-linear-validate-issue/SKILL.md +22 -0
- package/plugins/src/base/skills/lisa-linear-verify/SKILL.md +14 -0
- package/plugins/src/base/skills/lisa-linear-write-issue/SKILL.md +54 -3
- package/plugins/src/base/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
- package/plugins/src/base/skills/lisa-qa-clear/SKILL.md +19 -7
- package/plugins/src/base/skills/lisa-qa-fail/SKILL.md +28 -3
- package/plugins/src/base/skills/lisa-qa-queue/SKILL.md +28 -3
- package/plugins/src/base/skills/lisa-repair-intake/SKILL.md +94 -24
- package/plugins/src/base/skills/lisa-rework-triage/SKILL.md +32 -2
- package/plugins/src/base/skills/lisa-setup-linear/SKILL.md +5 -5
- package/plugins/src/base/skills/lisa-track/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-tracker-build-intake/SKILL.md +9 -0
- package/plugins/src/harper-fabric/hooks/inject-rules.sh +14 -3
- package/plugins/src/harper-fabric/rules/eager/harper-fabric.md +31 -0
- package/plugins/src/phaser/hooks/inject-rules.sh +14 -3
- package/plugins/src/phaser/rules/eager/phaser.md +44 -0
- package/plugins/src/rails/hooks/inject-rules.sh +14 -4
- package/plugins/src/rails/rules/eager/rails-conventions.md +23 -0
- package/rails/copy-overwrite/scripts/check-threshold-ratchet.mjs +63 -13
- package/rails/copy-overwrite/scripts/threshold-ratchet-compare.mjs +102 -21
- package/rails/copy-overwrite/scripts/threshold-ratchet-families.mjs +130 -2
- package/rails/create-only/.github/workflows/ci.yml +8 -0
- package/rails/create-only/.github/workflows/deploy.yml +22 -0
- package/scripts/build-plugins.sh +2 -1
- package/scripts/check-engine-floor.mjs +361 -0
- package/scripts/check-guard-parity-notes.mjs +709 -0
- package/scripts/check-template-workflow-refs.mjs +9 -0
- package/scripts/check-third-party-action-pins.mjs +17 -1
- package/scripts/check-workflow-contract-assertions.mjs +480 -0
- package/scripts/copy-opencode-plugin-templates.mjs +1 -0
- package/scripts/deployed-guard-advice.mjs +636 -0
- package/scripts/generate-agy-plugin-artifacts.mjs +35 -10
- package/scripts/generate-export-surface.mjs +105 -19
- package/scripts/generate-two-channel-couplings.ts +143 -212
- package/scripts/lisa-enforcement-fallback.sh +31 -0
- package/scripts/measure-scripts-profile-gap.mts +361 -0
- package/scripts/mutation-performance-measure.mjs +21 -5
- package/scripts/reconcile-release-tags.mjs +65 -84
- package/scripts/two-channel-couplings.json +302 -70
- package/scripts/workflow-contract-assertion.sh +47 -0
- package/typescript/copy-overwrite/.lintstagedrc.json +1 -0
- package/typescript/copy-overwrite/eslint.ignore.config.json +1 -0
- package/typescript/copy-overwrite/scripts/check-nightly-e2e-health.mjs +527 -53
- package/typescript/copy-overwrite/scripts/check-skipped-required-checks.mjs +301 -13
- package/typescript/copy-overwrite/scripts/check-threshold-ratchet.mjs +63 -13
- package/typescript/copy-overwrite/scripts/threshold-ratchet-compare.mjs +102 -21
- package/typescript/copy-overwrite/scripts/threshold-ratchet-families.mjs +130 -2
- package/typescript/create-only/.github/workflows/ci.yml +7 -0
- /package/plugins/lisa-harper-fabric/rules/{harper-fabric.md → reference/harper-fabric.md} +0 -0
- /package/plugins/lisa-harper-fabric-copilot/rules/{harper-fabric.md → reference/harper-fabric.md} +0 -0
- /package/plugins/lisa-phaser/rules/{phaser.md → reference/phaser.md} +0 -0
- /package/plugins/lisa-phaser-copilot/rules/{phaser.md → reference/phaser.md} +0 -0
- /package/plugins/lisa-rails/rules/{rails-conventions.md → reference/rails-conventions.md} +0 -0
- /package/plugins/lisa-rails-copilot/rules/{rails-conventions.md → reference/rails-conventions.md} +0 -0
- /package/plugins/src/harper-fabric/rules/{harper-fabric.md → reference/harper-fabric.md} +0 -0
- /package/plugins/src/phaser/rules/{phaser.md → reference/phaser.md} +0 -0
- /package/plugins/src/rails/rules/{rails-conventions.md → reference/rails-conventions.md} +0 -0
|
@@ -113,6 +113,27 @@ set -euo pipefail
|
|
|
113
113
|
|
|
114
114
|
input="$(cat)"
|
|
115
115
|
|
|
116
|
+
# Evaluate once per tool call when this guard is registered on both channels.
|
|
117
|
+
#
|
|
118
|
+
# Lisa reaches an agent through the repository dispatcher AND the plugin
|
|
119
|
+
# manifest, and where both are live the harness runs this guard twice for one
|
|
120
|
+
# tool call. `guard-dedupe.bash` short-circuits the second run ONLY when a
|
|
121
|
+
# byte-identical copy already ALLOWED this exact payload on this exact tool
|
|
122
|
+
# call; a differing vintage, a refusal, and a host with one channel all
|
|
123
|
+
# evaluate exactly as before. Nothing is de-registered by it
|
|
124
|
+
# (CodySwannGT/lisa#3814).
|
|
125
|
+
#
|
|
126
|
+
# Absent library means no dedupe, which is the pre-existing behaviour, so an
|
|
127
|
+
# older channel copy that predates it is unaffected.
|
|
128
|
+
lisa_guard_hook_dir="${BASH_SOURCE[0]%/*}"
|
|
129
|
+
lisa_guard_dedupe_lib="$lisa_guard_hook_dir/guard-dedupe.bash"
|
|
130
|
+
if [ -r "$lisa_guard_dedupe_lib" ]; then
|
|
131
|
+
# shellcheck source=guard-dedupe.bash
|
|
132
|
+
. "$lisa_guard_dedupe_lib"
|
|
133
|
+
trap 'lisa_guard_dedupe_record $?' EXIT
|
|
134
|
+
lisa_guard_dedupe block-direct-issue-create "$input"
|
|
135
|
+
fi
|
|
136
|
+
|
|
116
137
|
# Probe both interpreters before use and announce a missing one rather than
|
|
117
138
|
# swallowing it. Under `set -e` an absent jq aborts with 127, and Claude Code
|
|
118
139
|
# treats any non-2 exit as a NON-BLOCKING hook error — so the guard would
|
|
@@ -329,10 +350,66 @@ EOF
|
|
|
329
350
|
esac
|
|
330
351
|
}
|
|
331
352
|
|
|
353
|
+
# The branch-specific half of the remediation, printed only for the branch that
|
|
354
|
+
# fired. Every refusal this guard issues has to name an inverse the author can
|
|
355
|
+
# actually perform — a refusal with none is a state change with no inverse, and
|
|
356
|
+
# on a file-contents refusal it made a path permanently unnameable to any
|
|
357
|
+
# command the guard did not recognise as a reader (CodySwannGT/lisa#3683).
|
|
358
|
+
#
|
|
359
|
+
# NEITHER PARAGRAPH IS AN ESCAPE HATCH, and that is deliberate. `3594` records
|
|
360
|
+
# that a PreToolUse guard whose escape the guarded agent can set is not a
|
|
361
|
+
# control, so nothing here is settable: the only inverses named are edits to
|
|
362
|
+
# the refused command itself, and performing either one leaves a genuine
|
|
363
|
+
# undeclared creation refused by the checks above. Re-quoting an unlexable
|
|
364
|
+
# creation hands it to the parsed path, which refuses it; naming a document to
|
|
365
|
+
# a reader files nothing, which is the whole point. The one true escape stays
|
|
366
|
+
# what it was — an ambient variable a human exports before the session, which a
|
|
367
|
+
# tool-call shell cannot reach because its exports do not survive into this
|
|
368
|
+
# hook's environment.
|
|
369
|
+
remedy_hint() {
|
|
370
|
+
case "${1:-}" in
|
|
371
|
+
unparseable)
|
|
372
|
+
cat <<'EOF'
|
|
373
|
+
|
|
374
|
+
THIS COMMAND DID NOT LEX, so it was judged by pattern rather than by parse. An
|
|
375
|
+
unbalanced quote is the cause, and an apostrophe in ordinary prose is the
|
|
376
|
+
measured one. The pattern cannot separate a sentence about filing from a real
|
|
377
|
+
filing hidden behind a trailing `#` comment, so it refuses both.
|
|
378
|
+
|
|
379
|
+
THE INVERSE IS EXECUTABLE: re-quote the command so it lexes, and run it again.
|
|
380
|
+
It is not a bypass — a re-quoted creation is handed to the full parser, which
|
|
381
|
+
refuses it unless it declares readiness the ordinary way.
|
|
382
|
+
|
|
383
|
+
A command whose every segment runs a known READER is not refused on this branch
|
|
384
|
+
at all, so `cat`, `grep`, `ls`, `cp`, `mv`, `git` and `echo` never reach it.
|
|
385
|
+
EOF
|
|
386
|
+
;;
|
|
387
|
+
file)
|
|
388
|
+
cat <<'EOF'
|
|
389
|
+
|
|
390
|
+
THIS REFUSAL CAME FROM A FILE THIS COMMAND RUNS. A path is opened only when the
|
|
391
|
+
command puts it in a COMMAND position; a program that takes it as data — `cat`,
|
|
392
|
+
`grep`, `ls`, `cp`, `mv`, `git`, a test runner — never reaches this branch. Two
|
|
393
|
+
inverses, both executable:
|
|
394
|
+
|
|
395
|
+
- The file WRITES a payload locally and cannot transmit it (no HTTP client,
|
|
396
|
+
no CLI, no process spawn): it is read as data, not as a filing. Drop the
|
|
397
|
+
transmitting primitive, or slice the payload out of an existing file
|
|
398
|
+
instead of restating it here.
|
|
399
|
+
- The file produces no local artifact either — it is a DOCUMENT that quotes a
|
|
400
|
+
creation rather than a program that performs one. Name it to a reader
|
|
401
|
+
rather than to an interpreter; rewording its contents is not the remedy and
|
|
402
|
+
does not converge.
|
|
403
|
+
EOF
|
|
404
|
+
;;
|
|
405
|
+
esac
|
|
406
|
+
}
|
|
407
|
+
|
|
332
408
|
refuse() {
|
|
333
409
|
local signature="$1"
|
|
334
410
|
local roles="$2"
|
|
335
411
|
local target="$3"
|
|
412
|
+
local remedy="${4:-}"
|
|
336
413
|
if [ -n "$target" ]; then
|
|
337
414
|
refuse_cross_repo "$signature" "$roles" "$target"
|
|
338
415
|
fi
|
|
@@ -403,6 +480,8 @@ WHERE THE DECLARATION IS READ FROM: argv, the request payload — inline, in a
|
|
|
403
480
|
this command runs. Moving the create into a file no longer moves it out of
|
|
404
481
|
sight, so the declaration can live wherever the create does.
|
|
405
482
|
|
|
483
|
+
$(remedy_hint "$remedy")
|
|
484
|
+
|
|
406
485
|
OPERATOR ESCAPE: a human can export \`LISA_ALLOW_DIRECT_ISSUE_CREATE=1\` in the
|
|
407
486
|
environment before starting the session. It is deliberately not reachable by
|
|
408
487
|
setting it inline on this command — an inline assignment is refused.
|
|
@@ -782,6 +861,84 @@ TRACKER_ENDPOINT = re.compile(
|
|
|
782
861
|
re.IGNORECASE,
|
|
783
862
|
)
|
|
784
863
|
|
|
864
|
+
# ---------------------------------------------------------------------------
|
|
865
|
+
# A PAYLOAD HELD AS DATA IS NOT A SUBMISSION
|
|
866
|
+
#
|
|
867
|
+
# The conjunction above — an endpoint AND a creation verb in one file — cannot
|
|
868
|
+
# tell a payload being WRITTEN AS TEST DATA from one about to be SUBMITTED.
|
|
869
|
+
# Measured (CodySwannGT/lisa#3943): a helper that assembles test fixtures, and
|
|
870
|
+
# holds a mutation body as a string constant it writes to a fixture file, was
|
|
871
|
+
# refused as "a tracker creation inside <path>". It submits nothing. The same
|
|
872
|
+
# helper rewritten to SLICE the payload out of an existing file was allowed —
|
|
873
|
+
# so the discriminator was the literal, not the behaviour.
|
|
874
|
+
#
|
|
875
|
+
# The behaviour that separates them is EGRESS. A file can only file an issue if
|
|
876
|
+
# it can hand its bytes to another host or another process. A file with no such
|
|
877
|
+
# primitive anywhere in it cannot submit whatever its constants spell, so the
|
|
878
|
+
# conjunction is reading data.
|
|
879
|
+
#
|
|
880
|
+
# BOTH HALVES ARE REQUIRED, and the second one is why this is not a hole. The
|
|
881
|
+
# exemption needs POSITIVE evidence that the file produces a local artifact
|
|
882
|
+
# (`LOCAL_WRITE`) as well as the ABSENCE of egress, so it is bounded to the
|
|
883
|
+
# measured population — a fixture writer — rather than granted to any file that
|
|
884
|
+
# happens to use a transport nobody enumerated. Egress is over-matched on
|
|
885
|
+
# purpose: every miss in `PAYLOAD_EGRESS` widens the exemption, so the list
|
|
886
|
+
# reaches for `.post(`-shaped calls and process spawning as well as named HTTP
|
|
887
|
+
# clients, and a file doing both is refused. That is the guard's usual
|
|
888
|
+
# direction of failure — a false refusal is reported, a false allow is silent.
|
|
889
|
+
#
|
|
890
|
+
# RESIDUAL, stated rather than hidden: a submitting file that ALSO writes a
|
|
891
|
+
# local artifact and reaches the network through a primitive no pattern here
|
|
892
|
+
# names is allowed. Nothing about `text_declares_readiness`, the nested shell
|
|
893
|
+
# scan, the unparseable arm, or the argv path is weakened — this reads only the
|
|
894
|
+
# coarse conjunction, which is the only arm that ever inferred a submission
|
|
895
|
+
# from contents alone.
|
|
896
|
+
PAYLOAD_EGRESS = re.compile(
|
|
897
|
+
# Clients and trackers, by name.
|
|
898
|
+
r"\bcurl\b|\bwget\b|\bhttpie\b|\bgh\b|\bjira\b|\bacli\b"
|
|
899
|
+
r"|requests\.|httpx|aiohttp|pycurl|urllib|urlopen|http\.client|\bsocket\b"
|
|
900
|
+
r"|https?connection|httpurlconnection|httpclient|httprequest|webclient"
|
|
901
|
+
r"|okhttp|net::http|restclient|faraday|httparty|open-uri|\blwp\b|http::tiny"
|
|
902
|
+
r"|curl_init|curl_exec|guzzle|fsockopen|file_get_contents"
|
|
903
|
+
r"|\bfetch\s*\(|axios|xmlhttprequest|node-fetch|undici|superagent"
|
|
904
|
+
r"|http\.post|http\.newrequest"
|
|
905
|
+
# Handing the payload to another process, which can carry it anywhere.
|
|
906
|
+
r"|subprocess|os\.system|popen|child_process|execsync|spawnsync"
|
|
907
|
+
r"|\bspawn\s*\(|\bexec\s*\(|\bsystem\s*\(|bun\.spawn|deno\.command"
|
|
908
|
+
# The shape a send takes in almost any language, whatever the client is
|
|
909
|
+
# called. Over-matching here costs an exemption, never a refusal.
|
|
910
|
+
r"|\.post\s*\(|\.put\s*\(|\.patch\s*\(|\.request\s*\(|\.send\s*\("
|
|
911
|
+
r"|\.execute\s*\(|\.mutate\s*\(|\.do\s*\(",
|
|
912
|
+
re.IGNORECASE,
|
|
913
|
+
)
|
|
914
|
+
# Producing a local artifact: the positive half of the exemption.
|
|
915
|
+
LOCAL_WRITE = re.compile(
|
|
916
|
+
r"\.write\s*\(|\.writelines\s*\(|write_text\s*\(|write_bytes\s*\("
|
|
917
|
+
r"|writefilesync|writefile\s*\(|createwritestream|outputstream"
|
|
918
|
+
r"|ioutil\.writefile|\btee\b"
|
|
919
|
+
# A shell redirect. Anchored on a separator so `=>` and `->` are not read
|
|
920
|
+
# as one, and required to be followed by something path-shaped.
|
|
921
|
+
r"|(?:^|[\s;&|(])>>?\s*[\"']?[\w./$~-]",
|
|
922
|
+
re.IGNORECASE | re.MULTILINE,
|
|
923
|
+
)
|
|
924
|
+
|
|
925
|
+
|
|
926
|
+
def payload_is_inert(text):
|
|
927
|
+
"""Whether this file HOLDS a creation payload rather than submitting one.
|
|
928
|
+
|
|
929
|
+
Args:
|
|
930
|
+
text: The file's contents.
|
|
931
|
+
|
|
932
|
+
Returns:
|
|
933
|
+
True when the file writes a local artifact and carries no primitive
|
|
934
|
+
capable of transmitting anything.
|
|
935
|
+
"""
|
|
936
|
+
return (
|
|
937
|
+
LOCAL_WRITE.search(text) is not None
|
|
938
|
+
and PAYLOAD_EGRESS.search(text) is None
|
|
939
|
+
)
|
|
940
|
+
|
|
941
|
+
|
|
785
942
|
# ---------------------------------------------------------------------------
|
|
786
943
|
# DECLARING BUILD-READY WHEN THE ROLE IS A STATE AND NOT A LABEL
|
|
787
944
|
#
|
|
@@ -1814,11 +1971,23 @@ def is_assignment_word(token):
|
|
|
1814
1971
|
# harm #3705 records, because it can block the verification of a fix while
|
|
1815
1972
|
# saying nothing about why.
|
|
1816
1973
|
#
|
|
1974
|
+
# RELOCATION AND ECHOING ARE READS TOO, and leaving them off is what turned a
|
|
1975
|
+
# false positive into a property of the repository (CodySwannGT/lisa#3683). A
|
|
1976
|
+
# file that QUOTES a creation was already reachable by `grep`, `cat` and `ls`
|
|
1977
|
+
# once this list existed — but not by `cp` or `mv`, so the file could be read
|
|
1978
|
+
# and never moved, by any agent and by CI, for as long as the literal stayed in
|
|
1979
|
+
# it. The measured case shows why "reword it" is not the inverse: the refusal
|
|
1980
|
+
# recurred AFTER the content had been reworded, on a different file quoting the
|
|
1981
|
+
# same command. None of these four executes an operand: `cp` and `mv` copy
|
|
1982
|
+
# bytes, `echo` and `printf` write them to stdout. They are also the vocabulary
|
|
1983
|
+
# `unparseable_reads_only` answers with when the text could not be lexed at all.
|
|
1984
|
+
#
|
|
1817
1985
|
# RESIDUAL, stated rather than hidden: a reader NOT on this list is still
|
|
1818
1986
|
# followed and can still over-refuse. The set of read-only tools is unbounded,
|
|
1819
1987
|
# so this closes the measured population and not the class. Add names here as
|
|
1820
1988
|
# they are measured; do not invert the default to close it by fiat, or the
|
|
1821
1989
|
# executed-script reach that #3484 bought is lost.
|
|
1990
|
+
#
|
|
1822
1991
|
# The copy/move/link family is measured, not inferred: `cp <a file that files>`
|
|
1823
1992
|
# was refused as "an unparseable command that reads as a tracker creation
|
|
1824
1993
|
# inside <path>" (CodySwannGT/lisa#3683, trip 5, reproduced in this guard's own
|
|
@@ -1834,11 +2003,12 @@ def is_assignment_word(token):
|
|
|
1834
2003
|
# standing instruction to add on measurement rather than by sweep.
|
|
1835
2004
|
READ_ONLY_PROGRAMS = {
|
|
1836
2005
|
"awk", "bat", "cat", "cksum", "cmp", "column", "comm", "cp", "cut",
|
|
1837
|
-
"diff", "du", "file", "fold", "git", "grep", "head",
|
|
1838
|
-
"install", "jest", "jq", "less", "ln", "ls", "md5",
|
|
1839
|
-
"
|
|
1840
|
-
"
|
|
1841
|
-
"uniq", "vitest", "wc", "xxd",
|
|
2006
|
+
"diff", "du", "echo", "file", "fold", "git", "grep", "head",
|
|
2007
|
+
"hexdump", "install", "jest", "jq", "less", "ln", "ls", "md5",
|
|
2008
|
+
"md5sum", "more", "mv", "nl", "od", "printf", "pytest", "rg",
|
|
2009
|
+
"sed", "sha1sum", "sha256sum", "shellcheck", "shfmt", "sort",
|
|
2010
|
+
"stat", "strings", "tail", "tee", "uniq", "vitest", "wc", "xxd",
|
|
2011
|
+
"yamllint",
|
|
1842
2012
|
}
|
|
1843
2013
|
|
|
1844
2014
|
|
|
@@ -1879,6 +2049,67 @@ def executing_command(argv):
|
|
|
1879
2049
|
return (None, None, [])
|
|
1880
2050
|
|
|
1881
2051
|
|
|
2052
|
+
# Operators that end a command in raw shell text. Used ONLY where `shlex` has
|
|
2053
|
+
# already refused the text, so a quote-aware split is not available — and
|
|
2054
|
+
# OVER-splitting is the safe direction here, because an extra segment can only
|
|
2055
|
+
# add a command word to check, never remove one.
|
|
2056
|
+
RAW_SEGMENT_SPLIT = re.compile(r"&&|\|\||[;|&\n]")
|
|
2057
|
+
|
|
2058
|
+
|
|
2059
|
+
def raw_command_words(text):
|
|
2060
|
+
"""The program each segment of unlexable text puts in command position.
|
|
2061
|
+
|
|
2062
|
+
Answered on a whitespace split because the reason this path exists is that
|
|
2063
|
+
`shlex` refused the text. It still routes through `executing_command`, so a
|
|
2064
|
+
leading assignment and a wrapper — `nice`, `env`, `sudo`, `timeout` — are
|
|
2065
|
+
stepped over here exactly as they are on the parsed path. Reading the
|
|
2066
|
+
wrapper as the command is the bypass this guard's own comments record.
|
|
2067
|
+
|
|
2068
|
+
Args:
|
|
2069
|
+
text: The raw command string.
|
|
2070
|
+
|
|
2071
|
+
Returns:
|
|
2072
|
+
One entry per non-empty segment: the resolved program name, or None
|
|
2073
|
+
when the segment names none.
|
|
2074
|
+
"""
|
|
2075
|
+
words = []
|
|
2076
|
+
for chunk in RAW_SEGMENT_SPLIT.split(text):
|
|
2077
|
+
tokens = chunk.split()
|
|
2078
|
+
if not tokens:
|
|
2079
|
+
continue
|
|
2080
|
+
words.append(executing_command(tokens)[1])
|
|
2081
|
+
return words
|
|
2082
|
+
|
|
2083
|
+
|
|
2084
|
+
def unparseable_reads_only(text):
|
|
2085
|
+
"""Whether unlexable text only READS, and therefore files nothing.
|
|
2086
|
+
|
|
2087
|
+
`UNPARSEABLE_CREATION` matches raw text, so it cannot tell a filing from a
|
|
2088
|
+
sentence about one. `echo the guard's <creation> behaviour` fails to lex
|
|
2089
|
+
for the apostrophe alone, and was refused as a tracker creation — a command
|
|
2090
|
+
that files nothing, told that "this filing declares no readiness", with no
|
|
2091
|
+
printed remedy that applied to any part of it (CodySwannGT/lisa#3683).
|
|
2092
|
+
|
|
2093
|
+
The fallback is NOT removable and is not narrowed here. `<creation> #'` is
|
|
2094
|
+
a real filing that bash runs and `shlex` rejects, so "I could not parse it"
|
|
2095
|
+
must keep meaning refuse. What this asks instead is the COMMAND POSITION
|
|
2096
|
+
question the parsed path already asks and this arm skipped: only a command
|
|
2097
|
+
position can run anything, and a reader takes its operands as data.
|
|
2098
|
+
|
|
2099
|
+
Fails closed on everything else, which is the same asymmetry
|
|
2100
|
+
`READ_ONLY_PROGRAMS` documents: an unresolvable command word, an
|
|
2101
|
+
unrecognised program, or a text with no segments at all is refused.
|
|
2102
|
+
|
|
2103
|
+
Args:
|
|
2104
|
+
text: The raw command string that would not lex.
|
|
2105
|
+
|
|
2106
|
+
Returns:
|
|
2107
|
+
True when every segment's command word is a known reader.
|
|
2108
|
+
"""
|
|
2109
|
+
words = raw_command_words(text)
|
|
2110
|
+
return bool(words) and all(word in READ_ONLY_PROGRAMS for word in words)
|
|
2111
|
+
|
|
2112
|
+
|
|
1882
2113
|
def executed_operand(argv):
|
|
1883
2114
|
"""The path this command EXECUTES, or None.
|
|
1884
2115
|
|
|
@@ -2021,7 +2252,9 @@ def file_creation(text, depth):
|
|
|
2021
2252
|
`node wrapper.mjs`, a Python client, or anything else that speaks HTTP
|
|
2022
2253
|
directly — it needs a tracker endpoint AND a creation verb in the same
|
|
2023
2254
|
file, which is what keeps a changelog that merely mentions `issueCreate`
|
|
2024
|
-
from reading as a creation.
|
|
2255
|
+
from reading as a creation. It also needs the file to be capable of
|
|
2256
|
+
SENDING what it spells: a helper that writes the same payload into a test
|
|
2257
|
+
fixture submits nothing. See `payload_is_inert`.
|
|
2025
2258
|
|
|
2026
2259
|
Args:
|
|
2027
2260
|
text: The file's contents.
|
|
@@ -2041,6 +2274,9 @@ def file_creation(text, depth):
|
|
|
2041
2274
|
GRAPHQL_CREATE.search(text)
|
|
2042
2275
|
and TRACKER_ENDPOINT.search(text)
|
|
2043
2276
|
and not text_declares_readiness(text)
|
|
2277
|
+
# A payload the file WRITES rather than SENDS is data. See
|
|
2278
|
+
# `payload_is_inert` for why the absence of egress alone is not enough.
|
|
2279
|
+
and not payload_is_inert(text)
|
|
2044
2280
|
):
|
|
2045
2281
|
return "a tracker creation", [ready_role], None
|
|
2046
2282
|
return None
|
|
@@ -2095,6 +2331,12 @@ def scan(text, depth, from_file=False):
|
|
|
2095
2331
|
# reachable by declaring inline instead, which this change makes work.
|
|
2096
2332
|
if text_declares_readiness(text):
|
|
2097
2333
|
return None
|
|
2334
|
+
# The command-position arm of the same reasoning `executed_operand`
|
|
2335
|
+
# applies. A text every one of whose segments runs a known READER
|
|
2336
|
+
# cannot file anything, whatever its prose says — see
|
|
2337
|
+
# `unparseable_reads_only` for why this does not narrow the fallback.
|
|
2338
|
+
if unparseable_reads_only(text):
|
|
2339
|
+
return None
|
|
2098
2340
|
if UNPARSEABLE_CREATION.search(text):
|
|
2099
2341
|
return (
|
|
2100
2342
|
"an unparseable command that reads as a tracker creation",
|
|
@@ -2199,6 +2441,30 @@ def scan(text, depth, from_file=False):
|
|
|
2199
2441
|
# a human one retry and a false negative costs the entire control.
|
|
2200
2442
|
inline_override = (OVERRIDE_NAME + "=") in command
|
|
2201
2443
|
|
|
2444
|
+
|
|
2445
|
+
def remedy_for(signature):
|
|
2446
|
+
"""Which extra remediation paragraph this refusal earns.
|
|
2447
|
+
|
|
2448
|
+
A guard that prints every remedy it knows prints one that does not apply,
|
|
2449
|
+
and a remedy that cannot be performed is the failure this ticket is about
|
|
2450
|
+
(CodySwannGT/lisa#3683): the file-payload paragraph told an author to drop
|
|
2451
|
+
a transmitting primitive that a DOCUMENT does not have, and the
|
|
2452
|
+
unparseable arm printed declaration advice for a command that files
|
|
2453
|
+
nothing. Both are now keyed off which branch actually fired.
|
|
2454
|
+
|
|
2455
|
+
Args:
|
|
2456
|
+
signature: The refusal signature.
|
|
2457
|
+
|
|
2458
|
+
Returns:
|
|
2459
|
+
A key the shell half maps to a paragraph, or an empty string.
|
|
2460
|
+
"""
|
|
2461
|
+
if signature.startswith("an unparseable command"):
|
|
2462
|
+
return "unparseable"
|
|
2463
|
+
if " inside " in signature:
|
|
2464
|
+
return "file"
|
|
2465
|
+
return ""
|
|
2466
|
+
|
|
2467
|
+
|
|
2202
2468
|
found = scan(command, 0)
|
|
2203
2469
|
if found is not None:
|
|
2204
2470
|
signature, roles, target = found
|
|
@@ -2209,6 +2475,7 @@ if found is not None:
|
|
|
2209
2475
|
print("signature=%s" % signature)
|
|
2210
2476
|
print("roles=%s" % ", ".join(role for role in roles if role))
|
|
2211
2477
|
print("target=%s" % (target or ""))
|
|
2478
|
+
print("remedy=%s" % remedy_for(signature))
|
|
2212
2479
|
sys.exit(0)
|
|
2213
2480
|
|
|
2214
2481
|
print("ALLOW")
|
|
@@ -2239,11 +2506,28 @@ if [ -n "$structured_call" ]; then
|
|
|
2239
2506
|
exit 0
|
|
2240
2507
|
fi
|
|
2241
2508
|
|
|
2509
|
+
# A PACKED label string counts. Exact equality against the flattened value
|
|
2510
|
+
# list reads `labels: ["status:ready"]` and nothing else, but the same
|
|
2511
|
+
# compliant filing spelled `labels: "status:ready,type:Bug"` — the shape
|
|
2512
|
+
# `gh issue create --label` takes, and the shape several MCP servers pass
|
|
2513
|
+
# through verbatim — carries no value equal to the role, so the guard
|
|
2514
|
+
# refused a filing that had declared exactly what it demanded. A false
|
|
2515
|
+
# positive in a guard costs more than a miss: it teaches the operator that
|
|
2516
|
+
# the guard is wrong, and the next refusal is argued with rather than
|
|
2517
|
+
# obeyed.
|
|
2518
|
+
#
|
|
2519
|
+
# Split on the DELIMITERS a packed list uses (comma, semicolon, newline) and
|
|
2520
|
+
# trimmed, never on a `contains` match. `contains` would accept a body that
|
|
2521
|
+
# merely mentions the role in prose — "do not mark this status:ready" — and
|
|
2522
|
+
# that is a fail-open on the one question this path exists to answer.
|
|
2242
2523
|
structured_declaration="$(
|
|
2243
2524
|
printf '%s' "$input" |
|
|
2244
2525
|
jq -r --arg role "$ready_role" '
|
|
2245
2526
|
[(.tool_input // {}) | .. | strings] as $values
|
|
2246
|
-
|
|
|
2527
|
+
| ($values + ($values
|
|
2528
|
+
| map(splits("[,;\n]"))
|
|
2529
|
+
| map(sub("^\\s+"; "") | sub("\\s+$"; "")))) as $atoms
|
|
2530
|
+
| if ($atoms | index($role)) then "role"
|
|
2247
2531
|
elif ($values | map(select(contains("[lisa-human-gate]"))) | length) > 0 then "gate"
|
|
2248
2532
|
else "" end
|
|
2249
2533
|
' 2>/dev/null || printf 'UNREADABLE'
|
|
@@ -2296,7 +2580,8 @@ case "$verdict" in
|
|
|
2296
2580
|
refuse \
|
|
2297
2581
|
"$(verdict_field signature)" \
|
|
2298
2582
|
"$(verdict_field roles)" \
|
|
2299
|
-
"$(verdict_field target)"
|
|
2583
|
+
"$(verdict_field target)" \
|
|
2584
|
+
"$(verdict_field remedy)"
|
|
2300
2585
|
;;
|
|
2301
2586
|
esac
|
|
2302
2587
|
|
|
@@ -31,6 +31,27 @@ set -euo pipefail
|
|
|
31
31
|
|
|
32
32
|
input="$(cat)"
|
|
33
33
|
|
|
34
|
+
# Evaluate once per tool call when this guard is registered on both channels.
|
|
35
|
+
#
|
|
36
|
+
# Lisa reaches an agent through the repository dispatcher AND the plugin
|
|
37
|
+
# manifest, and where both are live the harness runs this guard twice for one
|
|
38
|
+
# tool call. `guard-dedupe.bash` short-circuits the second run ONLY when a
|
|
39
|
+
# byte-identical copy already ALLOWED this exact payload on this exact tool
|
|
40
|
+
# call; a differing vintage, a refusal, and a host with one channel all
|
|
41
|
+
# evaluate exactly as before. Nothing is de-registered by it
|
|
42
|
+
# (CodySwannGT/lisa#3814).
|
|
43
|
+
#
|
|
44
|
+
# Absent library means no dedupe, which is the pre-existing behaviour, so an
|
|
45
|
+
# older channel copy that predates it is unaffected.
|
|
46
|
+
lisa_guard_hook_dir="${BASH_SOURCE[0]%/*}"
|
|
47
|
+
lisa_guard_dedupe_lib="$lisa_guard_hook_dir/guard-dedupe.bash"
|
|
48
|
+
if [ -r "$lisa_guard_dedupe_lib" ]; then
|
|
49
|
+
# shellcheck source=guard-dedupe.bash
|
|
50
|
+
. "$lisa_guard_dedupe_lib"
|
|
51
|
+
trap 'lisa_guard_dedupe_record $?' EXIT
|
|
52
|
+
lisa_guard_dedupe block-instruction-file-edits "$input"
|
|
53
|
+
fi
|
|
54
|
+
|
|
34
55
|
command -v jq >/dev/null 2>&1 || exit 0
|
|
35
56
|
|
|
36
57
|
# The operator's override. Checked before anything else so it is always the
|
|
@@ -70,25 +70,57 @@
|
|
|
70
70
|
# propagate any further either — a path a followed script merely NAMES is data
|
|
71
71
|
# one file further out, not a third hop.
|
|
72
72
|
#
|
|
73
|
-
# ## Every agent surface reaches this guard
|
|
73
|
+
# ## Every agent surface reaches this guard, and Codex reaches it twice over
|
|
74
74
|
#
|
|
75
|
-
# There is no parity gap left to record. A note here used to record one
|
|
76
|
-
#
|
|
77
|
-
#
|
|
78
|
-
#
|
|
79
|
-
#
|
|
80
|
-
#
|
|
81
|
-
#
|
|
82
|
-
#
|
|
75
|
+
# There is no parity gap left to record. A note here used to record one, and it
|
|
76
|
+
# was wrong by the time anyone read it — through two wordings. The first named
|
|
77
|
+
# three surfaces; every one of those ports then shipped and the note stayed
|
|
78
|
+
# unedited. The second named Codex alone, and Codex had never been the gap
|
|
79
|
+
# either: the note and the check that read it were both looking in
|
|
80
|
+
# `src/codex/scripts/`, and `src/codex/hooks-installer.ts` says in its own
|
|
81
|
+
# opening remark that the linked-script layout that directory serves is RETIRED
|
|
82
|
+
# (CodySwannGT/lisa#3750).
|
|
83
|
+
#
|
|
84
|
+
# The note is deleted rather than trimmed, because a stale gap note is worse
|
|
85
|
+
# than none: it OVERSTATES the gap, and a reader who trusts it goes looking for
|
|
86
|
+
# three missing ports, finds two present, and cannot tell which third is real
|
|
87
|
+
# without redoing the measurement from scratch. AGENTS.md asks for a gap to be
|
|
88
|
+
# documented instead of silently dropped — that only works while the document
|
|
89
|
+
# is true.
|
|
90
|
+
#
|
|
91
|
+
# The deeper lesson is not "the note was stale". It is that a note asserting
|
|
92
|
+
# ABSENCE and a check deriving presence from ONE path shape agree with each
|
|
93
|
+
# other for free — the check read the same retired directory the note did, so
|
|
94
|
+
# it returned a clean tick on a claim that was false.
|
|
83
95
|
#
|
|
84
96
|
# Where each surface picks it up, so the next reader measures instead of
|
|
85
97
|
# guessing: Claude and Copilot from `.claude-plugin/plugin.json`, Cursor from
|
|
86
|
-
# `hooks/hooks.json`,
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
#
|
|
90
|
-
#
|
|
91
|
-
#
|
|
98
|
+
# `hooks/hooks.json`, Antigravity from `hooks.json` via the `.agy.sh` adapter
|
|
99
|
+
# beside this file, OpenCode from
|
|
100
|
+
# `src/opencode/plugin-templates/lisa-block-managed-file-edits.ts`, and Codex
|
|
101
|
+
# from TWO channels rather than one:
|
|
102
|
+
#
|
|
103
|
+
# `plugins/lisa/.codex-plugin/hooks.json` registers this guard directly, on
|
|
104
|
+
# the Codex plugin channel.
|
|
105
|
+
#
|
|
106
|
+
# `scripts/lisa-enforcement-fallback.sh` names this guard in its roster, and
|
|
107
|
+
# `src/codex/enforcement-fallback-installer.ts` registers that dispatcher on
|
|
108
|
+
# `PreToolUse` for `Bash|Edit|Write|apply_patch` — which is how Codex's
|
|
109
|
+
# `apply_patch` writes are reached at all.
|
|
110
|
+
#
|
|
111
|
+
# Both Codex channels are measured rather than inferred — see
|
|
112
|
+
# `tests/unit/codex/block-managed-file-edits-codex.test.ts`, which drives the
|
|
113
|
+
# registered command as a subprocess in a synthetic host project: a Codex
|
|
114
|
+
# `apply_patch` or `Bash` redirect at a copy-overwrite template exits 2; an
|
|
115
|
+
# ordinary edit to a host-owned file exits 0.
|
|
116
|
+
#
|
|
117
|
+
# Two checks hold this paragraph to the tree.
|
|
118
|
+
# `tests/unit/hooks/managed-file-guard-parity-note.test.ts` fails if it goes
|
|
119
|
+
# stale in either direction — a port that ships without the note being
|
|
120
|
+
# narrowed, and a port REMOVED without it being restored.
|
|
121
|
+
# `check:guard-parity-notes` refuses an absence claim written back in, and now
|
|
122
|
+
# resolves Codex from those two registration channels as well as from a path
|
|
123
|
+
# shape, so it can no longer agree with a false note for free.
|
|
92
124
|
#
|
|
93
125
|
# Exemptions (allowed):
|
|
94
126
|
# - `LISA_ALLOW_MANAGED_FILE_WRITE` set — the operator's explicit override,
|
|
@@ -107,6 +139,27 @@ set -euo pipefail
|
|
|
107
139
|
|
|
108
140
|
input="$(cat)"
|
|
109
141
|
|
|
142
|
+
# Evaluate once per tool call when this guard is registered on both channels.
|
|
143
|
+
#
|
|
144
|
+
# Lisa reaches an agent through the repository dispatcher AND the plugin
|
|
145
|
+
# manifest, and where both are live the harness runs this guard twice for one
|
|
146
|
+
# tool call. `guard-dedupe.bash` short-circuits the second run ONLY when a
|
|
147
|
+
# byte-identical copy already ALLOWED this exact payload on this exact tool
|
|
148
|
+
# call; a differing vintage, a refusal, and a host with one channel all
|
|
149
|
+
# evaluate exactly as before. Nothing is de-registered by it
|
|
150
|
+
# (CodySwannGT/lisa#3814).
|
|
151
|
+
#
|
|
152
|
+
# Absent library means no dedupe, which is the pre-existing behaviour, so an
|
|
153
|
+
# older channel copy that predates it is unaffected.
|
|
154
|
+
lisa_guard_hook_dir="${BASH_SOURCE[0]%/*}"
|
|
155
|
+
lisa_guard_dedupe_lib="$lisa_guard_hook_dir/guard-dedupe.bash"
|
|
156
|
+
if [ -r "$lisa_guard_dedupe_lib" ]; then
|
|
157
|
+
# shellcheck source=guard-dedupe.bash
|
|
158
|
+
. "$lisa_guard_dedupe_lib"
|
|
159
|
+
trap 'lisa_guard_dedupe_record $?' EXIT
|
|
160
|
+
lisa_guard_dedupe block-managed-file-edits "$input"
|
|
161
|
+
fi
|
|
162
|
+
|
|
110
163
|
command -v jq >/dev/null 2>&1 || exit 0
|
|
111
164
|
|
|
112
165
|
# The operator's override, checked first so it is always the cheapest way out.
|
|
@@ -547,6 +600,16 @@ SOURCE_BUILTINS = {"source", "."}
|
|
|
547
600
|
FOLLOW_WRAPPERS = {
|
|
548
601
|
"builtin": (frozenset(), 0),
|
|
549
602
|
"command": (frozenset(), 0),
|
|
603
|
+
# `env bash edit.sh` runs `bash edit.sh`. Its absence here was the one
|
|
604
|
+
# wrapper on this list a caller reaches for without thinking about
|
|
605
|
+
# wrappers at all, because `env VAR=value <cmd>` is how a one-off
|
|
606
|
+
# environment override is spelled. Those `VAR=value` operands need no
|
|
607
|
+
# positional count: the walk below re-enters its assignment skip on the
|
|
608
|
+
# next iteration and steps over as many as are present.
|
|
609
|
+
"env": (
|
|
610
|
+
frozenset({"-C", "--chdir", "-P", "-S", "--split-string", "-u", "--unset"}),
|
|
611
|
+
0,
|
|
612
|
+
),
|
|
550
613
|
"exec": (frozenset({"-a"}), 0),
|
|
551
614
|
"nice": (frozenset({"-n", "--adjustment"}), 0),
|
|
552
615
|
"nohup": (frozenset(), 0),
|
|
@@ -91,6 +91,27 @@ set -euo pipefail
|
|
|
91
91
|
|
|
92
92
|
input="$(cat)"
|
|
93
93
|
|
|
94
|
+
# Evaluate once per tool call when this guard is registered on both channels.
|
|
95
|
+
#
|
|
96
|
+
# Lisa reaches an agent through the repository dispatcher AND the plugin
|
|
97
|
+
# manifest, and where both are live the harness runs this guard twice for one
|
|
98
|
+
# tool call. `guard-dedupe.bash` short-circuits the second run ONLY when a
|
|
99
|
+
# byte-identical copy already ALLOWED this exact payload on this exact tool
|
|
100
|
+
# call; a differing vintage, a refusal, and a host with one channel all
|
|
101
|
+
# evaluate exactly as before. Nothing is de-registered by it
|
|
102
|
+
# (CodySwannGT/lisa#3814).
|
|
103
|
+
#
|
|
104
|
+
# Absent library means no dedupe, which is the pre-existing behaviour, so an
|
|
105
|
+
# older channel copy that predates it is unaffected.
|
|
106
|
+
lisa_guard_hook_dir="${BASH_SOURCE[0]%/*}"
|
|
107
|
+
lisa_guard_dedupe_lib="$lisa_guard_hook_dir/guard-dedupe.bash"
|
|
108
|
+
if [ -r "$lisa_guard_dedupe_lib" ]; then
|
|
109
|
+
# shellcheck source=guard-dedupe.bash
|
|
110
|
+
. "$lisa_guard_dedupe_lib"
|
|
111
|
+
trap 'lisa_guard_dedupe_record $?' EXIT
|
|
112
|
+
lisa_guard_dedupe block-no-verify "$input"
|
|
113
|
+
fi
|
|
114
|
+
|
|
94
115
|
# Both interpreters must be probed BEFORE they are used, and a missing one must
|
|
95
116
|
# be announced rather than swallowed.
|
|
96
117
|
#
|
|
@@ -24,6 +24,27 @@ set -euo pipefail
|
|
|
24
24
|
|
|
25
25
|
input="$(cat)"
|
|
26
26
|
|
|
27
|
+
# Evaluate once per tool call when this guard is registered on both channels.
|
|
28
|
+
#
|
|
29
|
+
# Lisa reaches an agent through the repository dispatcher AND the plugin
|
|
30
|
+
# manifest, and where both are live the harness runs this guard twice for one
|
|
31
|
+
# tool call. `guard-dedupe.bash` short-circuits the second run ONLY when a
|
|
32
|
+
# byte-identical copy already ALLOWED this exact payload on this exact tool
|
|
33
|
+
# call; a differing vintage, a refusal, and a host with one channel all
|
|
34
|
+
# evaluate exactly as before. Nothing is de-registered by it
|
|
35
|
+
# (CodySwannGT/lisa#3814).
|
|
36
|
+
#
|
|
37
|
+
# Absent library means no dedupe, which is the pre-existing behaviour, so an
|
|
38
|
+
# older channel copy that predates it is unaffected.
|
|
39
|
+
lisa_guard_hook_dir="${BASH_SOURCE[0]%/*}"
|
|
40
|
+
lisa_guard_dedupe_lib="$lisa_guard_hook_dir/guard-dedupe.bash"
|
|
41
|
+
if [ -r "$lisa_guard_dedupe_lib" ]; then
|
|
42
|
+
# shellcheck source=guard-dedupe.bash
|
|
43
|
+
. "$lisa_guard_dedupe_lib"
|
|
44
|
+
trap 'lisa_guard_dedupe_record $?' EXIT
|
|
45
|
+
lisa_guard_dedupe block-shell-json-parsing "$input"
|
|
46
|
+
fi
|
|
47
|
+
|
|
27
48
|
# Probe the interpreters before the first use. Under `set -euo pipefail` an
|
|
28
49
|
# absent jq does not merely skip the parse — the assignment below dies with
|
|
29
50
|
# 127, and 127 is not a refusal, so the guard vanishes and the command runs.
|