@codyswann/lisa 4.33.12 → 4.33.14
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/lisa-hooks/block-direct-issue-create.sh +6 -2
- package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +53 -4
- package/dist/configs/vitest/scratch-setup.d.ts +34 -0
- package/dist/configs/vitest/scratch-setup.d.ts.map +1 -1
- package/dist/configs/vitest/scratch-setup.js +89 -10
- package/dist/configs/vitest/scratch-setup.js.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +6 -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 +12 -8
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/opencode/plugin-templates/parity-safety-net.sh +53 -4
- 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-setup-workstation/scripts/catalogue.mjs +29 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-track/SKILL.md +17 -6
- package/plugins/lisa/hooks/block-direct-issue-create.sh +6 -2
- package/plugins/lisa/hooks/parity-safety-net.sh +53 -4
- package/plugins/lisa/skills/lisa-setup-workstation/scripts/catalogue.mjs +29 -0
- package/plugins/lisa/skills/lisa-track/SKILL.md +18 -7
- package/plugins/lisa-agy/hooks/block-direct-issue-create.sh +6 -2
- package/plugins/lisa-agy/hooks/parity-safety-net.sh +53 -4
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-setup-workstation/scripts/catalogue.mjs +29 -0
- package/plugins/lisa-agy/skills/lisa-track/SKILL.md +18 -7
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/hooks/block-direct-issue-create.sh +6 -2
- package/plugins/lisa-copilot/hooks/parity-safety-net.sh +53 -4
- package/plugins/lisa-copilot/skills/lisa-setup-workstation/scripts/catalogue.mjs +29 -0
- package/plugins/lisa-copilot/skills/lisa-track/SKILL.md +18 -7
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/block-direct-issue-create.sh +6 -2
- package/plugins/lisa-cursor/hooks/parity-safety-net.sh +53 -4
- package/plugins/lisa-cursor/skills/lisa-setup-workstation/scripts/catalogue.mjs +29 -0
- package/plugins/lisa-cursor/skills/lisa-track/SKILL.md +18 -7
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/hooks/block-direct-issue-create.sh +6 -2
- package/plugins/src/base/hooks/parity-safety-net.sh +53 -4
- package/plugins/src/base/skills/lisa-setup-workstation/scripts/catalogue.mjs +29 -0
- package/plugins/src/base/skills/lisa-track/SKILL.md +18 -7
- package/typescript/copy-contents/.husky/pre-commit +10 -6
- package/typescript/copy-contents/.husky/pre-push +46 -16
|
@@ -1019,14 +1019,40 @@ readonly GIT_CMD='(^|[^[:alnum:]_-])git[[:space:]]+'"$GIT_GLOBAL_OPTS"
|
|
|
1019
1019
|
# match. The preceding-char class still excludes `.`, `/`, and `-` to keep
|
|
1020
1020
|
# `--rm`-style flags and `foo.rm` names out.
|
|
1021
1021
|
readonly RM_CMD='(^|[^[:alnum:]_./-])([[:alnum:]_./-]*/)?rm'
|
|
1022
|
+
# Where a recursive-forced flag cluster is allowed to END.
|
|
1023
|
+
#
|
|
1024
|
+
# It used to be `([[:space:]]|$)` — whitespace or end of segment — and a closing
|
|
1025
|
+
# parenthesis is neither, so a SUBSHELL-wrapped pipeline fell out of every guard
|
|
1026
|
+
# built on these two patterns while its brace-wrapped twin was caught: `{ find
|
|
1027
|
+
# .git -type d | parallel rm -rf; }` matched because the `;` supplied the
|
|
1028
|
+
# boundary, and `(find .git -type d | parallel rm -rf)` did not (issue #3494).
|
|
1029
|
+
#
|
|
1030
|
+
# So the two STATEMENT-GROUPING terminators join whitespace here, and nothing
|
|
1031
|
+
# else. The general spelling — "any character that cannot be part of a flag",
|
|
1032
|
+
# `([^[:alnum:]-]|$)` — is the more principled statement of the intent, and is
|
|
1033
|
+
# deliberately NOT used: these patterns feed the rm TARGET WALK, the guard with
|
|
1034
|
+
# this file's longest false-positive history, and it would newly match ordinary
|
|
1035
|
+
# prose punctuation (`echo "rm -rf, then …"` on the comma). Paren and semicolon
|
|
1036
|
+
# buy the brace/paren parity the defect is about and buy nothing else.
|
|
1037
|
+
#
|
|
1038
|
+
# The cluster must still END at the boundary, so `-rfoo` is not read as `-rf`.
|
|
1039
|
+
readonly RM_RF_END='([[:space:];)]|$)'
|
|
1022
1040
|
# rm invoked with BOTH a recursive and a force flag — clustered (-rf/-fr, any
|
|
1023
1041
|
# extra letters) or split, in either order. The split gate pairs ANY recursive
|
|
1024
1042
|
# form (short cluster containing r, or --recursive) with ANY force form (short
|
|
1025
1043
|
# cluster containing f, or --force), so mixed spellings like `rm -r --force /`
|
|
1026
1044
|
# and `rm --recursive -f /` cannot slip between the short/short and long/long
|
|
1027
1045
|
# alternations (PR #1976 review).
|
|
1028
|
-
|
|
1029
|
-
|
|
1046
|
+
#
|
|
1047
|
+
# The FLAG TAILS are named separately from the `rm`-anchored patterns because
|
|
1048
|
+
# the find/xargs family below needs the identical flag vocabulary against a
|
|
1049
|
+
# different verb anchor. Before #3494 it carried its own short-cluster-only copy
|
|
1050
|
+
# and therefore missed `rm --recursive --force` — a divergence that could only
|
|
1051
|
+
# happen because the spelling set was written twice.
|
|
1052
|
+
readonly RM_RF_TAIL_CLUSTER='([[:space:]]+-[[:alnum:]-]+)*[[:space:]]+(-[[:alnum:]]*r[[:alnum:]]*f|-[[:alnum:]]*f[[:alnum:]]*r)'"$RM_RF_END"
|
|
1053
|
+
readonly RM_RF_TAIL_SPLIT='(([[:space:]][^;&|]*)?[[:space:]](-[[:alnum:]]*r[[:alnum:]]*|--recursive)([[:space:]][^;&|]*)?[[:space:]](-[[:alnum:]]*f[[:alnum:]]*|--force)'"$RM_RF_END"'|([[:space:]][^;&|]*)?[[:space:]](-[[:alnum:]]*f[[:alnum:]]*|--force)([[:space:]][^;&|]*)?[[:space:]](-[[:alnum:]]*r[[:alnum:]]*|--recursive)'"$RM_RF_END"')'
|
|
1054
|
+
readonly RM_RF_CLUSTER="$RM_CMD$RM_RF_TAIL_CLUSTER"
|
|
1055
|
+
readonly RM_RF_SPLIT="$RM_CMD$RM_RF_TAIL_SPLIT"
|
|
1030
1056
|
|
|
1031
1057
|
# 1. Recursive forced delete (`rm -rf`) of a filesystem root, home, or top-level
|
|
1032
1058
|
# wildcard. Two gates ANDed: the statement must invoke `rm` with BOTH a
|
|
@@ -1667,8 +1693,31 @@ fi
|
|
|
1667
1693
|
if matches '(^|[^[:alnum:]_./-])find[[:space:]][^;&|]*-exec[[:space:]]+rm[[:space:]]+(-[[:alnum:]]*r[[:alnum:]]*f|-[[:alnum:]]*f[[:alnum:]]*r)([[:space:]]|$)'; then
|
|
1668
1694
|
block "find -exec rm -rf performs a recursive forced delete on unreviewed paths"
|
|
1669
1695
|
fi
|
|
1670
|
-
|
|
1671
|
-
|
|
1696
|
+
# A command that reads its operands from STDIN and runs a program on them. The
|
|
1697
|
+
# targets are therefore whatever the upstream stage emitted, which is exactly
|
|
1698
|
+
# what makes a recursive forced delete behind one unauditable.
|
|
1699
|
+
#
|
|
1700
|
+
# Three fail-opens closed here (issue #3494), all of them the same mistake —
|
|
1701
|
+
# this rule was written standalone instead of inheriting vocabulary the file
|
|
1702
|
+
# already had:
|
|
1703
|
+
#
|
|
1704
|
+
# 1. The old prefix was `(^|[^[:alnum:]_./-])xargs`, whose excluded class
|
|
1705
|
+
# contains `/`, so the character before `xargs` in `/usr/bin/xargs` was
|
|
1706
|
+
# itself disqualifying and a PATH-SPELLED invocation never matched.
|
|
1707
|
+
# `gxargs` — the GNU findutils spelling shipped by macOS package managers —
|
|
1708
|
+
# failed the same way on the alphanumeric side. RM_CMD above solved this
|
|
1709
|
+
# exact problem for `rm` by putting the boundary before the WHOLE path and
|
|
1710
|
+
# making the directory part optional; that shape is reused verbatim.
|
|
1711
|
+
# 2. `parallel` was absent. It is a drop-in `xargs` substitute reaching the
|
|
1712
|
+
# same delete on the same unaudited stdin, and guard 14 below already had
|
|
1713
|
+
# to name it in its own evidence — it was missing only here.
|
|
1714
|
+
# 3. The flag set was a short-cluster-only copy, so `rm --recursive --force`
|
|
1715
|
+
# was allowed where `rm -rf` was refused. It now uses the SAME tails as the
|
|
1716
|
+
# rm walk, which is what makes that class of divergence unrepeatable.
|
|
1717
|
+
readonly STDIN_DELETER='(^|[^[:alnum:]_./-])([[:alnum:]_./-]*/)?(g?xargs|parallel)[[:space:]]([^;&|]*[[:space:]])?rm'
|
|
1718
|
+
if matches "$STDIN_DELETER$RM_RF_TAIL_CLUSTER" \
|
|
1719
|
+
|| matches "$STDIN_DELETER$RM_RF_TAIL_SPLIT"; then
|
|
1720
|
+
block "xargs/parallel rm -rf performs a recursive forced delete on dynamic stdin input"
|
|
1672
1721
|
fi
|
|
1673
1722
|
|
|
1674
1723
|
# 12. Disk destroyers, always on (exceeds upstream, which only scans these
|
package/package.json
CHANGED
|
@@ -165,7 +165,7 @@
|
|
|
165
165
|
"zod-validation-error": "^4.0.0"
|
|
166
166
|
},
|
|
167
167
|
"name": "@codyswann/lisa",
|
|
168
|
-
"version": "4.33.
|
|
168
|
+
"version": "4.33.14",
|
|
169
169
|
"description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
|
|
170
170
|
"main": "dist/index.js",
|
|
171
171
|
"exports": {
|
|
@@ -313,7 +313,7 @@
|
|
|
313
313
|
"test": "tests"
|
|
314
314
|
},
|
|
315
315
|
"types": "./dist/index.d.ts",
|
|
316
|
-
"lisaReleaseCommit": "
|
|
317
|
-
"gitHead": "
|
|
318
|
-
"lisaReleaseTag": "v4.33.
|
|
316
|
+
"lisaReleaseCommit": "62e4b472e9736181421b518094463ce0250270f0",
|
|
317
|
+
"gitHead": "62e4b472e9736181421b518094463ce0250270f0",
|
|
318
|
+
"lisaReleaseTag": "v4.33.14"
|
|
319
319
|
}
|
|
@@ -121,6 +121,20 @@ export const PROVIDERS = [
|
|
|
121
121
|
sha256:
|
|
122
122
|
"18253757286e119d450133a87eb463bf8c1ce418ce24c834f4f250d60cba6f9e",
|
|
123
123
|
},
|
|
124
|
+
// Bitwarden publishes macOS archives for the same tag, and omitting them
|
|
125
|
+
// made the one genuinely pinned credential CLI the one a macOS operator
|
|
126
|
+
// had to install by hand — the unverified path, for the tool where an
|
|
127
|
+
// unverified download matters most.
|
|
128
|
+
"darwin-arm64": {
|
|
129
|
+
url: "https://github.com/bitwarden/sdk-sm/releases/download/bws-v2.1.0/bws-aarch64-apple-darwin-2.1.0.zip",
|
|
130
|
+
sha256:
|
|
131
|
+
"9cb1c1c6e6164d83b2e339883ba02b4cbb37188ce9a484b1ce8249443163e066",
|
|
132
|
+
},
|
|
133
|
+
"darwin-x64": {
|
|
134
|
+
url: "https://github.com/bitwarden/sdk-sm/releases/download/bws-v2.1.0/bws-x86_64-apple-darwin-2.1.0.zip",
|
|
135
|
+
sha256:
|
|
136
|
+
"6f626b3971368902af1b9847c02791a1b4666969d7561e2047681cded7997537",
|
|
137
|
+
},
|
|
124
138
|
},
|
|
125
139
|
},
|
|
126
140
|
{
|
|
@@ -197,11 +211,26 @@ export const TOOLS = [
|
|
|
197
211
|
"12311e320d4cfdb54d7fa2d58cd1e3a2ccb4c12e1c3abb32b0a2e48bd0f991bf",
|
|
198
212
|
binary: "gh_2.83.0_linux_arm64/bin/gh",
|
|
199
213
|
},
|
|
214
|
+
// `install` is per-block for gh and only for gh: the vendor ships a
|
|
215
|
+
// .tar.gz for Linux and a .zip for macOS, so the entry-level `release-tar`
|
|
216
|
+
// is wrong for exactly these two. Without the override the resolved entry
|
|
217
|
+
// keeps `release-tar` and the installer runs `tar -xzf` over a ZIP, which
|
|
218
|
+
// fails during extraction — a macOS pin that resolves and cannot install.
|
|
200
219
|
"darwin-arm64": {
|
|
201
220
|
url: "https://github.com/cli/cli/releases/download/v2.83.0/gh_2.83.0_macOS_arm64.zip",
|
|
202
221
|
sha256:
|
|
203
222
|
"fecba907bc361d5e33620dbf1145f11432c39fb2b388a839463cfbb89a84820b",
|
|
204
223
|
binary: "gh_2.83.0_macOS_arm64/bin/gh",
|
|
224
|
+
install: "release-zip",
|
|
225
|
+
},
|
|
226
|
+
// Intel Macs are still in service, and an Apple Silicon-only macOS block
|
|
227
|
+
// reads as "macOS is covered" while leaving half of it uncovered.
|
|
228
|
+
"darwin-x64": {
|
|
229
|
+
url: "https://github.com/cli/cli/releases/download/v2.83.0/gh_2.83.0_macOS_amd64.zip",
|
|
230
|
+
sha256:
|
|
231
|
+
"0c0de650752bb92d7283e386cafd03d9ac5f47028c648c4ab821ef08a75c0716",
|
|
232
|
+
binary: "gh_2.83.0_macOS_amd64/bin/gh",
|
|
233
|
+
install: "release-zip",
|
|
205
234
|
},
|
|
206
235
|
},
|
|
207
236
|
},
|
|
@@ -17,13 +17,14 @@ This flow must return exactly one canonical `(tracker_provider, work_item_ref)`
|
|
|
17
17
|
- an explicit reference matching the configured tracker (`KEY-123`, `org/repo#123` or issue URL, Linear team identifier);
|
|
18
18
|
- an existing file path containing a specification (read the entire file, without offset/limit);
|
|
19
19
|
- plain-text work description.
|
|
20
|
-
3.
|
|
20
|
+
3. Detect the caller's readiness declaration, which is orthogonal to the classification above. Absent any declaration this is the default build-ready path. A caller deliberately holding the work for a pending human product call passes `human_gate: "<why a human must judge this first>"`; the two declarations are mutually exclusive and a `human_gate` present but empty is a blocking error, because an unexplained hold is indistinguishable from an accident.
|
|
21
|
+
4. Preserve the full resolved specification for the caller. Do not treat a ticket-like token for a different provider/project as plain text; report the mismatch.
|
|
21
22
|
|
|
22
23
|
## Phase 2 — Resolve one live work item
|
|
23
24
|
|
|
24
25
|
### Explicit reference
|
|
25
26
|
|
|
26
|
-
Invoke `lisa-tracker-read <ref>` and require a live result from the configured project. Reject nonexistent, inaccessible, closed/resolved/terminal, wrong-project, wrong-repository, or container items. This live read is mandatory even if caller context already includes ticket text.
|
|
27
|
+
Invoke `lisa-tracker-read <ref>` and require a live result from the configured project. Reject nonexistent, inaccessible, closed/resolved/terminal, wrong-project, wrong-repository, or container items. Read the returned body for the hold marker: an item carrying `[lisa-human-gate]` is held and Phase 3 refuses to claim it, whatever role or labels it carries. This live read is mandatory even if caller context already includes ticket text.
|
|
27
28
|
|
|
28
29
|
### File or plain text
|
|
29
30
|
|
|
@@ -34,15 +35,21 @@ Search conservatively before creating:
|
|
|
34
35
|
- GitHub: `gh issue list --repo <org>/<repo> --state open --search "<keywords> in:title,body"`.
|
|
35
36
|
- Jira: `lisa-atlassian-access operation: search-issues` with project-scoped JQL.
|
|
36
37
|
- Linear: `lisa-linear-access operation: list-issues` scoped to the configured team/workspace.
|
|
37
|
-
3. Treat search results as candidates, never proof. Live-read each plausible candidate through `lisa-tracker-read`, and discard terminal, container, blocked, cross-repo, and materially different outcomes.
|
|
38
|
+
3. Treat search results as candidates, never proof. Live-read each plausible candidate through `lisa-tracker-read`, and discard terminal, container, blocked, held-by-gate, cross-repo, and materially different outcomes. A held candidate is never reused: attaching new work to an item a human is holding is how the hold gets spent.
|
|
38
39
|
4. Reuse only when **exactly one** live leaf is a high-confidence semantic match for the same requested outcome and repository. A shared keyword or similar title is not enough. Record the search queries, candidates, and rejection reasons in the returned resolution evidence.
|
|
39
|
-
5. If there is no unique high-confidence match (zero or ambiguous candidates), create **exactly one** item by invoking `lisa-tracker-write` once. Synthesize one complete single-repository leaf (`Bug`, `Task`, `Sub-task`, or `Improvement`, never Epic/container) with the writer's required three-audience body, Gherkin acceptance criteria, repository, target environment, relationship search, and executable Validation Journey. Pass `build_ready: true
|
|
40
|
+
5. If there is no unique high-confidence match (zero or ambiguous candidates), create **exactly one** item by invoking `lisa-tracker-write` once. Synthesize one complete single-repository leaf (`Bug`, `Task`, `Sub-task`, or `Improvement`, never Epic/container) with the writer's required three-audience body, Gherkin acceptance criteria, repository, target environment, relationship search, and executable Validation Journey. Pass `build_ready: true` unless the caller declared a `human_gate`, in which case pass that `human_gate: "<why>"` reason instead of `build_ready: true` and never alongside it, so the writer leaves the leaf in the tracker's default backlog role — out of the lane build-intake claims from — and stamps the hold on the body as a `[lisa-human-gate]` marker. Discarding a declared gate and filing build-ready anyway is the one outcome this step must never produce. Do not create placeholder/thin tickets, do not create a hierarchy, and do not retry creation by making another item if validation fails; repair the proposed spec and retry the same writer operation only if the vendor contract supports idempotent reuse.
|
|
40
41
|
6. Live-read the writer's canonical returned reference with `lisa-tracker-read`. A create response without a verified live leaf is failure.
|
|
41
42
|
|
|
42
43
|
This is intentionally conservative: ambiguity creates one explicit work item instead of silently attaching work to the wrong existing item. Across the entire invocation, at most one new work item may be created.
|
|
43
44
|
|
|
44
45
|
## Phase 3 — Claim and bind
|
|
45
46
|
|
|
47
|
+
The work item is **held** when either of two independent signals is present: the caller declared a `human_gate`, or the resolved item's body contains the literal `[lisa-human-gate]` marker. Test the marker as a plain substring and never key the parse on a `reason=` value — markers are written both with and without one, and a parser that requires the key misses the keyless ones while appearing to work.
|
|
48
|
+
|
|
49
|
+
A held item stops the flow here, whatever the resolution outcome and whatever roles or labels the item carries. The marker is the authority: an item can be held while sitting in the build-ready role with no blocked or human-needed label, because a human stamping a hold on live work is the most likely way a gate is ever applied, and the vendor claim contracts reject a closed item or an active blocker but not a marked one. Do not invoke `lisa-tracker-claim`, do not write the worktree binding, and do not begin durable project work. Claiming a held leaf binds a live lane to it and makes it look already-attended, which suppresses exactly the human attention it was filed to attract. Return the structured result below with `claim_outcome: held-by-gate` and `binding_outcome: skipped-human-gate`, name which signal held it in `held_by` and carry the reason verbatim in `gate_reason` alongside the canonical reference, then stop. A marker written without a `reason=` key yields `gate_reason: null` — a missing reason narrows what the result can say, never whether the item is held.
|
|
50
|
+
|
|
51
|
+
Otherwise:
|
|
52
|
+
|
|
46
53
|
1. Invoke `lisa-tracker-claim <canonical-ref>`. Require its post-read verified `claim_outcome: claimed|reused`; no binding may be written after a failed/unverified claim.
|
|
47
54
|
2. Persist only the canonical reference in worktree-local machine state:
|
|
48
55
|
|
|
@@ -58,8 +65,11 @@ This is intentionally conservative: ambiguity creates one explicit work item ins
|
|
|
58
65
|
tracker_provider: jira|github|linear
|
|
59
66
|
work_item_ref: <canonical-ref>
|
|
60
67
|
resolution_outcome: explicit|reused|created
|
|
61
|
-
|
|
62
|
-
|
|
68
|
+
readiness_declaration: build-ready|human-gate
|
|
69
|
+
held_by: none|caller-declaration|item-marker
|
|
70
|
+
gate_reason: <why a human must judge this first>|null
|
|
71
|
+
claim_outcome: claimed|reused|held-by-gate
|
|
72
|
+
binding_outcome: verified|skipped-human-gate
|
|
63
73
|
```
|
|
64
74
|
|
|
65
75
|
## Lifecycle
|
|
@@ -68,4 +78,5 @@ This is intentionally conservative: ambiguity creates one explicit work item ins
|
|
|
68
78
|
- Branch setup may call `node scripts/lisa-work-item.mjs attach-branch` after the feature branch exists.
|
|
69
79
|
- Keep the binding across ordinary interruptions and blocked outcomes so resumed work remains attributable.
|
|
70
80
|
- Clear it only after true terminal completion — merged, deployed/verified where required, tracker evidence/backlink complete, and the work item terminal — by running `node scripts/lisa-work-item.mjs clear` and verifying no current binding remains.
|
|
81
|
+
- A held filing writes no binding at all, so there is nothing to clear. The gate is released by a human, not by this skill: a human removes the `[lisa-human-gate]` marker and flips the leaf into the build-ready role, and a later invocation then resolves it on the ordinary path.
|
|
71
82
|
- A tracker outage, invalid item, failed claim, or failed binding blocks durable work. Never continue untracked and never ask a Git hook to create the item.
|
|
@@ -269,8 +269,12 @@ FILE IT THE SANCTIONED WAY — one of these two, always explicit:
|
|
|
269
269
|
bar — reproduction, observed-versus-expected, Gherkin acceptance criteria.
|
|
270
270
|
|
|
271
271
|
2. A human product call is genuinely pending. Route the same way but pass
|
|
272
|
-
\`human_gate: "<why a human must judge this first>"
|
|
273
|
-
|
|
272
|
+
\`human_gate: "<why a human must judge this first>"\` in place of
|
|
273
|
+
\`build_ready: true\` — the two are mutually exclusive. The leaf is filed
|
|
274
|
+
into the default backlog role, outside the lane build-intake claims from,
|
|
275
|
+
and is deliberately NOT claimed, so it keeps attracting the human attention
|
|
276
|
+
it was filed for. The hold is stamped so it is auditable rather than
|
|
277
|
+
indistinguishable from an accident:
|
|
274
278
|
|
|
275
279
|
Held for a human product call: <reason>.
|
|
276
280
|
<!-- [lisa-human-gate] reason=<short-slug> -->
|
|
@@ -1019,14 +1019,40 @@ readonly GIT_CMD='(^|[^[:alnum:]_-])git[[:space:]]+'"$GIT_GLOBAL_OPTS"
|
|
|
1019
1019
|
# match. The preceding-char class still excludes `.`, `/`, and `-` to keep
|
|
1020
1020
|
# `--rm`-style flags and `foo.rm` names out.
|
|
1021
1021
|
readonly RM_CMD='(^|[^[:alnum:]_./-])([[:alnum:]_./-]*/)?rm'
|
|
1022
|
+
# Where a recursive-forced flag cluster is allowed to END.
|
|
1023
|
+
#
|
|
1024
|
+
# It used to be `([[:space:]]|$)` — whitespace or end of segment — and a closing
|
|
1025
|
+
# parenthesis is neither, so a SUBSHELL-wrapped pipeline fell out of every guard
|
|
1026
|
+
# built on these two patterns while its brace-wrapped twin was caught: `{ find
|
|
1027
|
+
# .git -type d | parallel rm -rf; }` matched because the `;` supplied the
|
|
1028
|
+
# boundary, and `(find .git -type d | parallel rm -rf)` did not (issue #3494).
|
|
1029
|
+
#
|
|
1030
|
+
# So the two STATEMENT-GROUPING terminators join whitespace here, and nothing
|
|
1031
|
+
# else. The general spelling — "any character that cannot be part of a flag",
|
|
1032
|
+
# `([^[:alnum:]-]|$)` — is the more principled statement of the intent, and is
|
|
1033
|
+
# deliberately NOT used: these patterns feed the rm TARGET WALK, the guard with
|
|
1034
|
+
# this file's longest false-positive history, and it would newly match ordinary
|
|
1035
|
+
# prose punctuation (`echo "rm -rf, then …"` on the comma). Paren and semicolon
|
|
1036
|
+
# buy the brace/paren parity the defect is about and buy nothing else.
|
|
1037
|
+
#
|
|
1038
|
+
# The cluster must still END at the boundary, so `-rfoo` is not read as `-rf`.
|
|
1039
|
+
readonly RM_RF_END='([[:space:];)]|$)'
|
|
1022
1040
|
# rm invoked with BOTH a recursive and a force flag — clustered (-rf/-fr, any
|
|
1023
1041
|
# extra letters) or split, in either order. The split gate pairs ANY recursive
|
|
1024
1042
|
# form (short cluster containing r, or --recursive) with ANY force form (short
|
|
1025
1043
|
# cluster containing f, or --force), so mixed spellings like `rm -r --force /`
|
|
1026
1044
|
# and `rm --recursive -f /` cannot slip between the short/short and long/long
|
|
1027
1045
|
# alternations (PR #1976 review).
|
|
1028
|
-
|
|
1029
|
-
|
|
1046
|
+
#
|
|
1047
|
+
# The FLAG TAILS are named separately from the `rm`-anchored patterns because
|
|
1048
|
+
# the find/xargs family below needs the identical flag vocabulary against a
|
|
1049
|
+
# different verb anchor. Before #3494 it carried its own short-cluster-only copy
|
|
1050
|
+
# and therefore missed `rm --recursive --force` — a divergence that could only
|
|
1051
|
+
# happen because the spelling set was written twice.
|
|
1052
|
+
readonly RM_RF_TAIL_CLUSTER='([[:space:]]+-[[:alnum:]-]+)*[[:space:]]+(-[[:alnum:]]*r[[:alnum:]]*f|-[[:alnum:]]*f[[:alnum:]]*r)'"$RM_RF_END"
|
|
1053
|
+
readonly RM_RF_TAIL_SPLIT='(([[:space:]][^;&|]*)?[[:space:]](-[[:alnum:]]*r[[:alnum:]]*|--recursive)([[:space:]][^;&|]*)?[[:space:]](-[[:alnum:]]*f[[:alnum:]]*|--force)'"$RM_RF_END"'|([[:space:]][^;&|]*)?[[:space:]](-[[:alnum:]]*f[[:alnum:]]*|--force)([[:space:]][^;&|]*)?[[:space:]](-[[:alnum:]]*r[[:alnum:]]*|--recursive)'"$RM_RF_END"')'
|
|
1054
|
+
readonly RM_RF_CLUSTER="$RM_CMD$RM_RF_TAIL_CLUSTER"
|
|
1055
|
+
readonly RM_RF_SPLIT="$RM_CMD$RM_RF_TAIL_SPLIT"
|
|
1030
1056
|
|
|
1031
1057
|
# 1. Recursive forced delete (`rm -rf`) of a filesystem root, home, or top-level
|
|
1032
1058
|
# wildcard. Two gates ANDed: the statement must invoke `rm` with BOTH a
|
|
@@ -1667,8 +1693,31 @@ fi
|
|
|
1667
1693
|
if matches '(^|[^[:alnum:]_./-])find[[:space:]][^;&|]*-exec[[:space:]]+rm[[:space:]]+(-[[:alnum:]]*r[[:alnum:]]*f|-[[:alnum:]]*f[[:alnum:]]*r)([[:space:]]|$)'; then
|
|
1668
1694
|
block "find -exec rm -rf performs a recursive forced delete on unreviewed paths"
|
|
1669
1695
|
fi
|
|
1670
|
-
|
|
1671
|
-
|
|
1696
|
+
# A command that reads its operands from STDIN and runs a program on them. The
|
|
1697
|
+
# targets are therefore whatever the upstream stage emitted, which is exactly
|
|
1698
|
+
# what makes a recursive forced delete behind one unauditable.
|
|
1699
|
+
#
|
|
1700
|
+
# Three fail-opens closed here (issue #3494), all of them the same mistake —
|
|
1701
|
+
# this rule was written standalone instead of inheriting vocabulary the file
|
|
1702
|
+
# already had:
|
|
1703
|
+
#
|
|
1704
|
+
# 1. The old prefix was `(^|[^[:alnum:]_./-])xargs`, whose excluded class
|
|
1705
|
+
# contains `/`, so the character before `xargs` in `/usr/bin/xargs` was
|
|
1706
|
+
# itself disqualifying and a PATH-SPELLED invocation never matched.
|
|
1707
|
+
# `gxargs` — the GNU findutils spelling shipped by macOS package managers —
|
|
1708
|
+
# failed the same way on the alphanumeric side. RM_CMD above solved this
|
|
1709
|
+
# exact problem for `rm` by putting the boundary before the WHOLE path and
|
|
1710
|
+
# making the directory part optional; that shape is reused verbatim.
|
|
1711
|
+
# 2. `parallel` was absent. It is a drop-in `xargs` substitute reaching the
|
|
1712
|
+
# same delete on the same unaudited stdin, and guard 14 below already had
|
|
1713
|
+
# to name it in its own evidence — it was missing only here.
|
|
1714
|
+
# 3. The flag set was a short-cluster-only copy, so `rm --recursive --force`
|
|
1715
|
+
# was allowed where `rm -rf` was refused. It now uses the SAME tails as the
|
|
1716
|
+
# rm walk, which is what makes that class of divergence unrepeatable.
|
|
1717
|
+
readonly STDIN_DELETER='(^|[^[:alnum:]_./-])([[:alnum:]_./-]*/)?(g?xargs|parallel)[[:space:]]([^;&|]*[[:space:]])?rm'
|
|
1718
|
+
if matches "$STDIN_DELETER$RM_RF_TAIL_CLUSTER" \
|
|
1719
|
+
|| matches "$STDIN_DELETER$RM_RF_TAIL_SPLIT"; then
|
|
1720
|
+
block "xargs/parallel rm -rf performs a recursive forced delete on dynamic stdin input"
|
|
1672
1721
|
fi
|
|
1673
1722
|
|
|
1674
1723
|
# 12. Disk destroyers, always on (exceeds upstream, which only scans these
|
|
@@ -121,6 +121,20 @@ export const PROVIDERS = [
|
|
|
121
121
|
sha256:
|
|
122
122
|
"18253757286e119d450133a87eb463bf8c1ce418ce24c834f4f250d60cba6f9e",
|
|
123
123
|
},
|
|
124
|
+
// Bitwarden publishes macOS archives for the same tag, and omitting them
|
|
125
|
+
// made the one genuinely pinned credential CLI the one a macOS operator
|
|
126
|
+
// had to install by hand — the unverified path, for the tool where an
|
|
127
|
+
// unverified download matters most.
|
|
128
|
+
"darwin-arm64": {
|
|
129
|
+
url: "https://github.com/bitwarden/sdk-sm/releases/download/bws-v2.1.0/bws-aarch64-apple-darwin-2.1.0.zip",
|
|
130
|
+
sha256:
|
|
131
|
+
"9cb1c1c6e6164d83b2e339883ba02b4cbb37188ce9a484b1ce8249443163e066",
|
|
132
|
+
},
|
|
133
|
+
"darwin-x64": {
|
|
134
|
+
url: "https://github.com/bitwarden/sdk-sm/releases/download/bws-v2.1.0/bws-x86_64-apple-darwin-2.1.0.zip",
|
|
135
|
+
sha256:
|
|
136
|
+
"6f626b3971368902af1b9847c02791a1b4666969d7561e2047681cded7997537",
|
|
137
|
+
},
|
|
124
138
|
},
|
|
125
139
|
},
|
|
126
140
|
{
|
|
@@ -197,11 +211,26 @@ export const TOOLS = [
|
|
|
197
211
|
"12311e320d4cfdb54d7fa2d58cd1e3a2ccb4c12e1c3abb32b0a2e48bd0f991bf",
|
|
198
212
|
binary: "gh_2.83.0_linux_arm64/bin/gh",
|
|
199
213
|
},
|
|
214
|
+
// `install` is per-block for gh and only for gh: the vendor ships a
|
|
215
|
+
// .tar.gz for Linux and a .zip for macOS, so the entry-level `release-tar`
|
|
216
|
+
// is wrong for exactly these two. Without the override the resolved entry
|
|
217
|
+
// keeps `release-tar` and the installer runs `tar -xzf` over a ZIP, which
|
|
218
|
+
// fails during extraction — a macOS pin that resolves and cannot install.
|
|
200
219
|
"darwin-arm64": {
|
|
201
220
|
url: "https://github.com/cli/cli/releases/download/v2.83.0/gh_2.83.0_macOS_arm64.zip",
|
|
202
221
|
sha256:
|
|
203
222
|
"fecba907bc361d5e33620dbf1145f11432c39fb2b388a839463cfbb89a84820b",
|
|
204
223
|
binary: "gh_2.83.0_macOS_arm64/bin/gh",
|
|
224
|
+
install: "release-zip",
|
|
225
|
+
},
|
|
226
|
+
// Intel Macs are still in service, and an Apple Silicon-only macOS block
|
|
227
|
+
// reads as "macOS is covered" while leaving half of it uncovered.
|
|
228
|
+
"darwin-x64": {
|
|
229
|
+
url: "https://github.com/cli/cli/releases/download/v2.83.0/gh_2.83.0_macOS_amd64.zip",
|
|
230
|
+
sha256:
|
|
231
|
+
"0c0de650752bb92d7283e386cafd03d9ac5f47028c648c4ab821ef08a75c0716",
|
|
232
|
+
binary: "gh_2.83.0_macOS_amd64/bin/gh",
|
|
233
|
+
install: "release-zip",
|
|
205
234
|
},
|
|
206
235
|
},
|
|
207
236
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: lisa-track
|
|
3
|
-
description: "Resolves exactly one live configured-tracker leaf for durable project work, claims it idempotently, and persists its canonical reference in worktree-local state. Accepts an existing Jira/GitHub/Linear ref, a spec file, or plain text. Use directly to mention/create the related ticket, and as lisa-implement's mandatory input gate."
|
|
3
|
+
description: "Resolves exactly one live configured-tracker leaf for durable project work, claims it idempotently, and persists its canonical reference in worktree-local state. Accepts an existing Jira/GitHub/Linear ref, a spec file, or plain text. Also accepts human_gate: <why a human must judge this first>, which files the leaf held — outside the build-ready lane, never claimed — instead of build-ready. Use directly to mention/create the related ticket, and as lisa-implement's mandatory input gate."
|
|
4
4
|
allowed-tools: ["Skill", "Bash", "Read"]
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -17,13 +17,14 @@ This flow must return exactly one canonical `(tracker_provider, work_item_ref)`
|
|
|
17
17
|
- an explicit reference matching the configured tracker (`KEY-123`, `org/repo#123` or issue URL, Linear team identifier);
|
|
18
18
|
- an existing file path containing a specification (read the entire file, without offset/limit);
|
|
19
19
|
- plain-text work description.
|
|
20
|
-
3.
|
|
20
|
+
3. Detect the caller's readiness declaration, which is orthogonal to the classification above. Absent any declaration this is the default build-ready path. A caller deliberately holding the work for a pending human product call passes `human_gate: "<why a human must judge this first>"`; the two declarations are mutually exclusive and a `human_gate` present but empty is a blocking error, because an unexplained hold is indistinguishable from an accident.
|
|
21
|
+
4. Preserve the full resolved specification for the caller. Do not treat a ticket-like token for a different provider/project as plain text; report the mismatch.
|
|
21
22
|
|
|
22
23
|
## Phase 2 — Resolve one live work item
|
|
23
24
|
|
|
24
25
|
### Explicit reference
|
|
25
26
|
|
|
26
|
-
Invoke `lisa-tracker-read <ref>` and require a live result from the configured project. Reject nonexistent, inaccessible, closed/resolved/terminal, wrong-project, wrong-repository, or container items. This live read is mandatory even if caller context already includes ticket text.
|
|
27
|
+
Invoke `lisa-tracker-read <ref>` and require a live result from the configured project. Reject nonexistent, inaccessible, closed/resolved/terminal, wrong-project, wrong-repository, or container items. Read the returned body for the hold marker: an item carrying `[lisa-human-gate]` is held and Phase 3 refuses to claim it, whatever role or labels it carries. This live read is mandatory even if caller context already includes ticket text.
|
|
27
28
|
|
|
28
29
|
### File or plain text
|
|
29
30
|
|
|
@@ -34,15 +35,21 @@ Search conservatively before creating:
|
|
|
34
35
|
- GitHub: `gh issue list --repo <org>/<repo> --state open --search "<keywords> in:title,body"`.
|
|
35
36
|
- Jira: `lisa-atlassian-access operation: search-issues` with project-scoped JQL.
|
|
36
37
|
- Linear: `lisa-linear-access operation: list-issues` scoped to the configured team/workspace.
|
|
37
|
-
3. Treat search results as candidates, never proof. Live-read each plausible candidate through `lisa-tracker-read`, and discard terminal, container, blocked, cross-repo, and materially different outcomes.
|
|
38
|
+
3. Treat search results as candidates, never proof. Live-read each plausible candidate through `lisa-tracker-read`, and discard terminal, container, blocked, held-by-gate, cross-repo, and materially different outcomes. A held candidate is never reused: attaching new work to an item a human is holding is how the hold gets spent.
|
|
38
39
|
4. Reuse only when **exactly one** live leaf is a high-confidence semantic match for the same requested outcome and repository. A shared keyword or similar title is not enough. Record the search queries, candidates, and rejection reasons in the returned resolution evidence.
|
|
39
|
-
5. If there is no unique high-confidence match (zero or ambiguous candidates), create **exactly one** item by invoking `lisa-tracker-write` once. Synthesize one complete single-repository leaf (`Bug`, `Task`, `Sub-task`, or `Improvement`, never Epic/container) with the writer's required three-audience body, Gherkin acceptance criteria, repository, target environment, relationship search, and executable Validation Journey. Pass `build_ready: true
|
|
40
|
+
5. If there is no unique high-confidence match (zero or ambiguous candidates), create **exactly one** item by invoking `lisa-tracker-write` once. Synthesize one complete single-repository leaf (`Bug`, `Task`, `Sub-task`, or `Improvement`, never Epic/container) with the writer's required three-audience body, Gherkin acceptance criteria, repository, target environment, relationship search, and executable Validation Journey. Pass `build_ready: true` unless the caller declared a `human_gate`, in which case pass that `human_gate: "<why>"` reason instead of `build_ready: true` and never alongside it, so the writer leaves the leaf in the tracker's default backlog role — out of the lane build-intake claims from — and stamps the hold on the body as a `[lisa-human-gate]` marker. Discarding a declared gate and filing build-ready anyway is the one outcome this step must never produce. Do not create placeholder/thin tickets, do not create a hierarchy, and do not retry creation by making another item if validation fails; repair the proposed spec and retry the same writer operation only if the vendor contract supports idempotent reuse.
|
|
40
41
|
6. Live-read the writer's canonical returned reference with `lisa-tracker-read`. A create response without a verified live leaf is failure.
|
|
41
42
|
|
|
42
43
|
This is intentionally conservative: ambiguity creates one explicit work item instead of silently attaching work to the wrong existing item. Across the entire invocation, at most one new work item may be created.
|
|
43
44
|
|
|
44
45
|
## Phase 3 — Claim and bind
|
|
45
46
|
|
|
47
|
+
The work item is **held** when either of two independent signals is present: the caller declared a `human_gate`, or the resolved item's body contains the literal `[lisa-human-gate]` marker. Test the marker as a plain substring and never key the parse on a `reason=` value — markers are written both with and without one, and a parser that requires the key misses the keyless ones while appearing to work.
|
|
48
|
+
|
|
49
|
+
A held item stops the flow here, whatever the resolution outcome and whatever roles or labels the item carries. The marker is the authority: an item can be held while sitting in the build-ready role with no blocked or human-needed label, because a human stamping a hold on live work is the most likely way a gate is ever applied, and the vendor claim contracts reject a closed item or an active blocker but not a marked one. Do not invoke `lisa-tracker-claim`, do not write the worktree binding, and do not begin durable project work. Claiming a held leaf binds a live lane to it and makes it look already-attended, which suppresses exactly the human attention it was filed to attract. Return the structured result below with `claim_outcome: held-by-gate` and `binding_outcome: skipped-human-gate`, name which signal held it in `held_by` and carry the reason verbatim in `gate_reason` alongside the canonical reference, then stop. A marker written without a `reason=` key yields `gate_reason: null` — a missing reason narrows what the result can say, never whether the item is held.
|
|
50
|
+
|
|
51
|
+
Otherwise:
|
|
52
|
+
|
|
46
53
|
1. Invoke `lisa-tracker-claim <canonical-ref>`. Require its post-read verified `claim_outcome: claimed|reused`; no binding may be written after a failed/unverified claim.
|
|
47
54
|
2. Persist only the canonical reference in worktree-local machine state:
|
|
48
55
|
|
|
@@ -58,8 +65,11 @@ This is intentionally conservative: ambiguity creates one explicit work item ins
|
|
|
58
65
|
tracker_provider: jira|github|linear
|
|
59
66
|
work_item_ref: <canonical-ref>
|
|
60
67
|
resolution_outcome: explicit|reused|created
|
|
61
|
-
|
|
62
|
-
|
|
68
|
+
readiness_declaration: build-ready|human-gate
|
|
69
|
+
held_by: none|caller-declaration|item-marker
|
|
70
|
+
gate_reason: <why a human must judge this first>|null
|
|
71
|
+
claim_outcome: claimed|reused|held-by-gate
|
|
72
|
+
binding_outcome: verified|skipped-human-gate
|
|
63
73
|
```
|
|
64
74
|
|
|
65
75
|
## Lifecycle
|
|
@@ -68,4 +78,5 @@ This is intentionally conservative: ambiguity creates one explicit work item ins
|
|
|
68
78
|
- Branch setup may call `node scripts/lisa-work-item.mjs attach-branch` after the feature branch exists.
|
|
69
79
|
- Keep the binding across ordinary interruptions and blocked outcomes so resumed work remains attributable.
|
|
70
80
|
- Clear it only after true terminal completion — merged, deployed/verified where required, tracker evidence/backlink complete, and the work item terminal — by running `node scripts/lisa-work-item.mjs clear` and verifying no current binding remains.
|
|
81
|
+
- A held filing writes no binding at all, so there is nothing to clear. The gate is released by a human, not by this skill: a human removes the `[lisa-human-gate]` marker and flips the leaf into the build-ready role, and a later invocation then resolves it on the ordinary path.
|
|
71
82
|
- A tracker outage, invalid item, failed claim, or failed binding blocks durable work. Never continue untracked and never ask a Git hook to create the item.
|
|
@@ -269,8 +269,12 @@ FILE IT THE SANCTIONED WAY — one of these two, always explicit:
|
|
|
269
269
|
bar — reproduction, observed-versus-expected, Gherkin acceptance criteria.
|
|
270
270
|
|
|
271
271
|
2. A human product call is genuinely pending. Route the same way but pass
|
|
272
|
-
\`human_gate: "<why a human must judge this first>"
|
|
273
|
-
|
|
272
|
+
\`human_gate: "<why a human must judge this first>"\` in place of
|
|
273
|
+
\`build_ready: true\` — the two are mutually exclusive. The leaf is filed
|
|
274
|
+
into the default backlog role, outside the lane build-intake claims from,
|
|
275
|
+
and is deliberately NOT claimed, so it keeps attracting the human attention
|
|
276
|
+
it was filed for. The hold is stamped so it is auditable rather than
|
|
277
|
+
indistinguishable from an accident:
|
|
274
278
|
|
|
275
279
|
Held for a human product call: <reason>.
|
|
276
280
|
<!-- [lisa-human-gate] reason=<short-slug> -->
|
|
@@ -1019,14 +1019,40 @@ readonly GIT_CMD='(^|[^[:alnum:]_-])git[[:space:]]+'"$GIT_GLOBAL_OPTS"
|
|
|
1019
1019
|
# match. The preceding-char class still excludes `.`, `/`, and `-` to keep
|
|
1020
1020
|
# `--rm`-style flags and `foo.rm` names out.
|
|
1021
1021
|
readonly RM_CMD='(^|[^[:alnum:]_./-])([[:alnum:]_./-]*/)?rm'
|
|
1022
|
+
# Where a recursive-forced flag cluster is allowed to END.
|
|
1023
|
+
#
|
|
1024
|
+
# It used to be `([[:space:]]|$)` — whitespace or end of segment — and a closing
|
|
1025
|
+
# parenthesis is neither, so a SUBSHELL-wrapped pipeline fell out of every guard
|
|
1026
|
+
# built on these two patterns while its brace-wrapped twin was caught: `{ find
|
|
1027
|
+
# .git -type d | parallel rm -rf; }` matched because the `;` supplied the
|
|
1028
|
+
# boundary, and `(find .git -type d | parallel rm -rf)` did not (issue #3494).
|
|
1029
|
+
#
|
|
1030
|
+
# So the two STATEMENT-GROUPING terminators join whitespace here, and nothing
|
|
1031
|
+
# else. The general spelling — "any character that cannot be part of a flag",
|
|
1032
|
+
# `([^[:alnum:]-]|$)` — is the more principled statement of the intent, and is
|
|
1033
|
+
# deliberately NOT used: these patterns feed the rm TARGET WALK, the guard with
|
|
1034
|
+
# this file's longest false-positive history, and it would newly match ordinary
|
|
1035
|
+
# prose punctuation (`echo "rm -rf, then …"` on the comma). Paren and semicolon
|
|
1036
|
+
# buy the brace/paren parity the defect is about and buy nothing else.
|
|
1037
|
+
#
|
|
1038
|
+
# The cluster must still END at the boundary, so `-rfoo` is not read as `-rf`.
|
|
1039
|
+
readonly RM_RF_END='([[:space:];)]|$)'
|
|
1022
1040
|
# rm invoked with BOTH a recursive and a force flag — clustered (-rf/-fr, any
|
|
1023
1041
|
# extra letters) or split, in either order. The split gate pairs ANY recursive
|
|
1024
1042
|
# form (short cluster containing r, or --recursive) with ANY force form (short
|
|
1025
1043
|
# cluster containing f, or --force), so mixed spellings like `rm -r --force /`
|
|
1026
1044
|
# and `rm --recursive -f /` cannot slip between the short/short and long/long
|
|
1027
1045
|
# alternations (PR #1976 review).
|
|
1028
|
-
|
|
1029
|
-
|
|
1046
|
+
#
|
|
1047
|
+
# The FLAG TAILS are named separately from the `rm`-anchored patterns because
|
|
1048
|
+
# the find/xargs family below needs the identical flag vocabulary against a
|
|
1049
|
+
# different verb anchor. Before #3494 it carried its own short-cluster-only copy
|
|
1050
|
+
# and therefore missed `rm --recursive --force` — a divergence that could only
|
|
1051
|
+
# happen because the spelling set was written twice.
|
|
1052
|
+
readonly RM_RF_TAIL_CLUSTER='([[:space:]]+-[[:alnum:]-]+)*[[:space:]]+(-[[:alnum:]]*r[[:alnum:]]*f|-[[:alnum:]]*f[[:alnum:]]*r)'"$RM_RF_END"
|
|
1053
|
+
readonly RM_RF_TAIL_SPLIT='(([[:space:]][^;&|]*)?[[:space:]](-[[:alnum:]]*r[[:alnum:]]*|--recursive)([[:space:]][^;&|]*)?[[:space:]](-[[:alnum:]]*f[[:alnum:]]*|--force)'"$RM_RF_END"'|([[:space:]][^;&|]*)?[[:space:]](-[[:alnum:]]*f[[:alnum:]]*|--force)([[:space:]][^;&|]*)?[[:space:]](-[[:alnum:]]*r[[:alnum:]]*|--recursive)'"$RM_RF_END"')'
|
|
1054
|
+
readonly RM_RF_CLUSTER="$RM_CMD$RM_RF_TAIL_CLUSTER"
|
|
1055
|
+
readonly RM_RF_SPLIT="$RM_CMD$RM_RF_TAIL_SPLIT"
|
|
1030
1056
|
|
|
1031
1057
|
# 1. Recursive forced delete (`rm -rf`) of a filesystem root, home, or top-level
|
|
1032
1058
|
# wildcard. Two gates ANDed: the statement must invoke `rm` with BOTH a
|
|
@@ -1667,8 +1693,31 @@ fi
|
|
|
1667
1693
|
if matches '(^|[^[:alnum:]_./-])find[[:space:]][^;&|]*-exec[[:space:]]+rm[[:space:]]+(-[[:alnum:]]*r[[:alnum:]]*f|-[[:alnum:]]*f[[:alnum:]]*r)([[:space:]]|$)'; then
|
|
1668
1694
|
block "find -exec rm -rf performs a recursive forced delete on unreviewed paths"
|
|
1669
1695
|
fi
|
|
1670
|
-
|
|
1671
|
-
|
|
1696
|
+
# A command that reads its operands from STDIN and runs a program on them. The
|
|
1697
|
+
# targets are therefore whatever the upstream stage emitted, which is exactly
|
|
1698
|
+
# what makes a recursive forced delete behind one unauditable.
|
|
1699
|
+
#
|
|
1700
|
+
# Three fail-opens closed here (issue #3494), all of them the same mistake —
|
|
1701
|
+
# this rule was written standalone instead of inheriting vocabulary the file
|
|
1702
|
+
# already had:
|
|
1703
|
+
#
|
|
1704
|
+
# 1. The old prefix was `(^|[^[:alnum:]_./-])xargs`, whose excluded class
|
|
1705
|
+
# contains `/`, so the character before `xargs` in `/usr/bin/xargs` was
|
|
1706
|
+
# itself disqualifying and a PATH-SPELLED invocation never matched.
|
|
1707
|
+
# `gxargs` — the GNU findutils spelling shipped by macOS package managers —
|
|
1708
|
+
# failed the same way on the alphanumeric side. RM_CMD above solved this
|
|
1709
|
+
# exact problem for `rm` by putting the boundary before the WHOLE path and
|
|
1710
|
+
# making the directory part optional; that shape is reused verbatim.
|
|
1711
|
+
# 2. `parallel` was absent. It is a drop-in `xargs` substitute reaching the
|
|
1712
|
+
# same delete on the same unaudited stdin, and guard 14 below already had
|
|
1713
|
+
# to name it in its own evidence — it was missing only here.
|
|
1714
|
+
# 3. The flag set was a short-cluster-only copy, so `rm --recursive --force`
|
|
1715
|
+
# was allowed where `rm -rf` was refused. It now uses the SAME tails as the
|
|
1716
|
+
# rm walk, which is what makes that class of divergence unrepeatable.
|
|
1717
|
+
readonly STDIN_DELETER='(^|[^[:alnum:]_./-])([[:alnum:]_./-]*/)?(g?xargs|parallel)[[:space:]]([^;&|]*[[:space:]])?rm'
|
|
1718
|
+
if matches "$STDIN_DELETER$RM_RF_TAIL_CLUSTER" \
|
|
1719
|
+
|| matches "$STDIN_DELETER$RM_RF_TAIL_SPLIT"; then
|
|
1720
|
+
block "xargs/parallel rm -rf performs a recursive forced delete on dynamic stdin input"
|
|
1672
1721
|
fi
|
|
1673
1722
|
|
|
1674
1723
|
# 12. Disk destroyers, always on (exceeds upstream, which only scans these
|
|
@@ -121,6 +121,20 @@ export const PROVIDERS = [
|
|
|
121
121
|
sha256:
|
|
122
122
|
"18253757286e119d450133a87eb463bf8c1ce418ce24c834f4f250d60cba6f9e",
|
|
123
123
|
},
|
|
124
|
+
// Bitwarden publishes macOS archives for the same tag, and omitting them
|
|
125
|
+
// made the one genuinely pinned credential CLI the one a macOS operator
|
|
126
|
+
// had to install by hand — the unverified path, for the tool where an
|
|
127
|
+
// unverified download matters most.
|
|
128
|
+
"darwin-arm64": {
|
|
129
|
+
url: "https://github.com/bitwarden/sdk-sm/releases/download/bws-v2.1.0/bws-aarch64-apple-darwin-2.1.0.zip",
|
|
130
|
+
sha256:
|
|
131
|
+
"9cb1c1c6e6164d83b2e339883ba02b4cbb37188ce9a484b1ce8249443163e066",
|
|
132
|
+
},
|
|
133
|
+
"darwin-x64": {
|
|
134
|
+
url: "https://github.com/bitwarden/sdk-sm/releases/download/bws-v2.1.0/bws-x86_64-apple-darwin-2.1.0.zip",
|
|
135
|
+
sha256:
|
|
136
|
+
"6f626b3971368902af1b9847c02791a1b4666969d7561e2047681cded7997537",
|
|
137
|
+
},
|
|
124
138
|
},
|
|
125
139
|
},
|
|
126
140
|
{
|
|
@@ -197,11 +211,26 @@ export const TOOLS = [
|
|
|
197
211
|
"12311e320d4cfdb54d7fa2d58cd1e3a2ccb4c12e1c3abb32b0a2e48bd0f991bf",
|
|
198
212
|
binary: "gh_2.83.0_linux_arm64/bin/gh",
|
|
199
213
|
},
|
|
214
|
+
// `install` is per-block for gh and only for gh: the vendor ships a
|
|
215
|
+
// .tar.gz for Linux and a .zip for macOS, so the entry-level `release-tar`
|
|
216
|
+
// is wrong for exactly these two. Without the override the resolved entry
|
|
217
|
+
// keeps `release-tar` and the installer runs `tar -xzf` over a ZIP, which
|
|
218
|
+
// fails during extraction — a macOS pin that resolves and cannot install.
|
|
200
219
|
"darwin-arm64": {
|
|
201
220
|
url: "https://github.com/cli/cli/releases/download/v2.83.0/gh_2.83.0_macOS_arm64.zip",
|
|
202
221
|
sha256:
|
|
203
222
|
"fecba907bc361d5e33620dbf1145f11432c39fb2b388a839463cfbb89a84820b",
|
|
204
223
|
binary: "gh_2.83.0_macOS_arm64/bin/gh",
|
|
224
|
+
install: "release-zip",
|
|
225
|
+
},
|
|
226
|
+
// Intel Macs are still in service, and an Apple Silicon-only macOS block
|
|
227
|
+
// reads as "macOS is covered" while leaving half of it uncovered.
|
|
228
|
+
"darwin-x64": {
|
|
229
|
+
url: "https://github.com/cli/cli/releases/download/v2.83.0/gh_2.83.0_macOS_amd64.zip",
|
|
230
|
+
sha256:
|
|
231
|
+
"0c0de650752bb92d7283e386cafd03d9ac5f47028c648c4ab821ef08a75c0716",
|
|
232
|
+
binary: "gh_2.83.0_macOS_amd64/bin/gh",
|
|
233
|
+
install: "release-zip",
|
|
205
234
|
},
|
|
206
235
|
},
|
|
207
236
|
},
|