@codyswann/lisa 3.14.3 → 3.14.5
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/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +13 -3
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/expo/create-only/.github/workflows/maestro-e2e.yml +21 -8
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-github-build-intake/SKILL.md +54 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-repair-intake/SKILL.md +95 -0
- package/plugins/lisa/scripts/lifecycle-label-trust.mjs +494 -0
- package/plugins/lisa/skills/lisa-github-build-intake/SKILL.md +54 -0
- package/plugins/lisa/skills/lisa-repair-intake/SKILL.md +95 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/scripts/lifecycle-label-trust.mjs +494 -0
- package/plugins/lisa-agy/skills/lisa-github-build-intake/SKILL.md +54 -0
- package/plugins/lisa-agy/skills/lisa-repair-intake/SKILL.md +95 -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 +1 -1
- package/plugins/lisa-copilot/scripts/lifecycle-label-trust.mjs +494 -0
- package/plugins/lisa-copilot/skills/lisa-github-build-intake/SKILL.md +54 -0
- package/plugins/lisa-copilot/skills/lisa-repair-intake/SKILL.md +95 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/scripts/lifecycle-label-trust.mjs +494 -0
- package/plugins/lisa-cursor/skills/lisa-github-build-intake/SKILL.md +54 -0
- package/plugins/lisa-cursor/skills/lisa-repair-intake/SKILL.md +95 -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-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/scripts/lifecycle-label-trust.mjs +494 -0
- package/plugins/src/base/skills/lisa-github-build-intake/SKILL.md +54 -0
- package/plugins/src/base/skills/lisa-repair-intake/SKILL.md +95 -0
|
@@ -71,11 +71,15 @@ on:
|
|
|
71
71
|
|
|
72
72
|
permissions:
|
|
73
73
|
contents: read
|
|
74
|
-
# Read-only, and only this run's own job list. It is
|
|
75
|
-
#
|
|
76
|
-
#
|
|
77
|
-
#
|
|
78
|
-
#
|
|
74
|
+
# Read-only, and only this run's own job list. It is granted HERE, in the
|
|
75
|
+
# caller, because that is the only place it can safely be granted: a called
|
|
76
|
+
# workflow that DECLARES a scope its caller did not grant is a
|
|
77
|
+
# `startup_failure` for the whole run. So this line plus the forwarded
|
|
78
|
+
# LEG_ORDER_TOKEN secret below are what let `serialize_platform_legs` read
|
|
79
|
+
# when the iOS leg finished.
|
|
80
|
+
#
|
|
81
|
+
# Drop this line and nothing breaks — the ordering job reports the gap and
|
|
82
|
+
# lets the Android leg start. You lose the ordering, not the suite.
|
|
79
83
|
actions: read
|
|
80
84
|
|
|
81
85
|
# Keyed on the ENVIRONMENT this suite drives, not on the ref. Every run of this
|
|
@@ -126,10 +130,19 @@ jobs:
|
|
|
126
130
|
# other's session, so the loser hits a sign-in screen mid-suite and fails
|
|
127
131
|
# an assertion that has nothing wrong with it. This runs iOS first, then
|
|
128
132
|
# Android. It is ordering, not gating: a FAILING iOS leg still releases
|
|
129
|
-
# Android, so turning it on never narrows what gets tested.
|
|
130
|
-
# `actions: read` permission above
|
|
131
|
-
#
|
|
133
|
+
# Android, so turning it on never narrows what gets tested. Needs the
|
|
134
|
+
# `actions: read` permission above AND the LEG_ORDER_TOKEN secret below;
|
|
135
|
+
# without either it reports the gap and runs the legs concurrently rather
|
|
136
|
+
# than failing. Leave it off if your legs use per-platform accounts — the
|
|
137
|
+
# concurrency is free coverage in that case. The cost when on is the suite
|
|
138
|
+
# taking iOS PLUS Android instead of the slower of the two.
|
|
132
139
|
# serialize_platform_legs: true
|
|
133
140
|
secrets:
|
|
134
141
|
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}
|
|
135
142
|
MAESTRO_SECRET_ENV: ${{ secrets.MAESTRO_SECRET_ENV }}
|
|
143
|
+
# Only read when serialize_platform_legs is true. A forwarded secret
|
|
144
|
+
# carries THIS workflow's token and therefore THIS workflow's
|
|
145
|
+
# `actions: read` — which is why the scope can be granted above without
|
|
146
|
+
# the reusable workflow having to declare it and startup-fail every caller
|
|
147
|
+
# that grants less. Harmless to pass when the input is off.
|
|
148
|
+
LEG_ORDER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
package/package.json
CHANGED
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
}
|
|
126
126
|
},
|
|
127
127
|
"name": "@codyswann/lisa",
|
|
128
|
-
"version": "3.14.
|
|
128
|
+
"version": "3.14.5",
|
|
129
129
|
"description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
|
|
130
130
|
"main": "dist/index.js",
|
|
131
131
|
"exports": {
|
|
@@ -168,6 +168,60 @@ gh label list --repo <org>/<repo> --json name \
|
|
|
168
168
|
|
|
169
169
|
If none of the configured role labels exist on the repo → label convention not adopted, surface a setup error and exit. If the role labels exist but none are `$READY` on any open issue matching the resolved assignee filter (or any open issue when the filter is empty) → genuinely empty queue, exit cleanly with `"No GitHub issues labeled $READY. Nothing to do."`
|
|
170
170
|
|
|
171
|
+
#### 2b. Lifecycle-label trust resolution (bot-authored labels are not signals)
|
|
172
|
+
|
|
173
|
+
**A `status:*` label is only a lifecycle signal if a trustworthy actor applied it.** Third-party GitHub Apps write labels through the same API humans do, and at least one — `coderabbitai[bot]` — stamps `status:*` on issues seconds after filing. Measured on CodySwannGT/lisa#2460–#2540: seven of eight bot-applied lifecycle labels landed 26–118s after creation. Those labels are guesses wearing the costume of the intake contract, and they break the queue in **both** directions:
|
|
174
|
+
|
|
175
|
+
- a bot-applied **`$CLAIMED`** makes an unworked issue look like somebody's active work, so no cycle picks it up and no human re-examines it (#2470 sat unworked for a day this way);
|
|
176
|
+
- a bot-applied **`$READY`** puts an issue into the build queue that no human ever flipped ready (#2538), inverting the gate the ready role exists to be.
|
|
177
|
+
|
|
178
|
+
Before treating any candidate's lifecycle labels as meaningful, resolve which ones are trustworthy:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
TRUST_DIR=$(mktemp -d)
|
|
182
|
+
trap 'rm -rf "$TRUST_DIR"' EXIT
|
|
183
|
+
|
|
184
|
+
gh api "repos/<org>/<repo>/issues/<n>" > "$TRUST_DIR/issue.json"
|
|
185
|
+
|
|
186
|
+
# --paginate emits ONE ARRAY PER PAGE. Reading `$t[0]` would pass only page one,
|
|
187
|
+
# silently dropping later label events — the newest of which is exactly the
|
|
188
|
+
# application this classifier keys on. --slurp + `add` flattens all pages.
|
|
189
|
+
gh api "repos/<org>/<repo>/issues/<n>/timeline?per_page=100" --paginate --slurp \
|
|
190
|
+
> "$TRUST_DIR/pages.json"
|
|
191
|
+
jq 'add // []' "$TRUST_DIR/pages.json" > "$TRUST_DIR/timeline.json"
|
|
192
|
+
|
|
193
|
+
# Resolve config the SAME way `read_role` does: the local file overrides the
|
|
194
|
+
# global one key by key. Reading only `.lisa.config.json` would resolve a stale
|
|
195
|
+
# terminal `done` label for any project that overrides it locally, and
|
|
196
|
+
# `lisa-repair-intake` WRITES on the drift direction that mistake produces.
|
|
197
|
+
# Both files are optional — a missing one must not abort the cycle.
|
|
198
|
+
jq -s '(.[0] // {}) * (.[1] // {})' \
|
|
199
|
+
<(cat .lisa.config.json 2>/dev/null || echo '{}') \
|
|
200
|
+
<(cat .lisa.config.local.json 2>/dev/null || echo '{}') \
|
|
201
|
+
> "$TRUST_DIR/config.json"
|
|
202
|
+
|
|
203
|
+
jq -n --slurpfile i "$TRUST_DIR/issue.json" \
|
|
204
|
+
--slurpfile t "$TRUST_DIR/timeline.json" \
|
|
205
|
+
--slurpfile c "$TRUST_DIR/config.json" \
|
|
206
|
+
'{issue: $i[0], timeline: $t[0], config: $c[0]}' \
|
|
207
|
+
> "$TRUST_DIR/input.json"
|
|
208
|
+
|
|
209
|
+
node "${CLAUDE_PLUGIN_ROOT}/scripts/lifecycle-label-trust.mjs" < "$TRUST_DIR/input.json"
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
The temp directory is **per invocation**. Fixed `/tmp/lisa-*.json` paths let two concurrent intake cycles interleave one issue's metadata with another's timeline, which yields a confident verdict about an item that was never examined.
|
|
213
|
+
|
|
214
|
+
The classifier returns `trusted`, `untrusted`, `unknownProvenance`, and a per-label `evaluated` list carrying the actor and the latency behind each decision. **Use `trusted` wherever this skill would otherwise read the raw label set**, and specifically:
|
|
215
|
+
|
|
216
|
+
- a candidate whose `$READY` is **untrusted** is **not claimable** — skip it and leave it for a human to flip genuinely ready; report it in the summary rather than dispatching it;
|
|
217
|
+
- a candidate whose `$CLAIMED` is **untrusted** is **not claimed** — if its `$READY` is trusted it stays a normal candidate, exactly as if the bot label were absent.
|
|
218
|
+
|
|
219
|
+
**Never unlabel to correct this.** The bot re-applies its label on each subsequent review event, so reverting produces a label-flap loop that is worse than the defect. The guard is that intake stops *believing* the label; it writes nothing. Distrust is idempotent and cannot race.
|
|
220
|
+
|
|
221
|
+
A label whose provenance cannot be established (applied at creation, which GitHub records no `labeled` event for) is trusted but listed in `unknownProvenance` — failing closed there would ignore the human `status:ready` that opens the queue. Report that list; do not silently drop it.
|
|
222
|
+
|
|
223
|
+
Lifecycle membership is decided by the **`status:` prefix**, never by a pinned member list. The live family has drifted 7 → 6 members, and a literal set breaks silently: an unrecognised member simply fails to match, so the guard would report clean on exactly the case it stopped covering.
|
|
224
|
+
|
|
171
225
|
### Phase 3 — Process the first eligible ready issue
|
|
172
226
|
|
|
173
227
|
#### 3a.0 Repo-scope gate (claim only current-repo issues)
|
|
@@ -560,6 +560,101 @@ native-open / active / unresolved:
|
|
|
560
560
|
4. Post a compact `[lisa-repair-intake]` note only when the native close-out changed state or when
|
|
561
561
|
an actionable setup error must be surfaced. Do not spam already-closed terminal items.
|
|
562
562
|
|
|
563
|
+
### Lifecycle label contradicts native state — walk BOTH directions
|
|
564
|
+
|
|
565
|
+
A lifecycle label can lie in two ways, and historically only one of them had an owner:
|
|
566
|
+
|
|
567
|
+
| label | native state | owned by |
|
|
568
|
+
|---|---|---|
|
|
569
|
+
| terminal `done` role | still open / active | the `Build terminal-open` section above |
|
|
570
|
+
| non-terminal role (`ready`, `claimed`, `blocked`, or any other `status:` member) | closed / completed | **nothing, until now** |
|
|
571
|
+
|
|
572
|
+
A pass that walks one direction leaves the other accumulating silently. Measured on the Linear TUN
|
|
573
|
+
board: **TUN-556 and TUN-503 both carry a terminal `status:done` while still natively open in
|
|
574
|
+
`On Dev`** — invisible to intake and indistinguishable from handled, exactly like a bot-applied
|
|
575
|
+
`status:in-progress` on GitHub.
|
|
576
|
+
|
|
577
|
+
Resolve both directions in one pass with the shipped detector, which always reports
|
|
578
|
+
`directionsWalked` covering both, so a "clean" verdict is an assertion that both were examined.
|
|
579
|
+
|
|
580
|
+
**Build the classifier input inside this cycle. Never consume a temp file another skill wrote.**
|
|
581
|
+
repair-intake runs as its own cycle, so borrowing `lisa-github-build-intake`'s scratch file fails
|
|
582
|
+
two ways: absent, the redirect fails and `set -e` aborts the whole cycle; stale from an earlier run
|
|
583
|
+
on a *different* item, the resolver classifies that other item and this section reports `drifts: []`
|
|
584
|
+
while `directionsWalked` still claims both directions were walked. That is precisely the
|
|
585
|
+
silent-clean verdict this section promises cannot happen.
|
|
586
|
+
|
|
587
|
+
```bash
|
|
588
|
+
TRUST_DIR=$(mktemp -d)
|
|
589
|
+
trap 'rm -rf "$TRUST_DIR"' EXIT
|
|
590
|
+
|
|
591
|
+
gh api "repos/<org>/<repo>/issues/<n>" > "$TRUST_DIR/issue.json"
|
|
592
|
+
|
|
593
|
+
# --paginate emits ONE ARRAY PER PAGE; --slurp + `add` flattens all of them so a
|
|
594
|
+
# label event on page two is not silently dropped.
|
|
595
|
+
gh api "repos/<org>/<repo>/issues/<n>/timeline?per_page=100" --paginate --slurp \
|
|
596
|
+
> "$TRUST_DIR/pages.json"
|
|
597
|
+
jq 'add // []' "$TRUST_DIR/pages.json" > "$TRUST_DIR/timeline.json"
|
|
598
|
+
|
|
599
|
+
# Same resolution `read_role` uses: local overrides global, both optional.
|
|
600
|
+
# Resolving a stale terminal `done` here would make the true terminal label look
|
|
601
|
+
# like `open-label-closed-state` — and that direction WRITES.
|
|
602
|
+
jq -s '(.[0] // {}) * (.[1] // {})' \
|
|
603
|
+
<(cat .lisa.config.json 2>/dev/null || echo '{}') \
|
|
604
|
+
<(cat .lisa.config.local.json 2>/dev/null || echo '{}') \
|
|
605
|
+
> "$TRUST_DIR/config.json"
|
|
606
|
+
|
|
607
|
+
jq -n --slurpfile i "$TRUST_DIR/issue.json" \
|
|
608
|
+
--slurpfile t "$TRUST_DIR/timeline.json" \
|
|
609
|
+
--slurpfile c "$TRUST_DIR/config.json" \
|
|
610
|
+
'{issue: $i[0], timeline: $t[0], config: $c[0]}' \
|
|
611
|
+
> "$TRUST_DIR/input.json"
|
|
612
|
+
|
|
613
|
+
node "${CLAUDE_PLUGIN_ROOT}/scripts/lifecycle-label-trust.mjs" < "$TRUST_DIR/input.json"
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
**Untrusted labels are excluded from `drifts` before any repair runs** — the classifier does this
|
|
617
|
+
itself, so repair must **never act on a label the classifier refused to believe**. Without it, a
|
|
618
|
+
bot-applied label on a natively closed item would be advanced to the terminal `done` role by the
|
|
619
|
+
`open-label-closed-state` repair, laundering the exact input the guard rejected into a real write.
|
|
620
|
+
The `excluded` array reports what was held back; surface it, do not repair it.
|
|
621
|
+
|
|
622
|
+
1. **`terminal-label-open-state`** → hand to the `Build terminal-open → native close` section above.
|
|
623
|
+
Do not restate its rules; the terminality test lives there (intermediate env rungs like
|
|
624
|
+
`status:on-dev` / `status:on-stg` are **not** terminal).
|
|
625
|
+
2. **`open-label-closed-state`** → the previously unowned direction. The item is natively closed or
|
|
626
|
+
completed while still wearing a non-terminal lifecycle role. Advance the label to the env-resolved
|
|
627
|
+
terminal `done` role and post one idempotent `[lisa-repair-intake]` note. This is a write on
|
|
628
|
+
**Lisa's own** lifecycle surface, not a contest with another writer.
|
|
629
|
+
Apply the leaf/container check from the **Lifecycle ownership guard** section *before* this
|
|
630
|
+
branch: repair-intake owns container repair, so a natively-closed `ready` **leaf** is skipped
|
|
631
|
+
here and left to the build lane rather than claimed by this pass.
|
|
632
|
+
3. **Vendor caveat — Linear.** On Linear the lifecycle surface is the native workflow **state**, not a
|
|
633
|
+
`status:*` label (see `config-resolution`). A `status:*` label there is leftover cruft that no
|
|
634
|
+
repair direction reads, which is precisely why TUN-556 and TUN-503 rotted. Treat Linear's native
|
|
635
|
+
state as authoritative and remove the contradicting stale label; never move the state to match a
|
|
636
|
+
label.
|
|
637
|
+
|
|
638
|
+
Membership in the lifecycle namespace is decided by the **`status:` prefix**, never a pinned member
|
|
639
|
+
list — the live family has drifted 7 → 6 members, and a literal set fails silently by simply not
|
|
640
|
+
matching the member it stopped covering.
|
|
641
|
+
|
|
642
|
+
### Bot-authored lifecycle label → distrust, never revert (read-only)
|
|
643
|
+
|
|
644
|
+
`status:*` labels written by a **bot** actor within the plausibility window of the item's creation are
|
|
645
|
+
not lifecycle signals — see `lisa-github-build-intake` Phase 2b for the measurement and the
|
|
646
|
+
classifier. Report them here so an operator can see how much of the board is wearing a label nobody
|
|
647
|
+
chose:
|
|
648
|
+
|
|
649
|
+
- list each item alongside the untrusted label, the bot login, and the latency, from the classifier's
|
|
650
|
+
`untrusted` array;
|
|
651
|
+
- list `unknownProvenance` labels separately — trusted, but unattributable;
|
|
652
|
+
- **do not unlabel and do not relabel.** The bot re-applies on each subsequent review event, so a
|
|
653
|
+
reverting reconciler produces a label-flap loop that is worse than the defect. Repair happens by
|
|
654
|
+
intake declining to *believe* the label, which is idempotent and writes nothing.
|
|
655
|
+
|
|
656
|
+
This class is deliberately **read-only** and never counts toward the repair cap's actionable work.
|
|
657
|
+
|
|
563
658
|
### Build parent rollup reconciliation (intermediate-env or terminal close-out)
|
|
564
659
|
|
|
565
660
|
For each parent/container item (an Epic, a Linear Project, or any item — of any type — with open child work),
|