@codyswann/lisa 4.55.2 → 4.55.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/all/copy-overwrite/scripts/check-orphaned-branches.mjs +49 -13
- package/all/copy-overwrite/scripts/lisa-enforcement-fallback.sh +9 -1
- package/all/copy-overwrite/scripts/lisa-hooks/block-blind-automerge.sh +10 -5
- package/all/copy-overwrite/scripts/lisa-hooks/block-direct-issue-create.sh +4 -0
- package/all/copy-overwrite/scripts/lisa-hooks/block-managed-file-edits.sh +73 -2
- package/all/copy-overwrite/scripts/lisa-hooks/guard-dedupe.bash +63 -29
- package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +2 -1
- package/all/copy-overwrite/scripts/lisa-hooks/worktree-binding-guard.sh +2 -1
- package/all/copy-overwrite/scripts/lisa-run-gates.mjs +7 -2
- package/all/copy-overwrite/scripts/lisa-work-item.mjs +50 -14
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +36 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +92 -80
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/opencode/plugin-templates/block-managed-file-edits.sh +76 -2
- package/dist/opencode/plugin-templates/block-no-verify.sh +3 -0
- package/dist/opencode/plugin-templates/guard-dedupe.bash +66 -29
- package/dist/opencode/plugin-templates/parity-safety-net-heredoc.py +3 -0
- package/dist/opencode/plugin-templates/parity-safety-net.sh +5 -1
- package/dist/opencode/support-file-resolver.d.ts.map +1 -1
- package/dist/opencode/support-file-resolver.js +3 -1
- package/dist/opencode/support-file-resolver.js.map +1 -1
- package/expo/copy-overwrite/plugins/withAndroidSplashNoClientExit.js +23 -21
- package/expo/create-only/.github/required-checks.json +1 -1
- package/nestjs/create-only/.github/required-checks.json +1 -1
- package/package.json +4 -4
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-github-build-intake/SKILL.md +38 -15
- package/plugins/lisa/.codex-plugin/skills/lisa-github-write-issue/SKILL.md +15 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-build-intake/SKILL.md +37 -13
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-write-ticket/SKILL.md +15 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-access/SKILL.md +56 -7
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-build-intake/SKILL.md +36 -11
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-prd-intake/SKILL.md +12 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-validate-issue/SKILL.md +1 -18
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-verify/SKILL.md +0 -17
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-write-issue/SKILL.md +18 -35
- package/plugins/lisa/.codex-plugin/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-repair-intake/SKILL.md +66 -12
- package/plugins/lisa/.codex-plugin/skills/lisa-rework-triage/SKILL.md +2 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-linear/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
- package/plugins/lisa/.codex-plugin/skills/lisa-track/SKILL.md +32 -4
- package/plugins/lisa/.codex-plugin/skills/lisa-tracker-build-intake/SKILL.md +15 -1
- package/plugins/lisa/hooks/block-blind-automerge.sh +13 -5
- package/plugins/lisa/hooks/block-direct-issue-create.sh +7 -0
- package/plugins/lisa/hooks/block-instruction-file-edits.sh +3 -0
- package/plugins/lisa/hooks/block-managed-file-edits.sh +76 -2
- package/plugins/lisa/hooks/block-no-verify.sh +3 -0
- package/plugins/lisa/hooks/block-shell-json-parsing.sh +3 -0
- package/plugins/lisa/hooks/guard-dedupe.bash +66 -29
- package/plugins/lisa/hooks/parity-safety-net-heredoc.py +3 -0
- package/plugins/lisa/hooks/parity-safety-net.sh +5 -1
- package/plugins/lisa/hooks/worktree-binding-guard.mjs +3 -0
- package/plugins/lisa/hooks/worktree-binding-guard.sh +5 -1
- package/plugins/lisa/rules/eager/00-rule-index.md +1 -0
- package/plugins/lisa/rules/eager/base-rules.md +3 -1
- package/plugins/lisa/rules/eager/withdrawn-rulings.md +14 -3
- package/plugins/lisa/rules/reference/automation-runbook-contract.md +1 -1
- package/plugins/lisa/rules/reference/bdd-e2e-coverage.md +1 -1
- package/plugins/lisa/rules/reference/blocker-containment.md +3 -3
- package/plugins/lisa/rules/reference/claim-archaeology.md +1 -1
- package/plugins/lisa/rules/reference/claim-evidence-mapping.md +1 -1
- package/plugins/lisa/rules/reference/claim-time-guards.md +1 -1
- package/plugins/lisa/rules/reference/control-reachability.md +1 -1
- package/plugins/lisa/rules/reference/convergent-review.md +1 -1
- package/plugins/lisa/rules/reference/credential-substrate-precedence.md +1 -1
- package/plugins/lisa/rules/reference/dependency-decision-records.md +1 -1
- package/plugins/lisa/rules/reference/dependency-internalization-kit.md +1 -1
- package/plugins/lisa/rules/reference/dependency-trust-classes.md +1 -1
- package/plugins/lisa/rules/reference/deployed-state-readback.md +1 -1
- package/plugins/lisa/rules/reference/derived-branch-plan.md +1 -1
- package/plugins/lisa/rules/reference/design-source-of-truth.md +1 -1
- package/plugins/lisa/rules/reference/design-value-binding.md +1 -1
- package/plugins/lisa/rules/reference/documentation-source-paths.md +1 -1
- package/plugins/lisa/rules/reference/leaf-only-lifecycle.md +1 -1
- package/plugins/lisa/rules/reference/observability-audit.md +1 -1
- package/plugins/lisa/rules/reference/prd-definition-of-ready.md +1 -1
- package/plugins/lisa/rules/reference/prd-lifecycle-rollup.md +1 -1
- package/plugins/lisa/rules/reference/pre-flight-autofill.md +1 -1
- package/plugins/lisa/rules/reference/promotion-contract.md +1 -1
- package/plugins/lisa/rules/reference/readiness-rubric.md +1 -1
- package/plugins/lisa/rules/reference/ready-role-filing.md +37 -1
- package/plugins/lisa/rules/reference/rejection-detection.md +1 -1
- package/plugins/lisa/rules/reference/repo-scope-split.md +9 -10
- package/plugins/lisa/rules/reference/reset-seed-coverage.md +1 -1
- package/plugins/lisa/rules/reference/usage-accounting.md +1 -1
- package/plugins/lisa/rules/reference/work-item-definition-of-ready.md +1 -1
- package/plugins/lisa/scripts/intake-blocker-reprobe.mjs +85 -17
- package/plugins/lisa/scripts/qa-signal-lifecycle.mjs +5 -2
- package/plugins/lisa/skills/lisa-github-build-intake/SKILL.md +38 -15
- package/plugins/lisa/skills/lisa-github-write-issue/SKILL.md +15 -1
- package/plugins/lisa/skills/lisa-jira-build-intake/SKILL.md +37 -13
- package/plugins/lisa/skills/lisa-jira-write-ticket/SKILL.md +15 -1
- package/plugins/lisa/skills/lisa-linear-access/SKILL.md +56 -7
- package/plugins/lisa/skills/lisa-linear-build-intake/SKILL.md +36 -11
- package/plugins/lisa/skills/lisa-linear-prd-intake/SKILL.md +12 -2
- package/plugins/lisa/skills/lisa-linear-validate-issue/SKILL.md +1 -18
- package/plugins/lisa/skills/lisa-linear-verify/SKILL.md +0 -17
- package/plugins/lisa/skills/lisa-linear-write-issue/SKILL.md +18 -35
- package/plugins/lisa/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
- package/plugins/lisa/skills/lisa-repair-intake/SKILL.md +66 -12
- package/plugins/lisa/skills/lisa-rework-triage/SKILL.md +2 -1
- package/plugins/lisa/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
- package/plugins/lisa/skills/lisa-setup-linear/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
- package/plugins/lisa/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
- package/plugins/lisa/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
- package/plugins/lisa/skills/lisa-track/SKILL.md +32 -4
- package/plugins/lisa/skills/lisa-tracker-build-intake/SKILL.md +15 -1
- package/plugins/lisa-agy/hooks/block-blind-automerge.sh +13 -5
- package/plugins/lisa-agy/hooks/block-direct-issue-create.sh +7 -0
- package/plugins/lisa-agy/hooks/block-instruction-file-edits.sh +3 -0
- package/plugins/lisa-agy/hooks/block-managed-file-edits.sh +76 -2
- package/plugins/lisa-agy/hooks/block-shell-json-parsing.sh +3 -0
- package/plugins/lisa-agy/hooks/guard-dedupe.bash +66 -29
- package/plugins/lisa-agy/hooks/parity-safety-net-heredoc.py +3 -0
- package/plugins/lisa-agy/hooks/parity-safety-net.sh +5 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/scripts/intake-blocker-reprobe.mjs +85 -17
- package/plugins/lisa-agy/scripts/qa-signal-lifecycle.mjs +5 -2
- package/plugins/lisa-agy/skills/lisa-github-build-intake/SKILL.md +38 -15
- package/plugins/lisa-agy/skills/lisa-github-write-issue/SKILL.md +15 -1
- package/plugins/lisa-agy/skills/lisa-jira-build-intake/SKILL.md +37 -13
- package/plugins/lisa-agy/skills/lisa-jira-write-ticket/SKILL.md +15 -1
- package/plugins/lisa-agy/skills/lisa-linear-access/SKILL.md +56 -7
- package/plugins/lisa-agy/skills/lisa-linear-build-intake/SKILL.md +36 -11
- package/plugins/lisa-agy/skills/lisa-linear-prd-intake/SKILL.md +12 -2
- package/plugins/lisa-agy/skills/lisa-linear-validate-issue/SKILL.md +1 -18
- package/plugins/lisa-agy/skills/lisa-linear-verify/SKILL.md +0 -17
- package/plugins/lisa-agy/skills/lisa-linear-write-issue/SKILL.md +18 -35
- package/plugins/lisa-agy/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
- package/plugins/lisa-agy/skills/lisa-repair-intake/SKILL.md +66 -12
- package/plugins/lisa-agy/skills/lisa-rework-triage/SKILL.md +2 -1
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
- package/plugins/lisa-agy/skills/lisa-setup-linear/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
- package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
- package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
- package/plugins/lisa-agy/skills/lisa-track/SKILL.md +32 -4
- package/plugins/lisa-agy/skills/lisa-tracker-build-intake/SKILL.md +15 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/hooks/block-blind-automerge.sh +13 -5
- package/plugins/lisa-copilot/hooks/block-direct-issue-create.sh +7 -0
- package/plugins/lisa-copilot/hooks/block-instruction-file-edits.sh +3 -0
- package/plugins/lisa-copilot/hooks/block-managed-file-edits.sh +76 -2
- package/plugins/lisa-copilot/hooks/block-no-verify.sh +3 -0
- package/plugins/lisa-copilot/hooks/block-shell-json-parsing.sh +3 -0
- package/plugins/lisa-copilot/hooks/guard-dedupe.bash +66 -29
- package/plugins/lisa-copilot/hooks/parity-safety-net-heredoc.py +3 -0
- package/plugins/lisa-copilot/hooks/parity-safety-net.sh +5 -1
- package/plugins/lisa-copilot/hooks/worktree-binding-guard.mjs +3 -0
- package/plugins/lisa-copilot/hooks/worktree-binding-guard.sh +5 -1
- package/plugins/lisa-copilot/rules/eager/00-rule-index.md +1 -0
- package/plugins/lisa-copilot/rules/eager/base-rules.md +3 -1
- package/plugins/lisa-copilot/rules/eager/withdrawn-rulings.md +14 -3
- package/plugins/lisa-copilot/rules/reference/automation-runbook-contract.md +1 -1
- package/plugins/lisa-copilot/rules/reference/bdd-e2e-coverage.md +1 -1
- package/plugins/lisa-copilot/rules/reference/blocker-containment.md +3 -3
- package/plugins/lisa-copilot/rules/reference/claim-archaeology.md +1 -1
- package/plugins/lisa-copilot/rules/reference/claim-evidence-mapping.md +1 -1
- package/plugins/lisa-copilot/rules/reference/claim-time-guards.md +1 -1
- package/plugins/lisa-copilot/rules/reference/control-reachability.md +1 -1
- package/plugins/lisa-copilot/rules/reference/convergent-review.md +1 -1
- package/plugins/lisa-copilot/rules/reference/credential-substrate-precedence.md +1 -1
- package/plugins/lisa-copilot/rules/reference/dependency-decision-records.md +1 -1
- package/plugins/lisa-copilot/rules/reference/dependency-internalization-kit.md +1 -1
- package/plugins/lisa-copilot/rules/reference/dependency-trust-classes.md +1 -1
- package/plugins/lisa-copilot/rules/reference/deployed-state-readback.md +1 -1
- package/plugins/lisa-copilot/rules/reference/derived-branch-plan.md +1 -1
- package/plugins/lisa-copilot/rules/reference/design-source-of-truth.md +1 -1
- package/plugins/lisa-copilot/rules/reference/design-value-binding.md +1 -1
- package/plugins/lisa-copilot/rules/reference/documentation-source-paths.md +1 -1
- package/plugins/lisa-copilot/rules/reference/leaf-only-lifecycle.md +1 -1
- package/plugins/lisa-copilot/rules/reference/observability-audit.md +1 -1
- package/plugins/lisa-copilot/rules/reference/prd-definition-of-ready.md +1 -1
- package/plugins/lisa-copilot/rules/reference/prd-lifecycle-rollup.md +1 -1
- package/plugins/lisa-copilot/rules/reference/pre-flight-autofill.md +1 -1
- package/plugins/lisa-copilot/rules/reference/promotion-contract.md +1 -1
- package/plugins/lisa-copilot/rules/reference/readiness-rubric.md +1 -1
- package/plugins/lisa-copilot/rules/reference/ready-role-filing.md +37 -1
- package/plugins/lisa-copilot/rules/reference/rejection-detection.md +1 -1
- package/plugins/lisa-copilot/rules/reference/repo-scope-split.md +9 -10
- package/plugins/lisa-copilot/rules/reference/reset-seed-coverage.md +1 -1
- package/plugins/lisa-copilot/rules/reference/usage-accounting.md +1 -1
- package/plugins/lisa-copilot/rules/reference/work-item-definition-of-ready.md +1 -1
- package/plugins/lisa-copilot/scripts/intake-blocker-reprobe.mjs +85 -17
- package/plugins/lisa-copilot/scripts/qa-signal-lifecycle.mjs +5 -2
- package/plugins/lisa-copilot/skills/lisa-github-build-intake/SKILL.md +38 -15
- package/plugins/lisa-copilot/skills/lisa-github-write-issue/SKILL.md +15 -1
- package/plugins/lisa-copilot/skills/lisa-jira-build-intake/SKILL.md +37 -13
- package/plugins/lisa-copilot/skills/lisa-jira-write-ticket/SKILL.md +15 -1
- package/plugins/lisa-copilot/skills/lisa-linear-access/SKILL.md +56 -7
- package/plugins/lisa-copilot/skills/lisa-linear-build-intake/SKILL.md +36 -11
- package/plugins/lisa-copilot/skills/lisa-linear-prd-intake/SKILL.md +12 -2
- package/plugins/lisa-copilot/skills/lisa-linear-validate-issue/SKILL.md +1 -18
- package/plugins/lisa-copilot/skills/lisa-linear-verify/SKILL.md +0 -17
- package/plugins/lisa-copilot/skills/lisa-linear-write-issue/SKILL.md +18 -35
- package/plugins/lisa-copilot/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
- package/plugins/lisa-copilot/skills/lisa-repair-intake/SKILL.md +66 -12
- package/plugins/lisa-copilot/skills/lisa-rework-triage/SKILL.md +2 -1
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
- package/plugins/lisa-copilot/skills/lisa-setup-linear/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
- package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
- package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
- package/plugins/lisa-copilot/skills/lisa-track/SKILL.md +32 -4
- package/plugins/lisa-copilot/skills/lisa-tracker-build-intake/SKILL.md +15 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/block-blind-automerge.sh +13 -5
- package/plugins/lisa-cursor/hooks/block-direct-issue-create.sh +7 -0
- package/plugins/lisa-cursor/hooks/block-instruction-file-edits.sh +3 -0
- package/plugins/lisa-cursor/hooks/block-managed-file-edits.sh +76 -2
- package/plugins/lisa-cursor/hooks/block-no-verify.sh +3 -0
- package/plugins/lisa-cursor/hooks/block-shell-json-parsing.sh +3 -0
- package/plugins/lisa-cursor/hooks/guard-dedupe.bash +66 -29
- package/plugins/lisa-cursor/hooks/parity-safety-net-heredoc.py +3 -0
- package/plugins/lisa-cursor/hooks/parity-safety-net.sh +5 -1
- package/plugins/lisa-cursor/hooks/worktree-binding-guard.mjs +3 -0
- package/plugins/lisa-cursor/hooks/worktree-binding-guard.sh +5 -1
- package/plugins/lisa-cursor/rules/00-rule-index.mdc +1 -0
- package/plugins/lisa-cursor/rules/automation-runbook-contract-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/base-rules.mdc +3 -1
- package/plugins/lisa-cursor/rules/bdd-e2e-coverage-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/blocker-containment-reference.mdc +3 -3
- package/plugins/lisa-cursor/rules/claim-archaeology-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/claim-evidence-mapping-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/claim-time-guards-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/control-reachability-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/convergent-review-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/credential-substrate-precedence-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/dependency-decision-records-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/dependency-internalization-kit-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/dependency-trust-classes-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/deployed-state-readback-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/derived-branch-plan-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/design-source-of-truth-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/design-value-binding-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/documentation-source-paths-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/leaf-only-lifecycle-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/observability-audit-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/prd-definition-of-ready-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/prd-lifecycle-rollup-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/pre-flight-autofill-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/promotion-contract-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/readiness-rubric-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/ready-role-filing-reference.mdc +37 -1
- package/plugins/lisa-cursor/rules/rejection-detection-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/repo-scope-split-reference.mdc +9 -10
- package/plugins/lisa-cursor/rules/reset-seed-coverage-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/usage-accounting-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/withdrawn-rulings.mdc +14 -3
- package/plugins/lisa-cursor/rules/work-item-definition-of-ready-reference.mdc +1 -1
- package/plugins/lisa-cursor/scripts/intake-blocker-reprobe.mjs +85 -17
- package/plugins/lisa-cursor/scripts/qa-signal-lifecycle.mjs +5 -2
- package/plugins/lisa-cursor/skills/lisa-github-build-intake/SKILL.md +38 -15
- package/plugins/lisa-cursor/skills/lisa-github-write-issue/SKILL.md +15 -1
- package/plugins/lisa-cursor/skills/lisa-jira-build-intake/SKILL.md +37 -13
- package/plugins/lisa-cursor/skills/lisa-jira-write-ticket/SKILL.md +15 -1
- package/plugins/lisa-cursor/skills/lisa-linear-access/SKILL.md +56 -7
- package/plugins/lisa-cursor/skills/lisa-linear-build-intake/SKILL.md +36 -11
- package/plugins/lisa-cursor/skills/lisa-linear-prd-intake/SKILL.md +12 -2
- package/plugins/lisa-cursor/skills/lisa-linear-validate-issue/SKILL.md +1 -18
- package/plugins/lisa-cursor/skills/lisa-linear-verify/SKILL.md +0 -17
- package/plugins/lisa-cursor/skills/lisa-linear-write-issue/SKILL.md +18 -35
- package/plugins/lisa-cursor/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
- package/plugins/lisa-cursor/skills/lisa-repair-intake/SKILL.md +66 -12
- package/plugins/lisa-cursor/skills/lisa-rework-triage/SKILL.md +2 -1
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
- package/plugins/lisa-cursor/skills/lisa-setup-linear/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
- package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
- package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
- package/plugins/lisa-cursor/skills/lisa-track/SKILL.md +32 -4
- package/plugins/lisa-cursor/skills/lisa-tracker-build-intake/SKILL.md +15 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/materialized-artifacts.json +11 -0
- package/plugins/src/base/hooks/block-blind-automerge.sh +10 -5
- package/plugins/src/base/hooks/block-direct-issue-create.sh +4 -0
- package/plugins/src/base/hooks/block-managed-file-edits.sh +73 -2
- package/plugins/src/base/hooks/guard-dedupe.bash +63 -29
- package/plugins/src/base/hooks/parity-safety-net.sh +2 -1
- package/plugins/src/base/hooks/worktree-binding-guard.sh +2 -1
- package/plugins/src/base/rules/eager/00-rule-index.md +1 -0
- package/plugins/src/base/rules/eager/base-rules.md +3 -1
- package/plugins/src/base/rules/eager/withdrawn-rulings.md +14 -3
- package/plugins/src/base/rules/reference/automation-runbook-contract.md +1 -1
- package/plugins/src/base/rules/reference/bdd-e2e-coverage.md +1 -1
- package/plugins/src/base/rules/reference/blocker-containment.md +3 -3
- package/plugins/src/base/rules/reference/claim-archaeology.md +1 -1
- package/plugins/src/base/rules/reference/claim-evidence-mapping.md +1 -1
- package/plugins/src/base/rules/reference/claim-time-guards.md +1 -1
- package/plugins/src/base/rules/reference/control-reachability.md +1 -1
- package/plugins/src/base/rules/reference/convergent-review.md +1 -1
- package/plugins/src/base/rules/reference/credential-substrate-precedence.md +1 -1
- package/plugins/src/base/rules/reference/dependency-decision-records.md +1 -1
- package/plugins/src/base/rules/reference/dependency-internalization-kit.md +1 -1
- package/plugins/src/base/rules/reference/dependency-trust-classes.md +1 -1
- package/plugins/src/base/rules/reference/deployed-state-readback.md +1 -1
- package/plugins/src/base/rules/reference/derived-branch-plan.md +1 -1
- package/plugins/src/base/rules/reference/design-source-of-truth.md +1 -1
- package/plugins/src/base/rules/reference/design-value-binding.md +1 -1
- package/plugins/src/base/rules/reference/documentation-source-paths.md +1 -1
- package/plugins/src/base/rules/reference/leaf-only-lifecycle.md +1 -1
- package/plugins/src/base/rules/reference/observability-audit.md +1 -1
- package/plugins/src/base/rules/reference/prd-definition-of-ready.md +1 -1
- package/plugins/src/base/rules/reference/prd-lifecycle-rollup.md +1 -1
- package/plugins/src/base/rules/reference/pre-flight-autofill.md +1 -1
- package/plugins/src/base/rules/reference/promotion-contract.md +1 -1
- package/plugins/src/base/rules/reference/readiness-rubric.md +1 -1
- package/plugins/src/base/rules/reference/ready-role-filing.md +37 -1
- package/plugins/src/base/rules/reference/rejection-detection.md +1 -1
- package/plugins/src/base/rules/reference/repo-scope-split.md +9 -10
- package/plugins/src/base/rules/reference/reset-seed-coverage.md +1 -1
- package/plugins/src/base/rules/reference/usage-accounting.md +1 -1
- package/plugins/src/base/rules/reference/work-item-definition-of-ready.md +1 -1
- package/plugins/src/base/scripts/intake-blocker-reprobe.mjs +85 -17
- package/plugins/src/base/scripts/qa-signal-lifecycle.mjs +5 -2
- package/plugins/src/base/skills/lisa-github-build-intake/SKILL.md +38 -15
- package/plugins/src/base/skills/lisa-github-write-issue/SKILL.md +15 -1
- package/plugins/src/base/skills/lisa-jira-build-intake/SKILL.md +37 -13
- package/plugins/src/base/skills/lisa-jira-write-ticket/SKILL.md +15 -1
- package/plugins/src/base/skills/lisa-linear-access/SKILL.md +56 -7
- package/plugins/src/base/skills/lisa-linear-build-intake/SKILL.md +36 -11
- package/plugins/src/base/skills/lisa-linear-prd-intake/SKILL.md +12 -2
- package/plugins/src/base/skills/lisa-linear-validate-issue/SKILL.md +1 -18
- package/plugins/src/base/skills/lisa-linear-verify/SKILL.md +0 -17
- package/plugins/src/base/skills/lisa-linear-write-issue/SKILL.md +18 -35
- package/plugins/src/base/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
- package/plugins/src/base/skills/lisa-repair-intake/SKILL.md +66 -12
- package/plugins/src/base/skills/lisa-rework-triage/SKILL.md +2 -1
- package/plugins/src/base/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
- package/plugins/src/base/skills/lisa-setup-linear/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
- package/plugins/src/base/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
- package/plugins/src/base/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
- package/plugins/src/base/skills/lisa-track/SKILL.md +32 -4
- package/plugins/src/base/skills/lisa-tracker-build-intake/SKILL.md +15 -1
- package/scripts/build-plugins.sh +4 -0
- package/scripts/copy-opencode-plugin-templates.mjs +5 -1
- package/scripts/lisa-enforcement-fallback.sh +9 -1
- package/scripts/two-channel-couplings.json +5 -33
- package/typescript/copy-overwrite/scripts/check-skipped-required-checks.mjs +551 -13
- package/typescript/create-only/.github/required-checks.json +1 -1
|
@@ -90,7 +90,7 @@ import { invokedAsScript } from "./lib/invoked-as-script.mjs";
|
|
|
90
90
|
// resolves there exactly as it does here. Imported rather than reimplemented:
|
|
91
91
|
// a second lane parser is a second answer to `whose is this?`, and two
|
|
92
92
|
// answers is the state this report exists to end.
|
|
93
|
-
import { parseLaneId, workItemLines } from "./lisa-work-item.mjs";
|
|
93
|
+
import { parseLaneId, soleWorkItem, workItemLines } from "./lisa-work-item.mjs";
|
|
94
94
|
|
|
95
95
|
/** Branch name prefixes that are never work-in-flight. */
|
|
96
96
|
const IGNORED_PREFIXES = Object.freeze(["backup/", "gh-readonly-queue/"]);
|
|
@@ -138,8 +138,8 @@ const REF_SOURCE = Object.freeze({ NAME: "branch name", TRAILER: "trailer" });
|
|
|
138
138
|
*/
|
|
139
139
|
const REF_IN_SEGMENT = /^(?:[a-z]+-)*[a-z]{0,4}(\d{2,})(?![0-9a-z])/u;
|
|
140
140
|
|
|
141
|
-
/** A
|
|
142
|
-
const
|
|
141
|
+
/** A failed tip read must remain distinguishable from a missing lane stamp. */
|
|
142
|
+
export const UNREADABLE_LANE = "unreadable";
|
|
143
143
|
|
|
144
144
|
/** `20260904`: a compact `yyyymmdd` date, never a work item. */
|
|
145
145
|
const COMPACT_DATE = /^20\d{6}$/u;
|
|
@@ -275,23 +275,50 @@ export function branchesAhead(remote = "origin", base = "main") {
|
|
|
275
275
|
* unread. See the `work-item-trailer-definition` rule (#3747).
|
|
276
276
|
*
|
|
277
277
|
* The branch name remains the fallback for a branch that genuinely carries no
|
|
278
|
-
* trailer, reported as an inference rather than as a fact.
|
|
278
|
+
* trailer, reported as an inference rather than as a fact. A branch naming
|
|
279
|
+
* several items cannot be classified from one item's state: leave it unresolved
|
|
280
|
+
* rather than choosing whichever commit git happened to print first.
|
|
279
281
|
* @param {string} branch Short branch name.
|
|
280
282
|
* @param {string} base Default branch name.
|
|
281
283
|
* @param {string} remote Remote name.
|
|
282
284
|
* @param {typeof run} exec Command runner.
|
|
285
|
+
* @param {object} [contract] Tracker contract; the canonical reader resolves it when omitted.
|
|
283
286
|
* @returns {{ref: string, refSource: string} | undefined} The reference.
|
|
284
287
|
*/
|
|
285
|
-
export function workItemRef(
|
|
288
|
+
export function workItemRef(
|
|
289
|
+
branch,
|
|
290
|
+
base,
|
|
291
|
+
remote = "origin",
|
|
292
|
+
exec = run,
|
|
293
|
+
contract
|
|
294
|
+
) {
|
|
286
295
|
const messages = exec("git", [
|
|
287
296
|
"log",
|
|
288
297
|
"--format=%B",
|
|
289
298
|
`${remote}/${base}..${remote}/${branch}`,
|
|
290
299
|
]);
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
300
|
+
if (messages === undefined) return undefined;
|
|
301
|
+
const values = workItemLines(messages);
|
|
302
|
+
// The canonical reader omits empty values. Count raw column-zero prefixes
|
|
303
|
+
// too, so an empty declaration stays invalid even beside a valid trailer.
|
|
304
|
+
const declarations = messages
|
|
305
|
+
.split(/\r?\n/)
|
|
306
|
+
.filter(line => /^work-item:/i.test(line));
|
|
307
|
+
if (declarations.length !== values.length) return undefined;
|
|
308
|
+
if (values.length > 0) {
|
|
309
|
+
try {
|
|
310
|
+
const canonical = soleWorkItem(messages, contract, "branch commits");
|
|
311
|
+
// This collector reads GitHub issues. A valid reference for a different
|
|
312
|
+
// tracker is not a GitHub issue number and cannot be looked up with gh.
|
|
313
|
+
const ref = canonical.split("#")[1];
|
|
314
|
+
return ref ? { ref, refSource: REF_SOURCE.TRAILER } : undefined;
|
|
315
|
+
} catch {
|
|
316
|
+
// probe-direction: fail-closed — an invalid binding leaves the branch
|
|
317
|
+
// unresolved, so a guessed issue cannot authorize its classification.
|
|
318
|
+
// Malformed and conflicting bindings are unknown, never permission to
|
|
319
|
+
// replace the commit evidence with a guess from the branch name.
|
|
320
|
+
return undefined;
|
|
321
|
+
}
|
|
295
322
|
}
|
|
296
323
|
const fromName = refInBranchName(branch);
|
|
297
324
|
return fromName ? { ref: fromName, refSource: REF_SOURCE.NAME } : undefined;
|
|
@@ -406,12 +433,19 @@ function parseJson(text) {
|
|
|
406
433
|
* @param {string} base Default branch name.
|
|
407
434
|
* @param {string} remote Remote name.
|
|
408
435
|
* @param {typeof run} exec Command runner.
|
|
436
|
+
* @param {object} [contract] Tracker contract, forwarded to the canonical trailer reader.
|
|
409
437
|
* @returns {Map<string, WorkItem>} States, keyed by branch.
|
|
410
438
|
*/
|
|
411
|
-
export function workItemStates(
|
|
439
|
+
export function workItemStates(
|
|
440
|
+
branches,
|
|
441
|
+
base,
|
|
442
|
+
remote = "origin",
|
|
443
|
+
exec = run,
|
|
444
|
+
contract
|
|
445
|
+
) {
|
|
412
446
|
const states = new Map();
|
|
413
447
|
for (const branch of branches) {
|
|
414
|
-
const found = workItemRef(branch, base, remote, exec);
|
|
448
|
+
const found = workItemRef(branch, base, remote, exec, contract);
|
|
415
449
|
if (!found) continue;
|
|
416
450
|
const view = parseJson(
|
|
417
451
|
exec("gh", ["issue", "view", found.ref, "--json", "number,state,url"])
|
|
@@ -486,7 +520,7 @@ export function divergences(branches, base, remote = "origin", exec = run) {
|
|
|
486
520
|
* @param {ReadonlyArray<string>} branches Branch names, without the remote.
|
|
487
521
|
* @param {string} remote Remote the branches live on.
|
|
488
522
|
* @param {(command: string, args: string[]) => (string|undefined)} [exec] Runner.
|
|
489
|
-
* @returns {Map<string, string>} Lane id by branch, absent
|
|
523
|
+
* @returns {Map<string, string>} Lane id or UNREADABLE_LANE by branch, absent only when no stamp was found.
|
|
490
524
|
*/
|
|
491
525
|
export function branchLanes(branches, remote = "origin", exec = run) {
|
|
492
526
|
const lanes = new Map();
|
|
@@ -497,7 +531,7 @@ export function branchLanes(branches, remote = "origin", exec = run) {
|
|
|
497
531
|
"--format=%B",
|
|
498
532
|
`${remote}/${branch}`,
|
|
499
533
|
]);
|
|
500
|
-
const lane = message === undefined ?
|
|
534
|
+
const lane = message === undefined ? UNREADABLE_LANE : parseLaneId(message);
|
|
501
535
|
if (lane !== undefined) lanes.set(branch, lane);
|
|
502
536
|
}
|
|
503
537
|
return lanes;
|
|
@@ -691,6 +725,8 @@ function renderRow(row, base) {
|
|
|
691
725
|
* @returns {string} The routing clause.
|
|
692
726
|
*/
|
|
693
727
|
function routing(row) {
|
|
728
|
+
if (row.lane === UNREADABLE_LANE)
|
|
729
|
+
return "tip commit could not be read, so its lane is unknown — retry the read before routing this branch";
|
|
694
730
|
if (row.lane === undefined)
|
|
695
731
|
return "no lane on the tip commit, so this cannot be routed to a session — it predates the stamp, or was made outside a Lisa hook";
|
|
696
732
|
return `produced by ${row.lane} — a session confirms this is its own with \`node scripts/lisa-work-item.mjs lane\``;
|
|
@@ -856,8 +856,16 @@ if [ -n "$notice_temp_base" ] && [ "$notice_state_trusted" -eq 1 ]; then
|
|
|
856
856
|
if [ ! -e "$guard_memo_dir" ] && [ ! -L "$guard_memo_dir" ]; then
|
|
857
857
|
(umask 077 && mkdir "$guard_memo_dir") 2>/dev/null || true
|
|
858
858
|
fi
|
|
859
|
+
# Unlike shared temp ancestors, memo leaves may not be writable by another
|
|
860
|
+
# account even with the sticky bit: it could pre-create the session child.
|
|
861
|
+
guard_memo_mode=""
|
|
862
|
+
guard_memo_mode="$(stat -c '%a' "$guard_memo_dir" 2>/dev/null)" ||
|
|
863
|
+
guard_memo_mode="$(stat -f '%p' "$guard_memo_dir" 2>/dev/null)" ||
|
|
864
|
+
guard_memo_mode=""
|
|
865
|
+
case "$guard_memo_mode" in "" | *[!0-7]*) guard_memo_mode=777 ;; esac
|
|
859
866
|
if [ -d "$guard_memo_dir" ] && [ ! -L "$guard_memo_dir" ] && \
|
|
860
|
-
[ -O "$guard_memo_dir" ]
|
|
867
|
+
[ -O "$guard_memo_dir" ] &&
|
|
868
|
+
[ $((8#$guard_memo_mode & 8#0022)) -eq 0 ]; then
|
|
861
869
|
export LISA_GUARD_MEMO_DIR="$guard_memo_dir"
|
|
862
870
|
export LISA_GUARD_MEMO_UID="$notice_uid"
|
|
863
871
|
fi
|
|
@@ -283,7 +283,8 @@ NODE_ID_PATTERN = re.compile(r"^[A-Za-z0-9_=-]+$")
|
|
|
283
283
|
NODE_QUERY = (
|
|
284
284
|
"query($id:ID!){node(id:$id){... on PullRequest"
|
|
285
285
|
"{number url reviewDecision state baseRefName "
|
|
286
|
-
"commits(last:1){nodes{commit{statusCheckRollup{contexts(first:100){
|
|
286
|
+
"commits(last:1){nodes{commit{statusCheckRollup{contexts(first:100){"
|
|
287
|
+
"pageInfo{hasNextPage} nodes{"
|
|
287
288
|
"__typename ... on CheckRun{name conclusion} "
|
|
288
289
|
"... on StatusContext{context state}"
|
|
289
290
|
"}}}}}}}}}"
|
|
@@ -297,9 +298,13 @@ NODE_QUERY = (
|
|
|
297
298
|
# checks, which is genuinely not failing and gets an empty list. A node without
|
|
298
299
|
# the scaffolding is a probe that never asked, and it must leave the key absent
|
|
299
300
|
# so the re-target path can tell the two apart instead of reading both as green.
|
|
301
|
+
# A truncated page is equally unreadable: a failing check may be on the next
|
|
302
|
+
# page, so a green first page cannot authorize an uncovered-base re-target.
|
|
300
303
|
NODE_JQ = (
|
|
301
304
|
"if .data.node == null then null else .data.node as $n "
|
|
302
|
-
"| if ($n
|
|
305
|
+
"| if ($n.commits.nodes[0].commit.statusCheckRollup.contexts.pageInfo.hasNextPage == true) "
|
|
306
|
+
"then ($n | del(.commits)) "
|
|
307
|
+
"elif ($n | has(\"commits\")) then ($n | del(.commits)) "
|
|
303
308
|
"+ {statusCheckRollup: "
|
|
304
309
|
"((($n.commits.nodes[0].commit.statusCheckRollup.contexts.nodes)) // [])} "
|
|
305
310
|
"else $n end end"
|
|
@@ -1027,9 +1032,9 @@ not read the pull request's checks.
|
|
|
1027
1032
|
|
|
1028
1033
|
That ref has ZERO required status checks, so the one thing standing between the
|
|
1029
1034
|
move and a permission-free gate bypass is whether the PR is currently failing —
|
|
1030
|
-
and the probe returned no `statusCheckRollup
|
|
1031
|
-
empty one. Reporting the move as the sanctioned green-PR
|
|
1032
|
-
|
|
1035
|
+
and the probe returned no complete `statusCheckRollup`. An absent or truncated
|
|
1036
|
+
rollup is not an empty one. Reporting the move as the sanctioned green-PR
|
|
1037
|
+
batching case here would claim a result the probe did not establish.
|
|
1033
1038
|
|
|
1034
1039
|
Re-target through `gh pr edit --base` instead, which reads the checks, or fix
|
|
1035
1040
|
the probe. Do not report the PR as batched until something has actually looked.
|
|
@@ -2109,6 +2109,10 @@ def unparseable_reads_only(text):
|
|
|
2109
2109
|
Returns:
|
|
2110
2110
|
True when every segment's command word is a known reader.
|
|
2111
2111
|
"""
|
|
2112
|
+
# Unlexable operands cannot be certified as data when shell substitutions
|
|
2113
|
+
# may execute inside them. Preserve the plain apostrophe reader exemption.
|
|
2114
|
+
if any(operator in text for operator in ("$(", "`", "<(", ">(")):
|
|
2115
|
+
return False
|
|
2112
2116
|
words = raw_command_words(text)
|
|
2113
2117
|
return bool(words) and all(word in READ_ONLY_PROGRAMS for word in words)
|
|
2114
2118
|
|
|
@@ -569,6 +569,7 @@ EOF
|
|
|
569
569
|
import os
|
|
570
570
|
import re
|
|
571
571
|
import shlex
|
|
572
|
+
import sys
|
|
572
573
|
|
|
573
574
|
command = os.environ.get("MANAGED_EDIT_COMMAND", "")
|
|
574
575
|
project = os.environ.get("MANAGED_EDIT_PROJECT", "") or os.getcwd()
|
|
@@ -579,6 +580,7 @@ project = os.environ.get("MANAGED_EDIT_PROJECT", "") or os.getcwd()
|
|
|
579
580
|
FOLLOW_MAX_BYTES = 262144
|
|
580
581
|
FOLLOW_MAX_FILES = 8
|
|
581
582
|
FOLLOW_MAX_DEPTH = 3
|
|
583
|
+
ENV_SPLIT_UNRESOLVED = 42
|
|
582
584
|
|
|
583
585
|
# Spelled with chr() rather than backslash escapes on purpose. This source is
|
|
584
586
|
# embedded in a shell heredoc and copied verbatim through several generators
|
|
@@ -610,7 +612,7 @@ FOLLOW_WRAPPERS = {
|
|
|
610
612
|
# positional count: the walk below re-enters its assignment skip on the
|
|
611
613
|
# next iteration and steps over as many as are present.
|
|
612
614
|
"env": (
|
|
613
|
-
frozenset({"-C", "--chdir", "-P", "-
|
|
615
|
+
frozenset({"-C", "--chdir", "-P", "-u", "--unset"}),
|
|
614
616
|
0,
|
|
615
617
|
),
|
|
616
618
|
"exec": (frozenset({"-a"}), 0),
|
|
@@ -744,6 +746,31 @@ def statements(tokens):
|
|
|
744
746
|
return grouped
|
|
745
747
|
|
|
746
748
|
|
|
749
|
+
class UnresolvedEnvSplit(ValueError):
|
|
750
|
+
"""Split-string syntax whose executed arguments cannot be proven here."""
|
|
751
|
+
|
|
752
|
+
|
|
753
|
+
def parse_env_split(split_string):
|
|
754
|
+
"""Parse only the env -S subset shared with ordinary quoted arguments.
|
|
755
|
+
|
|
756
|
+
GNU env has its own escapes, comments and environment interpolation. Do
|
|
757
|
+
not pretend shlex implements those, or invent environment values in a
|
|
758
|
+
pre-execution guard. The operator can spell the resulting command directly.
|
|
759
|
+
|
|
760
|
+
Args:
|
|
761
|
+
split_string: The literal argument supplied to env's split option.
|
|
762
|
+
|
|
763
|
+
Returns:
|
|
764
|
+
Arguments for the supported subset; otherwise raises UnresolvedEnvSplit.
|
|
765
|
+
"""
|
|
766
|
+
if any(marker in split_string for marker in (BACKSLASH, "#", "$", chr(96), chr(11), chr(12))):
|
|
767
|
+
raise UnresolvedEnvSplit("escapes, comments, shell substitution, variable expansion or control whitespace")
|
|
768
|
+
try:
|
|
769
|
+
return shlex.split(split_string, comments=False, posix=True)
|
|
770
|
+
except ValueError as error:
|
|
771
|
+
raise UnresolvedEnvSplit("unbalanced quoting") from error
|
|
772
|
+
|
|
773
|
+
|
|
747
774
|
def command_word(statement):
|
|
748
775
|
"""The program a statement runs, and the tokens after it.
|
|
749
776
|
|
|
@@ -760,6 +787,7 @@ def command_word(statement):
|
|
|
760
787
|
its command word is itself the executed script.
|
|
761
788
|
"""
|
|
762
789
|
index = 0
|
|
790
|
+
split_expansions = 0
|
|
763
791
|
while index < len(statement):
|
|
764
792
|
token = statement[index]
|
|
765
793
|
if "=" in token and not token.startswith(("=", "-")):
|
|
@@ -775,6 +803,36 @@ def command_word(statement):
|
|
|
775
803
|
if option == "--":
|
|
776
804
|
index += 1
|
|
777
805
|
break
|
|
806
|
+
# env -S inserts the split string back into env's argument list;
|
|
807
|
+
# it is executable input, not an option value to discard. Keep
|
|
808
|
+
# shell -c's quoted operand intact while expanding the env layer.
|
|
809
|
+
# The known no-operand flags can be combined before S (-vS/-ivS).
|
|
810
|
+
# Stop at S: the rest belongs to its argument, not more flags.
|
|
811
|
+
short_split = re.match(r"^-[iv0]*S(.*)$", option)
|
|
812
|
+
if program == "env" and (
|
|
813
|
+
option == "--split-string"
|
|
814
|
+
or option.startswith("--split-string=")
|
|
815
|
+
or short_split is not None
|
|
816
|
+
):
|
|
817
|
+
consumed = 1
|
|
818
|
+
if option == "--split-string" or (
|
|
819
|
+
short_split is not None and not short_split.group(1)
|
|
820
|
+
):
|
|
821
|
+
if index + 1 >= len(statement):
|
|
822
|
+
raise UnresolvedEnvSplit("missing split-string operand")
|
|
823
|
+
split_string = statement[index + 1]
|
|
824
|
+
consumed = 2
|
|
825
|
+
elif option.startswith("--split-string="):
|
|
826
|
+
split_string = option.split("=", 1)[1]
|
|
827
|
+
else:
|
|
828
|
+
split_string = short_split.group(1)
|
|
829
|
+
if split_expansions >= 64:
|
|
830
|
+
raise UnresolvedEnvSplit("nested split-string limit")
|
|
831
|
+
expanded = parse_env_split(split_string)
|
|
832
|
+
statement = [token] + expanded + statement[index + consumed :]
|
|
833
|
+
split_expansions += 1
|
|
834
|
+
index = 0
|
|
835
|
+
break
|
|
778
836
|
index += 2 if option in separate else 1
|
|
779
837
|
index += positional
|
|
780
838
|
return (None, None, [])
|
|
@@ -988,7 +1046,11 @@ def collect(text, depth, seen, out):
|
|
|
988
1046
|
|
|
989
1047
|
|
|
990
1048
|
targets = []
|
|
991
|
-
|
|
1049
|
+
try:
|
|
1050
|
+
collect(command, 0, set(), targets)
|
|
1051
|
+
except UnresolvedEnvSplit as error:
|
|
1052
|
+
sys.stderr.write(str(error) + NEWLINE)
|
|
1053
|
+
sys.exit(ENV_SPLIT_UNRESOLVED)
|
|
992
1054
|
for target in targets:
|
|
993
1055
|
cleaned = target.strip().strip("'\"")
|
|
994
1056
|
if cleaned:
|
|
@@ -1006,6 +1068,15 @@ PY
|
|
|
1006
1068
|
else
|
|
1007
1069
|
analyzer_status=$?
|
|
1008
1070
|
fi
|
|
1071
|
+
if [ "$analyzer_status" -eq 42 ]; then
|
|
1072
|
+
printf 'Blocked: cannot resolve env --split-string for managed-file protection.\n' >&2
|
|
1073
|
+
printf 'Use an explicit command without split-string escapes, comments or variable expansion.\n' >&2
|
|
1074
|
+
while IFS= read -r analyzer_line; do
|
|
1075
|
+
[ -n "$analyzer_line" ] && printf ' %s\n' "$analyzer_line" >&2
|
|
1076
|
+
done <"$analyzer_stderr"
|
|
1077
|
+
rm -f "$analyzer_stderr"
|
|
1078
|
+
exit 2
|
|
1079
|
+
fi
|
|
1009
1080
|
if [ "$analyzer_status" -ne 0 ]; then
|
|
1010
1081
|
printf 'block-managed-file-edits: Bash analyzer failed (exit %s); Bash write protection is NOT active\n' "$analyzer_status" >&2
|
|
1011
1082
|
while IFS= read -r analyzer_line; do
|
|
@@ -54,12 +54,9 @@
|
|
|
54
54
|
# refusal reaching the agent without the message that explains it. On a
|
|
55
55
|
# refusal every channel evaluates and speaks exactly as it does today.
|
|
56
56
|
#
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
# unless a second channel has actually been seen. The digest, and the one `stat`
|
|
61
|
-
# that feeds it the per-call discriminator, are reached only where a second
|
|
62
|
-
# channel is live, which is exactly where they are repaid.
|
|
57
|
+
# State ownership, modes and ancestry are verified before channel discovery.
|
|
58
|
+
# After that validation the channel check uses only builtins, and returns
|
|
59
|
+
# before digesting code or sizing the transcript unless another channel is live.
|
|
63
60
|
|
|
64
61
|
# Key this guard would memoise under, once computed. Empty means "record
|
|
65
62
|
# nothing on exit".
|
|
@@ -77,14 +74,25 @@ lisa_guard_memo_ready=0
|
|
|
77
74
|
# Digest tool for the memo key, resolved once. Empty disables the memo.
|
|
78
75
|
lisa_guard_memo_digest_tool=""
|
|
79
76
|
|
|
80
|
-
#
|
|
81
|
-
#
|
|
82
|
-
#
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
77
|
+
# Read ownership and all permission bits without accepting output from a
|
|
78
|
+
# failed platform-specific stat invocation.
|
|
79
|
+
# $1 - directory; sets lisa_guard_memo_owner and lisa_guard_memo_mode
|
|
80
|
+
lisa_guard_memo_stat() {
|
|
81
|
+
local facts=""
|
|
82
|
+
facts="$(stat -c '%u %a' "$1" 2>/dev/null)" ||
|
|
83
|
+
facts="$(stat -f '%u %p' "$1" 2>/dev/null)" || return 1
|
|
84
|
+
lisa_guard_memo_owner="${facts%% *}"
|
|
85
|
+
lisa_guard_memo_mode="${facts#* }"
|
|
86
|
+
case "$lisa_guard_memo_owner:$lisa_guard_memo_mode" in
|
|
87
|
+
*[!0-9:]* | :* | *:) return 1 ;;
|
|
88
|
+
esac
|
|
89
|
+
case "$lisa_guard_memo_mode" in *[!0-7]*) return 1 ;; esac
|
|
90
|
+
lisa_guard_memo_mode=$((8#$lisa_guard_memo_mode & 8#7777))
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
# Memo leaves must be private even when a sticky shared temp ancestor is safe.
|
|
94
|
+
# Ownership alone allows another local account to plant a session/allow file
|
|
95
|
+
# in an owned but group- or world-writable directory.
|
|
88
96
|
#
|
|
89
97
|
# $1 - candidate directory
|
|
90
98
|
lisa_guard_memo_dir_usable() {
|
|
@@ -92,23 +100,42 @@ lisa_guard_memo_dir_usable() {
|
|
|
92
100
|
[ -d "$1" ] || return 1
|
|
93
101
|
[ ! -L "$1" ] || return 1
|
|
94
102
|
[ -O "$1" ] || return 1
|
|
103
|
+
lisa_guard_memo_stat "$1" || return 1
|
|
104
|
+
[ $((lisa_guard_memo_mode & 8#0022)) -eq 0 ]
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
# Resolve parents physically and verify every ancestor. Plugin hooks do not
|
|
108
|
+
# inherit the dispatcher's trust walk, and exported paths are still inputs.
|
|
109
|
+
# $1 - candidate memo base; sets lisa_guard_memo_base only on success
|
|
110
|
+
lisa_guard_memo_trust_base() {
|
|
111
|
+
lisa_guard_memo_dir_usable "$1" || return 1
|
|
112
|
+
local physical=""
|
|
113
|
+
physical="$(cd "$1" 2>/dev/null && pwd -P)" || return 1
|
|
114
|
+
local current="$physical"
|
|
115
|
+
while :; do
|
|
116
|
+
lisa_guard_memo_stat "$current" || return 1
|
|
117
|
+
[ "$lisa_guard_memo_owner" = 0 ] || [ -O "$current" ] || return 1
|
|
118
|
+
if [ $((lisa_guard_memo_mode & 8#0022)) -ne 0 ] &&
|
|
119
|
+
[ $((lisa_guard_memo_mode & 8#1000)) -eq 0 ]; then
|
|
120
|
+
return 1
|
|
121
|
+
fi
|
|
122
|
+
[ "$current" != / ] || break
|
|
123
|
+
current="${current%/*}"
|
|
124
|
+
[ -n "$current" ] || current=/
|
|
125
|
+
done
|
|
126
|
+
lisa_guard_memo_base="$physical"
|
|
95
127
|
}
|
|
96
128
|
|
|
97
129
|
# Establish the memo base directory.
|
|
98
130
|
#
|
|
99
|
-
# Preferred source is `LISA_GUARD_MEMO_DIR`, exported by the dispatcher
|
|
100
|
-
#
|
|
101
|
-
#
|
|
102
|
-
# reached by the plugin channel has no such parent, so it creates the directory
|
|
103
|
-
# itself under `TMPDIR` — private by umask, and used only when this user owns
|
|
104
|
-
# it. If neither works the memo is unavailable and every guard evaluates, which
|
|
105
|
-
# is today's behaviour.
|
|
131
|
+
# Preferred source is `LISA_GUARD_MEMO_DIR`, exported by the dispatcher. Both
|
|
132
|
+
# this input and the plugin channel's TMPDIR fallback receive the same trust
|
|
133
|
+
# checks. Missing state is created privately; untrusted state disables memo use.
|
|
106
134
|
lisa_guard_memo_resolve_base() {
|
|
107
135
|
[ -z "$lisa_guard_memo_base" ] || return 0
|
|
108
136
|
|
|
109
137
|
local candidate="${LISA_GUARD_MEMO_DIR:-}"
|
|
110
|
-
if
|
|
111
|
-
lisa_guard_memo_base="$candidate"
|
|
138
|
+
if lisa_guard_memo_trust_base "$candidate"; then
|
|
112
139
|
return 0
|
|
113
140
|
fi
|
|
114
141
|
|
|
@@ -121,8 +148,7 @@ lisa_guard_memo_resolve_base() {
|
|
|
121
148
|
if [ ! -e "$candidate" ] && [ ! -L "$candidate" ]; then
|
|
122
149
|
(umask 077 && mkdir -p "$candidate") 2>/dev/null || true
|
|
123
150
|
fi
|
|
124
|
-
if
|
|
125
|
-
lisa_guard_memo_base="$candidate"
|
|
151
|
+
if lisa_guard_memo_trust_base "$candidate"; then
|
|
126
152
|
return 0
|
|
127
153
|
fi
|
|
128
154
|
return 1
|
|
@@ -168,6 +194,7 @@ lisa_guard_memo_sweep() {
|
|
|
168
194
|
#
|
|
169
195
|
# $1 - guard name, used only to name the memo file
|
|
170
196
|
# $2 - the raw tool payload this guard was handed on stdin
|
|
197
|
+
# Remaining arguments - files containing delegated verdict code
|
|
171
198
|
lisa_guard_dedupe() {
|
|
172
199
|
local guard_name="${1:-}"
|
|
173
200
|
local payload="${2:-}"
|
|
@@ -197,8 +224,7 @@ lisa_guard_dedupe() {
|
|
|
197
224
|
fresh_session=1
|
|
198
225
|
fi
|
|
199
226
|
fi
|
|
200
|
-
|
|
201
|
-
[ ! -L "$session_dir" ] || return 0
|
|
227
|
+
lisa_guard_memo_dir_usable "$session_dir" || return 0
|
|
202
228
|
|
|
203
229
|
# Swept once, by the process that created the session directory, so a
|
|
204
230
|
# long-lived machine does not accumulate memo state. Off the hot path by
|
|
@@ -281,11 +307,18 @@ lisa_guard_dedupe() {
|
|
|
281
307
|
case "$transcript_size" in "" | *[!0-9]*) return 0 ;; esac
|
|
282
308
|
|
|
283
309
|
# One command substitution, covering everything the verdict depends on: the
|
|
284
|
-
# guard's own bytes, this library
|
|
310
|
+
# guard's own bytes, this library, delegated code, payload, and call identity.
|
|
311
|
+
# Keep literal bytes (including trailing newlines), and propagate every read
|
|
312
|
+
# failure. A successful digest of a partial stream is not proof of identity.
|
|
285
313
|
local key=""
|
|
286
314
|
key="$(
|
|
315
|
+
set -o pipefail
|
|
287
316
|
{
|
|
288
|
-
|
|
317
|
+
local source_file=""
|
|
318
|
+
for source_file in "$self_script" "${BASH_SOURCE[0]}" "${@:3}"; do
|
|
319
|
+
cat "$source_file" 2>/dev/null || exit 1
|
|
320
|
+
printf '\0'
|
|
321
|
+
done
|
|
289
322
|
printf '\n%s\n' "$payload"
|
|
290
323
|
printf '%s\n' "$transcript_size"
|
|
291
324
|
} | if [ "$lisa_guard_memo_digest_tool" = "shasum" ]; then
|
|
@@ -296,6 +329,7 @@ lisa_guard_dedupe() {
|
|
|
296
329
|
)" || key=""
|
|
297
330
|
key="${key%% *}"
|
|
298
331
|
case "$key" in "" | *[!0-9a-f]*) return 0 ;; esac
|
|
332
|
+
[ "${#key}" -eq 64 ] || return 0
|
|
299
333
|
|
|
300
334
|
# One memo file per guard per session, rewritten as the call moves on, so the
|
|
301
335
|
# state directory stays a fixed size however long a session runs. A saturated
|
|
@@ -139,7 +139,8 @@ if [ -r "$lisa_guard_dedupe_lib" ]; then
|
|
|
139
139
|
# shellcheck source=guard-dedupe.bash
|
|
140
140
|
. "$lisa_guard_dedupe_lib"
|
|
141
141
|
trap 'lisa_guard_dedupe_record $?' EXIT
|
|
142
|
-
lisa_guard_dedupe parity-safety-net "$input"
|
|
142
|
+
lisa_guard_dedupe parity-safety-net "$input" \
|
|
143
|
+
"$lisa_guard_hook_dir/parity-safety-net-heredoc.py"
|
|
143
144
|
fi
|
|
144
145
|
|
|
145
146
|
tool_name="$(printf '%s' "$input" | jq -r '.tool_name // empty')"
|
|
@@ -57,7 +57,8 @@ if [ -r "$lisa_guard_dedupe_lib" ]; then
|
|
|
57
57
|
# shellcheck source=guard-dedupe.bash
|
|
58
58
|
. "$lisa_guard_dedupe_lib"
|
|
59
59
|
trap 'lisa_guard_dedupe_record $?' EXIT
|
|
60
|
-
lisa_guard_dedupe worktree-binding-guard "$input"
|
|
60
|
+
lisa_guard_dedupe worktree-binding-guard "$input" \
|
|
61
|
+
"$lisa_guard_hook_dir/worktree-binding-guard.mjs"
|
|
61
62
|
fi
|
|
62
63
|
|
|
63
64
|
if ! command -v node >/dev/null 2>&1; then
|
|
@@ -679,7 +679,7 @@ function summarise(result, priorKills = []) {
|
|
|
679
679
|
`🛑 ${result.moment}: INTERRUPTED, not completed — ` +
|
|
680
680
|
`${result.interrupted}. ${result.passed.length} of ` +
|
|
681
681
|
`${result.total} gate(s) were proved before it stopped; the rest ` +
|
|
682
|
-
`were not
|
|
682
|
+
`were not proved. This is NOT a pass.`
|
|
683
683
|
);
|
|
684
684
|
}
|
|
685
685
|
return lines;
|
|
@@ -817,7 +817,7 @@ function verdictFor(gate, { proved, blockedBy, exec, siblings, declarations }) {
|
|
|
817
817
|
* @param {function({kind: string, gateId: string}): boolean} [options.recordKill]
|
|
818
818
|
* Kill-marker writer, injectable so marker cardinality can be proved without
|
|
819
819
|
* writing to the host's shared marker directory.
|
|
820
|
-
* @param {function(): (string|null)} [options.interrupted] Asks, between gates,
|
|
820
|
+
* @param {function(): (string|null)} [options.interrupted] Asks, between gates and before returning,
|
|
821
821
|
* whether the run this chain serves is still there. Injectable for the same
|
|
822
822
|
* reason `exec` is: a test states that the caller went away instead of having
|
|
823
823
|
* to kill a real hook mid-chain.
|
|
@@ -941,6 +941,11 @@ export function runGates({
|
|
|
941
941
|
blockedBy = gate.id;
|
|
942
942
|
}
|
|
943
943
|
|
|
944
|
+
// The caller may disappear while the FINAL gate executes. There is then no
|
|
945
|
+
// next iteration to observe it, and an optional gate cannot block on its own.
|
|
946
|
+
// Record the interruption before this result can claim completion.
|
|
947
|
+
interruption ??= interrupted();
|
|
948
|
+
|
|
944
949
|
const bucket = state => results.filter(entry => entry.state === state);
|
|
945
950
|
const result = {
|
|
946
951
|
moment,
|