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