@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
|
@@ -26,4 +26,12 @@
|
|
|
26
26
|
# failure this ledger exists to make impossible.
|
|
27
27
|
.lisa/WITHDRAWN.jsonl merge=union
|
|
28
28
|
|
|
29
|
+
# Short-lived operational hazards, appended by any session that declares
|
|
30
|
+
# one (CodySwannGT/lisa#3681). Same append-only JSONL shape as the
|
|
31
|
+
# tombstones above and the same built-in union driver — a default line
|
|
32
|
+
# merge would drop a hazard, and a hazard nobody is told about is the
|
|
33
|
+
# collision this ledger exists to prevent. Entries carry an expiry and
|
|
34
|
+
# stop applying on their own, so the file drains without an editor.
|
|
35
|
+
.lisa/HAZARDS.jsonl merge=union
|
|
36
|
+
|
|
29
37
|
# END: AI GUARDRAILS
|
|
@@ -86,6 +86,11 @@
|
|
|
86
86
|
import { spawnSync } from "node:child_process";
|
|
87
87
|
|
|
88
88
|
import { invokedAsScript } from "./lib/invoked-as-script.mjs";
|
|
89
|
+
// Downstream both files land flat in one `scripts/` directory, so this
|
|
90
|
+
// resolves there exactly as it does here. Imported rather than reimplemented:
|
|
91
|
+
// a second lane parser is a second answer to `whose is this?`, and two
|
|
92
|
+
// answers is the state this report exists to end.
|
|
93
|
+
import { parseLaneId, workItemLines } from "./lisa-work-item.mjs";
|
|
89
94
|
|
|
90
95
|
/** Branch name prefixes that are never work-in-flight. */
|
|
91
96
|
const IGNORED_PREFIXES = Object.freeze(["backup/", "gh-readonly-queue/"]);
|
|
@@ -254,9 +259,23 @@ export function branchesAhead(remote = "origin", base = "main") {
|
|
|
254
259
|
* The work-item reference a branch is bound to, and how strongly.
|
|
255
260
|
*
|
|
256
261
|
* The trailer is asked first because it is the same binding the commit gate
|
|
257
|
-
* enforces
|
|
258
|
-
*
|
|
259
|
-
*
|
|
262
|
+
* enforces — and it is read the way the gate reads it, through the exported
|
|
263
|
+
* `workItemLines`, over each commit's whole message.
|
|
264
|
+
*
|
|
265
|
+
* This used to ask git: `--format=%(trailers:key=Work-Item,valueonly)`. That is
|
|
266
|
+
* the obvious tool and it is the wrong one. Git's parser sees only a message's
|
|
267
|
+
* FINAL contiguous block of `Key: value` lines, and this fleet's convention
|
|
268
|
+
* puts `🤖 Generated with Claude Code` boilerplate below the trailer, which
|
|
269
|
+
* ends the block. Measured over non-merge commits that name an item: on a
|
|
270
|
+
* single multi-agent branch git found none on 47 of 51; over full
|
|
271
|
+
* default-branch history it missed 477 of 1,730. So the claim this docstring
|
|
272
|
+
* used to make — "it is routinely absent" — was mostly a property of the
|
|
273
|
+
* reader, not of the branches, and every one of those branches was ruled on
|
|
274
|
+
* from a branch-name INFERENCE while its canonical binding sat in the message
|
|
275
|
+
* unread. See the `work-item-trailer-definition` rule (#3747).
|
|
276
|
+
*
|
|
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.
|
|
260
279
|
* @param {string} branch Short branch name.
|
|
261
280
|
* @param {string} base Default branch name.
|
|
262
281
|
* @param {string} remote Remote name.
|
|
@@ -264,15 +283,12 @@ export function branchesAhead(remote = "origin", base = "main") {
|
|
|
264
283
|
* @returns {{ref: string, refSource: string} | undefined} The reference.
|
|
265
284
|
*/
|
|
266
285
|
export function workItemRef(branch, base, remote = "origin", exec = run) {
|
|
267
|
-
const
|
|
286
|
+
const messages = exec("git", [
|
|
268
287
|
"log",
|
|
269
|
-
"--format=%
|
|
288
|
+
"--format=%B",
|
|
270
289
|
`${remote}/${base}..${remote}/${branch}`,
|
|
271
290
|
]);
|
|
272
|
-
const fromTrailer =
|
|
273
|
-
?.split(/[\n,]/u)
|
|
274
|
-
.map(value => value.trim())
|
|
275
|
-
.find(value => value.length > 0);
|
|
291
|
+
const [fromTrailer] = workItemLines(messages ?? "");
|
|
276
292
|
if (fromTrailer) {
|
|
277
293
|
const digits = REF_IN_TRAILER.exec(fromTrailer);
|
|
278
294
|
if (digits) return { ref: digits[1], refSource: REF_SOURCE.TRAILER };
|
|
@@ -455,6 +471,38 @@ export function divergences(branches, base, remote = "origin", exec = run) {
|
|
|
455
471
|
return counts;
|
|
456
472
|
}
|
|
457
473
|
|
|
474
|
+
/**
|
|
475
|
+
* The lane that produced each branch's tip commit.
|
|
476
|
+
*
|
|
477
|
+
* Every session in this fleet pushes under ONE git identity, so `author.login`
|
|
478
|
+
* is a constant here and answers nothing: it is the same name on a branch a
|
|
479
|
+
* live session is still working and on one whose session ended days ago. The
|
|
480
|
+
* lane stamp is the only thing in the record that differs between them, which
|
|
481
|
+
* makes it the only thing this report can route on.
|
|
482
|
+
*
|
|
483
|
+
* A branch with no stamp is reported as unrouted rather than omitted. Silence
|
|
484
|
+
* about attribution reads as "nobody made this", and the sweep that motivated
|
|
485
|
+
* the stamp already reached two wrong conclusions that way.
|
|
486
|
+
* @param {ReadonlyArray<string>} branches Branch names, without the remote.
|
|
487
|
+
* @param {string} remote Remote the branches live on.
|
|
488
|
+
* @param {(command: string, args: string[]) => (string|undefined)} [exec] Runner.
|
|
489
|
+
* @returns {Map<string, string>} Lane id by branch, absent where none is stamped.
|
|
490
|
+
*/
|
|
491
|
+
export function branchLanes(branches, remote = "origin", exec = run) {
|
|
492
|
+
const lanes = new Map();
|
|
493
|
+
for (const branch of branches) {
|
|
494
|
+
const message = exec("git", [
|
|
495
|
+
"log",
|
|
496
|
+
"-1",
|
|
497
|
+
"--format=%B",
|
|
498
|
+
`${remote}/${branch}`,
|
|
499
|
+
]);
|
|
500
|
+
const lane = message === undefined ? undefined : parseLaneId(message);
|
|
501
|
+
if (lane !== undefined) lanes.set(branch, lane);
|
|
502
|
+
}
|
|
503
|
+
return lanes;
|
|
504
|
+
}
|
|
505
|
+
|
|
458
506
|
/**
|
|
459
507
|
* Branches carrying commits that no pull request has ever covered, each with
|
|
460
508
|
* the tracker's verdict on whether the WORK survived.
|
|
@@ -467,12 +515,14 @@ export function divergences(branches, base, remote = "origin", exec = run) {
|
|
|
467
515
|
* @param {Set<string>} inputs.submitted Branch names that have a pull request.
|
|
468
516
|
* @param {Map<string, WorkItem>} [inputs.items] Work-item states by branch.
|
|
469
517
|
* @param {Map<string, Divergence>} [inputs.divergence] Two-sided counts by branch.
|
|
518
|
+
* @param {Map<string, string>} [inputs.lanes] Lane id by branch.
|
|
470
519
|
* @returns {ReadonlyArray<Candidate>} The candidates, each carrying a verdict.
|
|
471
520
|
*/
|
|
472
521
|
export function orphanedBranches({
|
|
473
522
|
ahead,
|
|
474
523
|
divergence = new Map(),
|
|
475
524
|
items = new Map(),
|
|
525
|
+
lanes = new Map(),
|
|
476
526
|
submitted,
|
|
477
527
|
}) {
|
|
478
528
|
return ahead
|
|
@@ -488,6 +538,7 @@ export function orphanedBranches({
|
|
|
488
538
|
baseLeads: sides ? sides.baseAhead > 0 : undefined,
|
|
489
539
|
branch: row.branch,
|
|
490
540
|
divergence: sides,
|
|
541
|
+
lane: lanes.get(row.branch),
|
|
491
542
|
ref: item?.ref,
|
|
492
543
|
refSource: item?.refSource,
|
|
493
544
|
route: item?.route,
|
|
@@ -516,6 +567,7 @@ export function main(argv = process.argv.slice(2), probe = {}) {
|
|
|
516
567
|
const {
|
|
517
568
|
collectAhead = branchesAhead,
|
|
518
569
|
collectDivergence = divergences,
|
|
570
|
+
collectLanes = branchLanes,
|
|
519
571
|
collectSubmitted = branchesWithPullRequests,
|
|
520
572
|
collectWorkItems = workItemStates,
|
|
521
573
|
log = console.log,
|
|
@@ -553,6 +605,7 @@ export function main(argv = process.argv.slice(2), probe = {}) {
|
|
|
553
605
|
ahead,
|
|
554
606
|
divergence: collectDivergence(candidates, base, remote),
|
|
555
607
|
items: collectWorkItems(candidates, base, remote),
|
|
608
|
+
lanes: collectLanes(candidates, remote),
|
|
556
609
|
submitted,
|
|
557
610
|
});
|
|
558
611
|
log(
|
|
@@ -624,9 +677,25 @@ function renderRow(row, base) {
|
|
|
624
677
|
lines.push(
|
|
625
678
|
` ${row.ahead} commit(s) ahead of ${base}${direction(row, base)}`
|
|
626
679
|
);
|
|
680
|
+
lines.push(` ${routing(row)}`);
|
|
627
681
|
return lines;
|
|
628
682
|
}
|
|
629
683
|
|
|
684
|
+
/**
|
|
685
|
+
* Who this branch can be handed back to.
|
|
686
|
+
*
|
|
687
|
+
* The lane is a one-way digest of an id the agent runtime supplied: it names
|
|
688
|
+
* no session, no URL and no person, and a live session recognises its own work
|
|
689
|
+
* by deriving the same token rather than by being told whose it is.
|
|
690
|
+
* @param {Candidate} row A row from `orphanedBranches`.
|
|
691
|
+
* @returns {string} The routing clause.
|
|
692
|
+
*/
|
|
693
|
+
function routing(row) {
|
|
694
|
+
if (row.lane === undefined)
|
|
695
|
+
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
|
+
return `produced by ${row.lane} — a session confirms this is its own with \`node scripts/lisa-work-item.mjs lane\``;
|
|
697
|
+
}
|
|
698
|
+
|
|
630
699
|
/**
|
|
631
700
|
* The conflict-direction clause for one candidate.
|
|
632
701
|
*
|
|
@@ -160,8 +160,24 @@ const NO_VERSION = "missing-version-comment";
|
|
|
160
160
|
* `uses: <owner>/<repo>[/<subpath>]@<ref>` with anything after it captured, so
|
|
161
161
|
* a trailing `# v1.2.3` can be read. Local (`./path`) and container
|
|
162
162
|
* (`docker://`) references have no owner and never match.
|
|
163
|
+
*
|
|
164
|
+
* The value may be QUOTED, and that is not a stylistic detail here. YAML
|
|
165
|
+
* accepts `uses: "owner/repo@ref"` and `uses: 'owner/repo@ref'` as the same
|
|
166
|
+
* scalar GitHub Actions resolves identically, but a value class that excluded
|
|
167
|
+
* quotes could not begin matching at one — so a quoted reference did not fail
|
|
168
|
+
* the scan, it left it entirely, and the gate reported clean over an entry it
|
|
169
|
+
* had never inspected. A pin checker that silently skips a spelling is a
|
|
170
|
+
* vacuous pass on every reference written that way, which is the failure this
|
|
171
|
+
* gate exists to close rather than to reproduce.
|
|
172
|
+
*
|
|
173
|
+
* The closing quote is matched by back-reference so `"a'` cannot read as a
|
|
174
|
+
* quoted value, and it is consumed by the match so the trailing `# v1.2.3` is
|
|
175
|
+
* still found by the slice in `findActionRefs`. An unquoted value binds the
|
|
176
|
+
* group to the empty string, where the back-reference matches empty and the
|
|
177
|
+
* pattern behaves exactly as it did before.
|
|
163
178
|
*/
|
|
164
|
-
const USES_RE =
|
|
179
|
+
const USES_RE =
|
|
180
|
+
/^[ \t]*(?:-[ \t]*)?uses:[ \t]*(?<quote>["']?)(?<value>[^\s"'#]+)\k<quote>/;
|
|
165
181
|
|
|
166
182
|
/** A well-formed action owner. Anchored, so matching is linear. */
|
|
167
183
|
const OWNER_RE = /^[A-Za-z0-9][A-Za-z0-9-]*$/;
|
|
@@ -11,6 +11,20 @@
|
|
|
11
11
|
* This small asynchronous supervisor is invoked through a synchronous parent:
|
|
12
12
|
* it owns the process-group id, applies the deadline, terminates the group, and
|
|
13
13
|
* only then lets the parent continue.
|
|
14
|
+
*
|
|
15
|
+
* ## Interrupting a run
|
|
16
|
+
*
|
|
17
|
+
* The deadline was never the only way a run ends early. An operator — or an
|
|
18
|
+
* agent told to free a contended machine — kills the gate runner, and because
|
|
19
|
+
* this supervisor holds the ONLY handle to the detached group it started,
|
|
20
|
+
* killing its parent used to leave it reparented to pid 1 with the whole tree
|
|
21
|
+
* still executing. Measured: the kill reported success, `pgrep` for the runner
|
|
22
|
+
* returned nothing, and eleven processes carried on (CodySwannGT/lisa#3829).
|
|
23
|
+
*
|
|
24
|
+
* So the supervisor watches for the run it serves going away, tears the tree
|
|
25
|
+
* down through the same single reap the deadline uses, and SAYS SO. A silent
|
|
26
|
+
* teardown would be the same defect wearing a different face: the operator who
|
|
27
|
+
* asked for the run to stop cannot tell a stop from a survival either way.
|
|
14
28
|
* @module scripts/lib/process-tree-runner
|
|
15
29
|
*/
|
|
16
30
|
|
|
@@ -22,6 +36,16 @@ const KILL_GRACE_MS = 750;
|
|
|
22
36
|
const REAP_POLL_MS = 25;
|
|
23
37
|
const WINDOWS_TIMEOUT_EXIT_CODE = 255;
|
|
24
38
|
const TERMINATING_SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* How often the supervisor asks whether the run it serves is still there.
|
|
42
|
+
*
|
|
43
|
+
* Four times a second. This is a liveness poll, not a budget: the cost of
|
|
44
|
+
* asking is one `kill(pid, 0)` per watched pid, and the cost of asking too
|
|
45
|
+
* rarely is measured in whole test suites that keep running for a machine
|
|
46
|
+
* nobody is watching any more.
|
|
47
|
+
*/
|
|
48
|
+
const ORPHAN_POLL_MS = 250;
|
|
25
49
|
const DEFAULT_REAP_CONTROLS = Object.freeze({
|
|
26
50
|
kill: killTree,
|
|
27
51
|
exists: treeExists,
|
|
@@ -41,17 +65,126 @@ export function timeoutVerdictForPlatform(platform) {
|
|
|
41
65
|
: { code: null, signal: "SIGKILL" };
|
|
42
66
|
}
|
|
43
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Whether a pid is worth watching for disappearance.
|
|
70
|
+
*
|
|
71
|
+
* `1` is excluded on purpose rather than by accident. It is both the pid an
|
|
72
|
+
* orphan is reparented ONTO and a process that never exits, so watching it
|
|
73
|
+
* would arm a condition that can never fire while looking exactly like a
|
|
74
|
+
* watch that is working.
|
|
75
|
+
* @param {unknown} value A candidate pid.
|
|
76
|
+
* @returns {boolean} Whether it names a process that can meaningfully die.
|
|
77
|
+
*/
|
|
78
|
+
export function isWatchablePid(value) {
|
|
79
|
+
return Number.isInteger(value) && value > 1;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Parse the pids this supervisor was asked to outlive nobody but.
|
|
84
|
+
* @param {readonly string[]} argv Raw arguments.
|
|
85
|
+
* @returns {number[]} Watchable pids, in the order given, without duplicates.
|
|
86
|
+
*/
|
|
87
|
+
export function parseWatchPids(argv) {
|
|
88
|
+
const prefix = "--watch-pid=";
|
|
89
|
+
const pids = argv
|
|
90
|
+
.filter(value => value.startsWith(prefix))
|
|
91
|
+
.map(value => Number(value.slice(prefix.length)))
|
|
92
|
+
.filter(pid => isWatchablePid(pid));
|
|
93
|
+
return [...new Set(pids)];
|
|
94
|
+
}
|
|
95
|
+
|
|
44
96
|
function parseArguments(argv) {
|
|
45
97
|
const timeoutArg = argv.find(value => value.startsWith("--timeout-ms="));
|
|
46
98
|
const separator = argv.indexOf("--");
|
|
47
99
|
const timeoutMs = Number(timeoutArg?.slice("--timeout-ms=".length));
|
|
100
|
+
const flags = separator >= 0 ? argv.slice(0, separator) : argv;
|
|
48
101
|
const command = separator >= 0 ? argv.slice(separator + 1).join(" ") : "";
|
|
49
102
|
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0 || !command) {
|
|
50
103
|
throw new Error(
|
|
51
|
-
"usage: process-tree-runner.mjs --timeout-ms=<positive-ms>
|
|
104
|
+
"usage: process-tree-runner.mjs --timeout-ms=<positive-ms> " +
|
|
105
|
+
"[--watch-pid=<pid>]... -- <command>"
|
|
52
106
|
);
|
|
53
107
|
}
|
|
54
|
-
return { command, timeoutMs };
|
|
108
|
+
return { command, timeoutMs, watchPids: parseWatchPids(flags) };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Whether a pid no longer names a live process.
|
|
113
|
+
*
|
|
114
|
+
* Fail-CLOSED, matching every other liveness question in this file: only the
|
|
115
|
+
* kernel's `ESRCH` proves absence. An `EPERM` — a pid that outlived the run and
|
|
116
|
+
* was reused by another user's process — means the question was refused, not
|
|
117
|
+
* answered, and refusing to answer must never be read as "the run is over".
|
|
118
|
+
* That direction matters here more than anywhere else in the module: a wrong
|
|
119
|
+
* "gone" tears down a gate run that was still executing correctly.
|
|
120
|
+
* @param {number} pid The process to ask about.
|
|
121
|
+
* @param {typeof process.kill} probe Injectable process probe.
|
|
122
|
+
* @returns {boolean} Whether the process is provably absent.
|
|
123
|
+
*/
|
|
124
|
+
export function pidIsGone(pid, probe = process.kill) {
|
|
125
|
+
try {
|
|
126
|
+
probe(pid, 0);
|
|
127
|
+
return false;
|
|
128
|
+
} catch (error) {
|
|
129
|
+
return isAbsentProcessError(error);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Why this supervisor should stop, if it should.
|
|
135
|
+
*
|
|
136
|
+
* Two separable questions, deliberately answered in one place so the report
|
|
137
|
+
* can name WHICH one fired:
|
|
138
|
+
*
|
|
139
|
+
* 1. **Orphaning.** The process that started this supervisor is gone, so the
|
|
140
|
+
* verdict this run would produce has no one left to receive it. Detected by
|
|
141
|
+
* comparing the parent pid captured at start against the live one — a
|
|
142
|
+
* reparented process reads `1` (or a subreaper) instead of its launcher.
|
|
143
|
+
* 2. **A watched pid died.** The caller named a process further up the chain —
|
|
144
|
+
* a git hook, say — whose death means the run is moot even though this
|
|
145
|
+
* supervisor's own parent is still alive.
|
|
146
|
+
*
|
|
147
|
+
* Watch-pid absence is proved with `pidIsGone`, which fails closed; the
|
|
148
|
+
* orphan check needs no such care because a changed parent pid is a fact the
|
|
149
|
+
* kernel reports directly and cannot be refused.
|
|
150
|
+
* @param {object} options Inputs.
|
|
151
|
+
* @param {number} options.launchParentPid Parent pid captured at start.
|
|
152
|
+
* @param {number} options.currentParentPid Parent pid right now.
|
|
153
|
+
* @param {readonly number[]} [options.watchPids] Pids to outlive nobody but.
|
|
154
|
+
* @param {(pid: number) => boolean} [options.gone] Injectable absence probe.
|
|
155
|
+
* @returns {string|null} An operator-readable reason, or null to keep running.
|
|
156
|
+
*/
|
|
157
|
+
export function interruptionReason({
|
|
158
|
+
launchParentPid,
|
|
159
|
+
currentParentPid,
|
|
160
|
+
watchPids = [],
|
|
161
|
+
gone = pidIsGone,
|
|
162
|
+
}) {
|
|
163
|
+
if (isWatchablePid(launchParentPid) && currentParentPid !== launchParentPid) {
|
|
164
|
+
return `the run that started it (pid ${launchParentPid}) exited`;
|
|
165
|
+
}
|
|
166
|
+
const dead = watchPids.find(pid => gone(pid));
|
|
167
|
+
if (dead !== undefined) return `the run it serves (pid ${dead}) exited`;
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* The line an interrupted supervisor leaves behind.
|
|
173
|
+
*
|
|
174
|
+
* A silent teardown reintroduces the defect in a different form: the operator
|
|
175
|
+
* who asked for the run to stop gets the same silence whether it stopped or
|
|
176
|
+
* kept running. So this states three things — that the run was INTERRUPTED
|
|
177
|
+
* rather than completed, why, and what was terminated — and it goes to stderr,
|
|
178
|
+
* which the gate runner inherits.
|
|
179
|
+
* @param {string} reason Why the supervisor stopped.
|
|
180
|
+
* @param {number} pid The process-group leader that was reaped.
|
|
181
|
+
* @returns {string} One line, for stderr.
|
|
182
|
+
*/
|
|
183
|
+
export function interruptionReport(reason, pid) {
|
|
184
|
+
return (
|
|
185
|
+
`🛑 gate command INTERRUPTED — ${reason}; terminated its process tree ` +
|
|
186
|
+
`(group ${pid}) and every descendant. Nothing was proved by it.`
|
|
187
|
+
);
|
|
55
188
|
}
|
|
56
189
|
|
|
57
190
|
/** Whether a failed POSIX process-group operation proved the group is absent. */
|
|
@@ -265,7 +398,32 @@ export async function reapTree(pid, controls = DEFAULT_REAP_CONTROLS) {
|
|
|
265
398
|
throw new Error(`gate process tree ${pid} survived SIGKILL`);
|
|
266
399
|
}
|
|
267
400
|
|
|
268
|
-
|
|
401
|
+
/**
|
|
402
|
+
* Run one command as a supervised process tree.
|
|
403
|
+
*
|
|
404
|
+
* The supervisor answers to three ways a run can end early — its deadline, a
|
|
405
|
+
* terminating signal, and now the run it serves going away — and all three
|
|
406
|
+
* share one reap, because a tree reaped twice is a tree whose second kill
|
|
407
|
+
* lands on a recycled pid.
|
|
408
|
+
* @param {string} command Shell source to supervise.
|
|
409
|
+
* @param {number} timeoutMs Deadline for the whole tree.
|
|
410
|
+
* @param {typeof reapTree} [reap] Injectable group reaper.
|
|
411
|
+
* @param {object} [options] Interrupt-watch seams, injectable for tests.
|
|
412
|
+
* @param {readonly number[]} [options.watchPids] Pids whose death ends this run.
|
|
413
|
+
* @param {typeof interruptionReason} [options.detect] Interrupt predicate.
|
|
414
|
+
* @param {number} [options.pollMs] How often to ask.
|
|
415
|
+
* @param {(line: string) => void} [options.report] Where the report goes.
|
|
416
|
+
* @param {number} [options.launchParentPid] Parent pid captured at start.
|
|
417
|
+
* @returns {Promise<{code: number|null, signal: string|null}>} The verdict.
|
|
418
|
+
*/
|
|
419
|
+
export function supervise(command, timeoutMs, reap = reapTree, options = {}) {
|
|
420
|
+
const {
|
|
421
|
+
watchPids = [],
|
|
422
|
+
detect = interruptionReason,
|
|
423
|
+
pollMs = ORPHAN_POLL_MS,
|
|
424
|
+
report = line => process.stderr.write(`${line}\n`),
|
|
425
|
+
launchParentPid = process.ppid,
|
|
426
|
+
} = options;
|
|
269
427
|
return new Promise((resolve, reject) => {
|
|
270
428
|
const shell =
|
|
271
429
|
process.platform === "win32"
|
|
@@ -336,6 +494,7 @@ export function supervise(command, timeoutMs, reap = reapTree) {
|
|
|
336
494
|
};
|
|
337
495
|
const cleanup = () => {
|
|
338
496
|
clearDeadline();
|
|
497
|
+
clearInterval(interruptWatch);
|
|
339
498
|
clearSignalHandlers();
|
|
340
499
|
};
|
|
341
500
|
// Every exit path shares one reap. A signal that arrives while the direct
|
|
@@ -376,17 +535,43 @@ export function supervise(command, timeoutMs, reap = reapTree) {
|
|
|
376
535
|
);
|
|
377
536
|
};
|
|
378
537
|
|
|
379
|
-
|
|
380
|
-
|
|
538
|
+
// Both early-stop paths below end the same way: reap the tree ONCE, say
|
|
539
|
+
// what was terminated, then take the signal-shaped exit. `stopWith` exists
|
|
540
|
+
// so the reporting cannot drift between them — a signal that tears the
|
|
541
|
+
// tree down silently and a watch that reports is the same defect twice.
|
|
542
|
+
const stopWith = (reason, signal) => {
|
|
543
|
+
if (terminating || settled) return;
|
|
381
544
|
terminating = true;
|
|
382
545
|
settling = true;
|
|
383
546
|
clearDeadline();
|
|
547
|
+
clearInterval(interruptWatch);
|
|
384
548
|
reapOnce().then(() => {
|
|
385
549
|
cleanup();
|
|
550
|
+
report(interruptionReport(reason, pid));
|
|
386
551
|
// Restore the signal-shaped exit after the detached tree is gone.
|
|
387
552
|
process.kill(process.pid, signal);
|
|
388
553
|
}, failReap);
|
|
389
554
|
};
|
|
555
|
+
const relaySignal = signal => {
|
|
556
|
+
stopWith(`it received ${signal}`, signal);
|
|
557
|
+
};
|
|
558
|
+
// The wiring this module always had the means for and nothing invoked.
|
|
559
|
+
// Without it, killing the gate runner leaves this supervisor reparented to
|
|
560
|
+
// pid 1 with its whole detached tree still executing — measured, and the
|
|
561
|
+
// reason the tree survives is precisely that this supervisor holds the
|
|
562
|
+
// only handle to it. Nothing else on the machine knows the group id.
|
|
563
|
+
const interruptWatch = setInterval(() => {
|
|
564
|
+
if (terminating || settling || settled) return;
|
|
565
|
+
const reason = detect({
|
|
566
|
+
launchParentPid,
|
|
567
|
+
currentParentPid: process.ppid,
|
|
568
|
+
watchPids,
|
|
569
|
+
});
|
|
570
|
+
// SIGTERM rather than the signal nobody sent: this supervisor was not
|
|
571
|
+
// signalled, it outlived its purpose, and SIGTERM is the vocabulary the
|
|
572
|
+
// gate runner already reads as "terminated, therefore no verdict".
|
|
573
|
+
if (reason !== null) stopWith(reason, "SIGTERM");
|
|
574
|
+
}, pollMs);
|
|
390
575
|
const deadline = setTimeout(() => {
|
|
391
576
|
timedOut = true;
|
|
392
577
|
terminating = true;
|
|
@@ -432,8 +617,10 @@ export function supervise(command, timeoutMs, reap = reapTree) {
|
|
|
432
617
|
}
|
|
433
618
|
|
|
434
619
|
async function main() {
|
|
435
|
-
const { command, timeoutMs } = parseArguments(
|
|
436
|
-
|
|
620
|
+
const { command, timeoutMs, watchPids } = parseArguments(
|
|
621
|
+
process.argv.slice(2)
|
|
622
|
+
);
|
|
623
|
+
const result = await supervise(command, timeoutMs, reapTree, { watchPids });
|
|
437
624
|
if (result.signal) {
|
|
438
625
|
process.kill(process.pid, result.signal);
|
|
439
626
|
} else {
|
|
@@ -832,6 +832,37 @@ if [ "$notice_due" -eq 1 ]; then
|
|
|
832
832
|
fi
|
|
833
833
|
fi
|
|
834
834
|
|
|
835
|
+
# ---------------------------------------------------------------------------
|
|
836
|
+
# One evaluation per guard per tool call
|
|
837
|
+
#
|
|
838
|
+
# The guards below are also registered individually by the plugin manifest, so
|
|
839
|
+
# on a machine where both channels are live each one runs twice for a single
|
|
840
|
+
# tool call. `guard-dedupe.bash`, sourced by each guard, lets the second copy
|
|
841
|
+
# replay the first copy's ALLOW instead of recomputing it — and only when the
|
|
842
|
+
# two copies are byte-identical, on the identical payload, within the identical
|
|
843
|
+
# tool call. Nothing above is de-registered for it, because plugin-hook liveness
|
|
844
|
+
# is not observable from disk and standing down on a disk guess is how this
|
|
845
|
+
# dispatcher once switched enforcement off silently (CodySwannGT/lisa#3814).
|
|
846
|
+
#
|
|
847
|
+
# What is handed down here is only the state directory. `$notice_temp_base` has
|
|
848
|
+
# already been resolved physically and had every parent checked for ownership
|
|
849
|
+
# and mode, so the eight guards this loop spawns inherit that work instead of
|
|
850
|
+
# repeating it eight times. A guard reached by the plugin channel has no such
|
|
851
|
+
# parent and resolves its own; a guard that finds this variable unusable does
|
|
852
|
+
# the same. Exported only when the chain was trusted, so an untrusted TMPDIR
|
|
853
|
+
# hands down nothing rather than handing down a bad path.
|
|
854
|
+
if [ -n "$notice_temp_base" ] && [ "$notice_state_trusted" -eq 1 ]; then
|
|
855
|
+
guard_memo_dir="$notice_temp_base/lisa-guard-memo-$notice_uid"
|
|
856
|
+
if [ ! -e "$guard_memo_dir" ] && [ ! -L "$guard_memo_dir" ]; then
|
|
857
|
+
(umask 077 && mkdir "$guard_memo_dir") 2>/dev/null || true
|
|
858
|
+
fi
|
|
859
|
+
if [ -d "$guard_memo_dir" ] && [ ! -L "$guard_memo_dir" ] && \
|
|
860
|
+
[ -O "$guard_memo_dir" ]; then
|
|
861
|
+
export LISA_GUARD_MEMO_DIR="$guard_memo_dir"
|
|
862
|
+
export LISA_GUARD_MEMO_UID="$notice_uid"
|
|
863
|
+
fi
|
|
864
|
+
fi
|
|
865
|
+
|
|
835
866
|
# ---------------------------------------------------------------------------
|
|
836
867
|
# Dispatch
|
|
837
868
|
status=0
|
|
@@ -165,6 +165,27 @@ set -euo pipefail
|
|
|
165
165
|
|
|
166
166
|
input="$(cat)"
|
|
167
167
|
|
|
168
|
+
# Evaluate once per tool call when this guard is registered on both channels.
|
|
169
|
+
#
|
|
170
|
+
# Lisa reaches an agent through the repository dispatcher AND the plugin
|
|
171
|
+
# manifest, and where both are live the harness runs this guard twice for one
|
|
172
|
+
# tool call. `guard-dedupe.bash` short-circuits the second run ONLY when a
|
|
173
|
+
# byte-identical copy already ALLOWED this exact payload on this exact tool
|
|
174
|
+
# call; a differing vintage, a refusal, and a host with one channel all
|
|
175
|
+
# evaluate exactly as before. Nothing is de-registered by it
|
|
176
|
+
# (CodySwannGT/lisa#3814).
|
|
177
|
+
#
|
|
178
|
+
# Absent library means no dedupe, which is the pre-existing behaviour, so an
|
|
179
|
+
# older channel copy that predates it is unaffected.
|
|
180
|
+
lisa_guard_hook_dir="${BASH_SOURCE[0]%/*}"
|
|
181
|
+
lisa_guard_dedupe_lib="$lisa_guard_hook_dir/guard-dedupe.bash"
|
|
182
|
+
if [ -r "$lisa_guard_dedupe_lib" ]; then
|
|
183
|
+
# shellcheck source=guard-dedupe.bash
|
|
184
|
+
. "$lisa_guard_dedupe_lib"
|
|
185
|
+
trap 'lisa_guard_dedupe_record $?' EXIT
|
|
186
|
+
lisa_guard_dedupe block-blind-automerge "$input"
|
|
187
|
+
fi
|
|
188
|
+
|
|
168
189
|
# Both interpreters are probed BEFORE use, and a missing one is ANNOUNCED
|
|
169
190
|
# rather than swallowed. Under `set -e` an absent jq would abort with 127, and
|
|
170
191
|
# Claude Code treats any non-2 exit as a non-blocking hook error — so the guard
|
|
@@ -247,9 +268,41 @@ PULL_REQUEST_ID_PATTERN = re.compile(
|
|
|
247
268
|
NODE_ID_PATTERN = re.compile(r"^[A-Za-z0-9_=-]+$")
|
|
248
269
|
|
|
249
270
|
# The probe that resolves a node id to the same fields `gh pr view` returns.
|
|
271
|
+
#
|
|
272
|
+
# `statusCheckRollup` is in here because the RE-TARGET path needs it and this
|
|
273
|
+
# is the only probe a GraphQL `updatePullRequest` can use. Without it
|
|
274
|
+
# `failing_check_names` read an absent key, found no failing check, and the
|
|
275
|
+
# guard announced the re-target as the sanctioned green-PR batching case —
|
|
276
|
+
# for a pull request that might be failing every check it has. The porcelain
|
|
277
|
+
# path asked the right question and the API path could not, so the identical
|
|
278
|
+
# act was guarded through `gh pr edit --base` and waved through as GraphQL:
|
|
279
|
+
# a bypass that needs no permission, only a different spelling.
|
|
280
|
+
#
|
|
281
|
+
# The nodes are reshaped by `--jq` below into the flat list `gh pr view
|
|
282
|
+
# --json statusCheckRollup` returns, so one reader serves both probes.
|
|
250
283
|
NODE_QUERY = (
|
|
251
284
|
"query($id:ID!){node(id:$id){... on PullRequest"
|
|
252
|
-
"{number url reviewDecision state baseRefName
|
|
285
|
+
"{number url reviewDecision state baseRefName "
|
|
286
|
+
"commits(last:1){nodes{commit{statusCheckRollup{contexts(first:100){nodes{"
|
|
287
|
+
"__typename ... on CheckRun{name conclusion} "
|
|
288
|
+
"... on StatusContext{context state}"
|
|
289
|
+
"}}}}}}}}}"
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
# Reshape the envelope into the porcelain's shape: unwrap `.data.node`, hoist
|
|
293
|
+
# the last commit's rollup contexts to a top-level `statusCheckRollup` list,
|
|
294
|
+
# and drop the `commits` scaffolding that carried it.
|
|
295
|
+
# `has("commits")` rather than an unconditional default, and the distinction is
|
|
296
|
+
# the point: a node that CARRIES the scaffolding and no contexts is a PR with no
|
|
297
|
+
# checks, which is genuinely not failing and gets an empty list. A node without
|
|
298
|
+
# the scaffolding is a probe that never asked, and it must leave the key absent
|
|
299
|
+
# so the re-target path can tell the two apart instead of reading both as green.
|
|
300
|
+
NODE_JQ = (
|
|
301
|
+
"if .data.node == null then null else .data.node as $n "
|
|
302
|
+
"| if ($n | has(\"commits\")) then ($n | del(.commits)) "
|
|
303
|
+
"+ {statusCheckRollup: "
|
|
304
|
+
"((($n.commits.nodes[0].commit.statusCheckRollup.contexts.nodes)) // [])} "
|
|
305
|
+
"else $n end end"
|
|
253
306
|
)
|
|
254
307
|
|
|
255
308
|
# What a probe spec asks for. `view` and `node` differ only in how the PR is
|
|
@@ -728,7 +781,7 @@ def probe_args(kind, subject, repo_args, fields):
|
|
|
728
781
|
"gh", "api", "graphql",
|
|
729
782
|
"-f", "query=" + NODE_QUERY,
|
|
730
783
|
"-f", "id=" + subject,
|
|
731
|
-
"--jq",
|
|
784
|
+
"--jq", NODE_JQ,
|
|
732
785
|
]
|
|
733
786
|
args = ["gh", "pr", "view"]
|
|
734
787
|
if subject is not None:
|
|
@@ -969,6 +1022,20 @@ Do one of these instead:
|
|
|
969
1022
|
- leave the PR on its covered base and merge it there.
|
|
970
1023
|
"""
|
|
971
1024
|
|
|
1025
|
+
RETARGET_CHECKS_UNREADABLE = """Blocked: re-targeting %s onto "%s" cannot be judged, because this guard could
|
|
1026
|
+
not read the pull request's checks.
|
|
1027
|
+
|
|
1028
|
+
That ref has ZERO required status checks, so the one thing standing between the
|
|
1029
|
+
move and a permission-free gate bypass is whether the PR is currently failing —
|
|
1030
|
+
and the probe returned no `statusCheckRollup` at all. An absent rollup is not an
|
|
1031
|
+
empty one. Reporting the move as the sanctioned green-PR batching case here
|
|
1032
|
+
would be a verdict about a question that was never asked.
|
|
1033
|
+
|
|
1034
|
+
Re-target through `gh pr edit --base` instead, which reads the checks, or fix
|
|
1035
|
+
the probe. Do not report the PR as batched until something has actually looked.
|
|
1036
|
+
"""
|
|
1037
|
+
|
|
1038
|
+
|
|
972
1039
|
UNCOVERED_RETARGET_NOTICE = (
|
|
973
1040
|
"block-blind-automerge: %s is being re-targeted onto \"%s\", a ref with ZERO "
|
|
974
1041
|
"required status checks; its gates will run but cannot block a merge\n"
|
|
@@ -1039,6 +1106,18 @@ for act, kind, subject, repo_args, target_ref in acts:
|
|
|
1039
1106
|
# nothing can block it", which holds whatever base it came from.
|
|
1040
1107
|
if not base_is_uncovered(payload, repo_args, target_ref):
|
|
1041
1108
|
continue
|
|
1109
|
+
# Fails CLOSED, and only here. Everywhere else in this file an
|
|
1110
|
+
# unanswerable question degrades and continues, because the guard being
|
|
1111
|
+
# unable to run must not become the reason a command is refused. This
|
|
1112
|
+
# branch is different: the base has already been confirmed to have zero
|
|
1113
|
+
# required checks, so an empty `failing` list is the whole basis for
|
|
1114
|
+
# allowing the move, and an ABSENT rollup produces the identical empty
|
|
1115
|
+
# list. Passing on it would be a measurement that could not fail.
|
|
1116
|
+
if not isinstance(payload.get("statusCheckRollup"), list):
|
|
1117
|
+
sys.stderr.write(
|
|
1118
|
+
RETARGET_CHECKS_UNREADABLE % (pr_name(payload), target_ref)
|
|
1119
|
+
)
|
|
1120
|
+
sys.exit(1)
|
|
1042
1121
|
failing = failing_check_names(payload)
|
|
1043
1122
|
if not failing:
|
|
1044
1123
|
# A green PR moving onto a stack base is the sanctioned batching
|