@codyswann/lisa 2.326.2 → 2.327.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/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +25 -23
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/sync/lifecycle-defaults.d.ts +61 -0
- package/dist/sync/lifecycle-defaults.d.ts.map +1 -0
- package/dist/sync/lifecycle-defaults.js +75 -0
- package/dist/sync/lifecycle-defaults.js.map +1 -0
- package/dist/sync/registry.d.ts.map +1 -1
- package/dist/sync/registry.js +15 -23
- package/dist/sync/registry.js.map +1 -1
- 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-confluence-to-tracker/SKILL.md +3 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-github-validate-issue/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-build-intake/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-validate-ticket/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-access/SKILL.md +20 -9
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-build-intake/SKILL.md +76 -66
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-claim/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-evidence/SKILL.md +7 -7
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-journey/SKILL.md +2 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-sync/SKILL.md +34 -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 +6 -6
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-write-issue/SKILL.md +8 -7
- package/plugins/lisa/.codex-plugin/skills/lisa-notion-to-tracker/SKILL.md +3 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-repair-intake/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-linear/SKILL.md +59 -12
- package/plugins/lisa/.codex-plugin/skills/lisa-tracker-sync/SKILL.md +2 -2
- package/plugins/lisa/agents/linear-agent.md +6 -6
- package/plugins/lisa/agents/linear-build-intake.md +3 -3
- package/plugins/lisa/rules/eager/rejection-detection.md +2 -2
- package/plugins/lisa/rules/reference/config-resolution.md +37 -9
- package/plugins/lisa/rules/reference/rejection-detection.md +2 -2
- package/plugins/lisa/skills/lisa-confluence-to-tracker/SKILL.md +3 -3
- package/plugins/lisa/skills/lisa-github-validate-issue/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-jira-build-intake/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-jira-validate-ticket/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-linear-access/SKILL.md +20 -9
- package/plugins/lisa/skills/lisa-linear-build-intake/SKILL.md +77 -67
- package/plugins/lisa/skills/lisa-linear-claim/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-linear-evidence/SKILL.md +7 -7
- package/plugins/lisa/skills/lisa-linear-journey/SKILL.md +2 -2
- package/plugins/lisa/skills/lisa-linear-sync/SKILL.md +34 -30
- package/plugins/lisa/skills/lisa-linear-to-tracker/SKILL.md +3 -3
- package/plugins/lisa/skills/lisa-linear-validate-issue/SKILL.md +6 -6
- package/plugins/lisa/skills/lisa-linear-write-issue/SKILL.md +8 -7
- package/plugins/lisa/skills/lisa-notion-to-tracker/SKILL.md +3 -3
- package/plugins/lisa/skills/lisa-repair-intake/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-setup-linear/SKILL.md +59 -12
- package/plugins/lisa/skills/lisa-tracker-build-intake/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-tracker-sync/SKILL.md +2 -2
- package/plugins/lisa-agy/agents/linear-agent.md +6 -6
- package/plugins/lisa-agy/agents/linear-build-intake.md +3 -3
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-confluence-to-tracker/SKILL.md +3 -3
- package/plugins/lisa-agy/skills/lisa-github-validate-issue/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-jira-build-intake/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-jira-validate-ticket/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-linear-access/SKILL.md +20 -9
- package/plugins/lisa-agy/skills/lisa-linear-build-intake/SKILL.md +77 -67
- package/plugins/lisa-agy/skills/lisa-linear-claim/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-linear-evidence/SKILL.md +7 -7
- package/plugins/lisa-agy/skills/lisa-linear-journey/SKILL.md +2 -2
- package/plugins/lisa-agy/skills/lisa-linear-sync/SKILL.md +34 -30
- package/plugins/lisa-agy/skills/lisa-linear-to-tracker/SKILL.md +3 -3
- package/plugins/lisa-agy/skills/lisa-linear-validate-issue/SKILL.md +6 -6
- package/plugins/lisa-agy/skills/lisa-linear-write-issue/SKILL.md +8 -7
- package/plugins/lisa-agy/skills/lisa-notion-to-tracker/SKILL.md +3 -3
- package/plugins/lisa-agy/skills/lisa-repair-intake/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-setup-linear/SKILL.md +59 -12
- package/plugins/lisa-agy/skills/lisa-tracker-build-intake/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-tracker-sync/SKILL.md +2 -2
- 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/agents/linear-agent.agent.md +6 -6
- package/plugins/lisa-copilot/agents/linear-build-intake.agent.md +3 -3
- package/plugins/lisa-copilot/rules/eager/rejection-detection.md +2 -2
- package/plugins/lisa-copilot/rules/reference/config-resolution.md +37 -9
- package/plugins/lisa-copilot/rules/reference/rejection-detection.md +2 -2
- package/plugins/lisa-copilot/skills/lisa-confluence-to-tracker/SKILL.md +3 -3
- package/plugins/lisa-copilot/skills/lisa-github-validate-issue/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-jira-build-intake/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-jira-validate-ticket/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-linear-access/SKILL.md +20 -9
- package/plugins/lisa-copilot/skills/lisa-linear-build-intake/SKILL.md +77 -67
- package/plugins/lisa-copilot/skills/lisa-linear-claim/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-linear-evidence/SKILL.md +7 -7
- package/plugins/lisa-copilot/skills/lisa-linear-journey/SKILL.md +2 -2
- package/plugins/lisa-copilot/skills/lisa-linear-sync/SKILL.md +34 -30
- package/plugins/lisa-copilot/skills/lisa-linear-to-tracker/SKILL.md +3 -3
- package/plugins/lisa-copilot/skills/lisa-linear-validate-issue/SKILL.md +6 -6
- package/plugins/lisa-copilot/skills/lisa-linear-write-issue/SKILL.md +8 -7
- package/plugins/lisa-copilot/skills/lisa-notion-to-tracker/SKILL.md +3 -3
- package/plugins/lisa-copilot/skills/lisa-repair-intake/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-setup-linear/SKILL.md +59 -12
- package/plugins/lisa-copilot/skills/lisa-tracker-build-intake/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-tracker-sync/SKILL.md +2 -2
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/agents/linear-agent.md +6 -6
- package/plugins/lisa-cursor/agents/linear-build-intake.md +3 -3
- package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +37 -9
- package/plugins/lisa-cursor/rules/rejection-detection-reference.mdc +2 -2
- package/plugins/lisa-cursor/rules/rejection-detection.mdc +2 -2
- package/plugins/lisa-cursor/skills/lisa-confluence-to-tracker/SKILL.md +3 -3
- package/plugins/lisa-cursor/skills/lisa-github-validate-issue/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-jira-build-intake/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-jira-validate-ticket/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-linear-access/SKILL.md +20 -9
- package/plugins/lisa-cursor/skills/lisa-linear-build-intake/SKILL.md +77 -67
- package/plugins/lisa-cursor/skills/lisa-linear-claim/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-linear-evidence/SKILL.md +7 -7
- package/plugins/lisa-cursor/skills/lisa-linear-journey/SKILL.md +2 -2
- package/plugins/lisa-cursor/skills/lisa-linear-sync/SKILL.md +34 -30
- package/plugins/lisa-cursor/skills/lisa-linear-to-tracker/SKILL.md +3 -3
- package/plugins/lisa-cursor/skills/lisa-linear-validate-issue/SKILL.md +6 -6
- package/plugins/lisa-cursor/skills/lisa-linear-write-issue/SKILL.md +8 -7
- package/plugins/lisa-cursor/skills/lisa-notion-to-tracker/SKILL.md +3 -3
- package/plugins/lisa-cursor/skills/lisa-repair-intake/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-setup-linear/SKILL.md +59 -12
- package/plugins/lisa-cursor/skills/lisa-tracker-build-intake/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-tracker-sync/SKILL.md +2 -2
- 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/agents/linear-agent.md +6 -6
- package/plugins/src/base/agents/linear-build-intake.md +3 -3
- package/plugins/src/base/rules/eager/rejection-detection.md +2 -2
- package/plugins/src/base/rules/reference/config-resolution.md +37 -9
- package/plugins/src/base/rules/reference/rejection-detection.md +2 -2
- package/plugins/src/base/skills/lisa-confluence-to-tracker/SKILL.md +3 -3
- package/plugins/src/base/skills/lisa-github-validate-issue/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-jira-build-intake/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-jira-validate-ticket/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-linear-access/SKILL.md +20 -9
- package/plugins/src/base/skills/lisa-linear-build-intake/SKILL.md +77 -67
- package/plugins/src/base/skills/lisa-linear-claim/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-linear-evidence/SKILL.md +7 -7
- package/plugins/src/base/skills/lisa-linear-journey/SKILL.md +2 -2
- package/plugins/src/base/skills/lisa-linear-sync/SKILL.md +34 -30
- package/plugins/src/base/skills/lisa-linear-to-tracker/SKILL.md +3 -3
- package/plugins/src/base/skills/lisa-linear-validate-issue/SKILL.md +6 -6
- package/plugins/src/base/skills/lisa-linear-write-issue/SKILL.md +8 -7
- package/plugins/src/base/skills/lisa-notion-to-tracker/SKILL.md +3 -3
- package/plugins/src/base/skills/lisa-repair-intake/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-setup-linear/SKILL.md +59 -12
- package/plugins/src/base/skills/lisa-tracker-build-intake/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-tracker-sync/SKILL.md +2 -2
|
@@ -12,27 +12,28 @@ allowed-tools: ["Skill", "Bash"]
|
|
|
12
12
|
2. The literal token `linear` — falls back to `linear.teamKey` from `.lisa.config.json`.
|
|
13
13
|
3. A pre-built Linear MCP filter (advanced) — used as-is.
|
|
14
14
|
|
|
15
|
-
Run one build-intake cycle. The first eligible ready Issue is claimed, built via the `linear-agent` workflow run in-session (Phase 3c, culminating in `lisa-implement`),
|
|
15
|
+
Run one build-intake cycle. The first eligible ready Issue is claimed, built via the `linear-agent` workflow run in-session (Phase 3c, culminating in `lisa-implement`), transitioned to the configured `done` state on completion, then the cycle exits. Remaining ready Issues stay queued for later scheduler invocations.
|
|
16
16
|
|
|
17
17
|
This skill is the destination of the `lisa-tracker-build-intake` shim when `tracker = "linear"`.
|
|
18
18
|
|
|
19
19
|
## Workflow resolution
|
|
20
20
|
|
|
21
|
-
Build-queue
|
|
21
|
+
Build-queue **workflow state** names are read from `.lisa.config.json` `linear.workflow.*`, falling back to defaults documented in the `config-resolution` rule. Bash pattern:
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
24
|
# Read role with default fallback. Local overrides global per-key.
|
|
25
25
|
read_role() {
|
|
26
26
|
local role="$1" default="$2"
|
|
27
27
|
local local_v global_v
|
|
28
|
-
local_v=$(jq -r ".linear.
|
|
29
|
-
global_v=$(jq -r ".linear.
|
|
28
|
+
local_v=$(jq -r ".linear.workflow.${role} // empty" .lisa.config.local.json 2>/dev/null)
|
|
29
|
+
global_v=$(jq -r ".linear.workflow.${role} // empty" .lisa.config.json 2>/dev/null)
|
|
30
30
|
echo "${local_v:-${global_v:-$default}}"
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
READY=$(read_role ready "
|
|
34
|
-
CLAIMED=$(read_role claimed "
|
|
35
|
-
REVIEW=$(read_role review "
|
|
33
|
+
READY=$(read_role ready "Todo")
|
|
34
|
+
CLAIMED=$(read_role claimed "In Progress")
|
|
35
|
+
REVIEW=$(read_role review "In Review")
|
|
36
|
+
BLOCKED=$(read_role blocked "Blocked")
|
|
36
37
|
```
|
|
37
38
|
|
|
38
39
|
For env-keyed `done`, resolve the env first, then look up `done[<env>]`:
|
|
@@ -50,32 +51,38 @@ if [ -z "$TARGET_ENV" ] && [ -n "$PR_BASE_BRANCH" ]; then
|
|
|
50
51
|
.lisa.config.json 2>/dev/null | head -1)
|
|
51
52
|
fi
|
|
52
53
|
|
|
53
|
-
DONE_TYPE=$(jq -r '.linear.
|
|
54
|
+
DONE_TYPE=$(jq -r '.linear.workflow.done | type' .lisa.config.json 2>/dev/null)
|
|
54
55
|
if [ "$DONE_TYPE" = "string" ]; then
|
|
55
|
-
DONE=$(jq -r '.linear.
|
|
56
|
+
DONE=$(jq -r '.linear.workflow.done' .lisa.config.json)
|
|
56
57
|
elif [ "$DONE_TYPE" = "object" ]; then
|
|
57
|
-
[ -z "$TARGET_ENV" ] && { echo "ERROR: linear.
|
|
58
|
-
DONE=$(jq -r --arg e "$TARGET_ENV" '.linear.
|
|
59
|
-
[ -z "$DONE" ] && { echo "ERROR: linear.
|
|
58
|
+
[ -z "$TARGET_ENV" ] && { echo "ERROR: linear.workflow.done is env-keyed but env not resolvable"; exit 1; }
|
|
59
|
+
DONE=$(jq -r --arg e "$TARGET_ENV" '.linear.workflow.done[$e] // empty' .lisa.config.json)
|
|
60
|
+
[ -z "$DONE" ] && { echo "ERROR: linear.workflow.done has no entry for env '$TARGET_ENV'"; exit 1; }
|
|
60
61
|
else
|
|
61
62
|
case "$TARGET_ENV" in
|
|
62
|
-
dev) DONE="
|
|
63
|
-
staging) DONE="
|
|
64
|
-
production) DONE="
|
|
65
|
-
*) echo "ERROR: cannot resolve done
|
|
63
|
+
dev) DONE="On Dev" ;;
|
|
64
|
+
staging) DONE="On Stg" ;;
|
|
65
|
+
production) DONE="Done" ;;
|
|
66
|
+
*) echo "ERROR: cannot resolve done state without env"; exit 1 ;;
|
|
66
67
|
esac
|
|
67
68
|
fi
|
|
68
69
|
```
|
|
69
70
|
|
|
70
|
-
In prose below, the role names refer to the resolved
|
|
71
|
+
In prose below, the role names refer to the resolved **states**: e.g. "the `ready` state" means whatever `linear.workflow.ready` resolves to (default: `Todo`).
|
|
71
72
|
|
|
72
|
-
## Why
|
|
73
|
+
## Why native states, not labels
|
|
73
74
|
|
|
74
|
-
Linear
|
|
75
|
+
Linear Issues carry first-class workflow states with a machine-readable `type` (`backlog` / `unstarted` / `started` / `completed` / `canceled`) — the same shape JIRA statuses have, and the reason this adapter mirrors `lisa-jira-build-intake` rather than `lisa-github-build-intake`. GitHub Issues has no such field, so labels are the only lane available *there*; that is a constraint of GitHub's data model, not a preference Linear shares.
|
|
76
|
+
|
|
77
|
+
Driving this queue off labels (as it was until the state migration) left two writers on one lifecycle: Linear's own git automations move `state` on merge while Lisa moved only labels, so the two disagreed permanently on any merge that did not run through a Lisa flow — and `On Dev` / `On Stg` could never appear on a board, cycle or insight, because Linear groups by state.
|
|
78
|
+
|
|
79
|
+
The old objection was that per-team state names vary and get renamed. That is equally true of the JIRA statuses Lisa keys on regardless, and Linear additionally exposes the rename-proof `type` discriminator. Names live in `linear.workflow`, so a rename is a one-key override.
|
|
80
|
+
|
|
81
|
+
**Consequence for terminal closure:** native closure is no longer a separate step bolted onto a label transition. Moving to the terminal `done` state **is** the closure, and `leaf-only-lifecycle`'s "terminal native closure" requirement is satisfied by the same write that sets the role. The intermediate env rungs are typed `started`, not `completed`, so an Issue on `On Dev` correctly reads as open work.
|
|
75
82
|
|
|
76
83
|
## Configuration
|
|
77
84
|
|
|
78
|
-
Reads `linear.workspace`, `linear.teamKey`, and `linear.
|
|
85
|
+
Reads `linear.workspace`, `linear.teamKey`, and `linear.workflow.*` from `.lisa.config.json` (with `.local` override).
|
|
79
86
|
|
|
80
87
|
## Confirmation policy
|
|
81
88
|
|
|
@@ -85,29 +92,29 @@ Specifically forbidden:
|
|
|
85
92
|
|
|
86
93
|
- Previewing projected scope (Issue count, projected PR count, build duration) and asking whether to continue.
|
|
87
94
|
- Offering A/B/C-style choices like "proceed / skip a few / dry-run only" — the documented behavior IS the default.
|
|
88
|
-
- Pausing because the queue is large, items look complex, or items are likely to be `
|
|
95
|
+
- Pausing because the queue is large, items look complex, or items are likely to be moved to `$BLOCKED` by the pre-flight gate. The pre-flight `$BLOCKED` outcome is a valid terminal state of the per-Issue lifecycle.
|
|
89
96
|
- Pausing because the build flow looks expensive.
|
|
90
97
|
|
|
91
98
|
The only legitimate reasons to stop early:
|
|
92
99
|
|
|
93
100
|
- Missing team key or required configuration. Surface and exit.
|
|
94
|
-
-
|
|
95
|
-
- Empty ready set. Exit cleanly with `"No Linear Issues
|
|
101
|
+
- Workflow states not yet adopted (the `ready` state does not exist on the team). Surface and exit with an Adoption hint pointing at `/lisa:setup:linear`.
|
|
102
|
+
- Empty ready set. Exit cleanly with `"No Linear Issues in state $READY. Nothing to do."`
|
|
96
103
|
|
|
97
104
|
## Lifecycle assumed
|
|
98
105
|
|
|
99
|
-
Linear build queue uses
|
|
106
|
+
The Linear build queue uses native issue **workflow states**:
|
|
100
107
|
|
|
101
108
|
```text
|
|
102
109
|
ready → claimed → review → done(env-keyed) (downstream)
|
|
103
110
|
(human/PM) (us claim) (us PR ready) (us build done)
|
|
104
111
|
```
|
|
105
112
|
|
|
106
|
-
(Defaults: `
|
|
113
|
+
(Defaults: `Todo` / `In Progress` / `In Review` / `On Dev`/`On Stg`/`Done`.)
|
|
107
114
|
|
|
108
|
-
This skill ONLY transitions `$READY → $CLAIMED` on claim, and `$CLAIMED → $DONE` on completion. It never touches `
|
|
115
|
+
This skill ONLY transitions `$READY → $CLAIMED` on claim, and `$CLAIMED → $DONE` on completion. It never touches the terminal production `done`, `$REVIEW` (owned by the lifecycle / `lisa-linear-evidence`), or `$BLOCKED` (owned by the pre-flight gate).
|
|
109
116
|
|
|
110
|
-
**Pre-flight check**: at start of each cycle, confirm `$READY`, `$CLAIMED`, and the relevant `$DONE` variants exist on the team via `lisa-linear-access operation: list-
|
|
117
|
+
**Pre-flight check**: at start of each cycle, confirm `$READY`, `$CLAIMED`, and the relevant `$DONE` variants exist on the team via `lisa-linear-access operation: list-workflow-states`. If `$READY` is missing, stop and report adoption needed. **Unlike labels, a missing state cannot be created on demand here** — a workflow state is team configuration with a `type` and a board position, and guessing either would put an Issue somewhere a human did not sanction. Any missing state is a setup defect: report it, name the role and the expected state, and point at `/lisa:setup:linear`.
|
|
111
118
|
|
|
112
119
|
## Phases
|
|
113
120
|
|
|
@@ -120,13 +127,13 @@ This skill ONLY transitions `$READY → $CLAIMED` on claim, and `$CLAIMED → $D
|
|
|
120
127
|
|
|
121
128
|
### Phase 2 — Find ready Issues
|
|
122
129
|
|
|
123
|
-
Query: `lisa-linear-access operation: list-issues({team: <teamId>,
|
|
130
|
+
Query: `lisa-linear-access operation: list-issues({team: <teamId>, state: "$READY"})`.
|
|
124
131
|
|
|
125
|
-
Capture each Issue's: identifier, title, type label, priority, assignee, project, labels, description summary.
|
|
132
|
+
Capture each Issue's: identifier, title, type label, priority, assignee, project, state, labels, description summary.
|
|
126
133
|
|
|
127
|
-
> **No query-time repo pre-filter here (by design).** Unlike `lisa-jira-build-intake`, which narrows its JQL with `AND (labels = "repo:<current>" OR labels IS EMPTY)` (the query-time arm of `repo-scope-split`), the Linear `list_issues` label filter is an AND-of-labels and cannot express "current-repo **or** unlabeled" in one query. Adding `repo:<current>` to this query would strand unlabeled Issues the determine + stamp path must see. So the Linear scanner keeps this query broad and relies on the per-candidate 3a.0 gate below for repo scoping.
|
|
134
|
+
> **No query-time repo pre-filter here (by design).** Unlike `lisa-jira-build-intake`, which narrows its JQL with `AND (labels = "repo:<current>" OR labels IS EMPTY)` (the query-time arm of `repo-scope-split`), the Linear `list_issues` label filter is an AND-of-labels and cannot express "current-repo **or** unlabeled" in one query. Adding `repo:<current>` to this query would strand unlabeled Issues the determine + stamp path must see. So the Linear scanner keeps this query broad and relies on the per-candidate 3a.0 gate below for repo scoping. (The `state` filter above is orthogonal to that — it narrows the lifecycle lane, not the repo, and is a single-valued equality so it has none of the AND-of-labels problem.)
|
|
128
135
|
|
|
129
|
-
If empty, report `"No Linear Issues
|
|
136
|
+
If empty, report `"No Linear Issues in state $READY. Nothing to do."` and exit. Common idle case.
|
|
130
137
|
|
|
131
138
|
### Phase 3 — Process the first eligible ready Issue
|
|
132
139
|
|
|
@@ -145,9 +152,9 @@ A Linear team can oversee multiple repos (`frontend` / `backend` / `infrastructu
|
|
|
145
152
|
|
|
146
153
|
#### 3a. Leaf-only claim gate (skip / safe-block containers)
|
|
147
154
|
|
|
148
|
-
Build intake claims **only independently implementable leaf work units**. This enforces the claim-time arm of the vendor-neutral `leaf-only-lifecycle` rule: a parent/container that still
|
|
155
|
+
Build intake claims **only independently implementable leaf work units**. This enforces the claim-time arm of the vendor-neutral `leaf-only-lifecycle` rule: a parent/container that still sits in the build-ready state (e.g. moved to `$READY` before this rule existed, or hand-moved on a Project-grouped parent Issue) is **never claimed** — intake skips it or safe-blocks it with a clear lifecycle-repair message. It is the claim-time complement to the write-time labeling in `lisa-linear-write-issue` and the validate-time S15 gate in `lisa-linear-validate-issue`; all three cite the same rule so the classification never drifts. **Never silently implement a container.**
|
|
149
156
|
|
|
150
|
-
Run this gate **before** the claim
|
|
157
|
+
Run this gate **before** the claim transition, starting with the oldest/highest-priority ready candidate. Do NOT transition, comment "Claimed", or dispatch the lifecycle for an Issue that fails the gate.
|
|
151
158
|
|
|
152
159
|
**Resolve container vs. leaf — structural first, then nominal.** Per `leaf-only-lifecycle` the classification is structural: an Issue is a **container** if it has **open** child work, whatever its declared type; otherwise the **type label** decides. Resolve child work using the same hierarchy `lisa-linear-read-issue` uses — Linear's native parentage: an Issue groups **sub-issues** via `parentId`, and a **Project** (the Epic equivalent) groups Issues via `projectId`. Relations (`save_issue_relation` — `blocks` / `is blocked by`) express dependencies and are **not** parentage — do not count them as children.
|
|
153
160
|
|
|
@@ -176,33 +183,35 @@ Classify and act (first match wins). The type comes from the Issue's `type:` lab
|
|
|
176
183
|
|
|
177
184
|
The childless-parent exception promotes every childless type **except Epic** to a claimable leaf: a childless Story is a directly shippable increment and a childless Spike *is* the investigation unit, so neither is stranded. Only a childless **Epic** (a Linear Project) stays unclaimed — it is a pure rollup container by design, and a childless one is an incomplete decomposition or a mis-applied role, never an implementable unit.
|
|
178
185
|
|
|
179
|
-
**Safe-block (default action for a flagged container).** Leave the build-ready
|
|
186
|
+
**Safe-block (default action for a flagged container).** Leave the Issue in the build-ready state (don't silently move it — that hides the lifecycle error), post a single lifecycle-repair comment, record the Issue under "Skipped (container)" in the summary, and end the cycle. Do NOT transition to `$CLAIMED`. Keep the comment idempotent — skip posting if an identical `[claude-build-intake]` lifecycle-repair comment already exists on the Issue, so a re-entrant cycle doesn't spam it.
|
|
180
187
|
|
|
181
188
|
Post via `lisa-linear-access operation: save-comment` with:
|
|
182
189
|
|
|
183
190
|
```text
|
|
184
|
-
[claude-build-intake] Not claimed: this Issue
|
|
191
|
+
[claude-build-intake] Not claimed: this Issue sits in the build-ready state ($READY) but is a container with open child work (or a childless Epic), which violates the leaf-only-lifecycle rule. Build-ready is leaf-only per leaf-only-lifecycle — an agent claims and implements leaves, never a container. Repair: move the build-ready role off this parent onto its leaf children (or, for a childless Epic, decompose it into leaf children or reclassify it to a leaf type). A parent's lifecycle state rolls up from its children and is never set to ready directly.
|
|
185
192
|
```
|
|
186
193
|
|
|
187
194
|
This gate never blocks a legitimate flat Task/Bug: those have no open children and a leaf `type:`, so they fall straight through to the claim in 3b.
|
|
188
195
|
|
|
189
196
|
#### 3b. Claim
|
|
190
197
|
|
|
191
|
-
**Rejection detection runs first — before the
|
|
198
|
+
**Rejection detection runs first — before the transition below.** Per the vendor-neutral `rejection-detection` rule (cite the slug; do not restate its classification table), classify this Issue at the **top of 3b, BEFORE** the `$READY → $CLAIMED` transition — afterwards the current-lane signal is gone. Read the Issue's history via `lisa-linear-access operation: history id: <ISSUE-ID>`, keyed on **workflow-state** history, and classify it `rejection-reclaim | forward-only | never-left-ready | unknown` (a `rejection-reclaim` is a move back into `$READY` from a later lane). State names come from `.lisa.config.json`, never hardcoded.
|
|
199
|
+
|
|
200
|
+
> Reading state history is strictly simpler than the label history this used to key on: `IssueHistory` inlines `fromState.name` / `toState.name` on each node, so the transition is read directly with no label-ID resolution against `list-issue-labels` and no reconstruction from `addedLabelIds`/`removedLabelIds` deltas. That reconstruction was lossy — the deltas carry no prior/next full set — which is one more reason the build lane moved to states. A failing/absent history yields `unknown` and the claim proceeds — detection never blocks the build. Issues carrying a learning marker (`[lisa-learning-drop]` / `[lisa-learning-pr]` / `[lisa-learning-upstream-handoff]`) or the `learning:needs-triage` label are never rejection triggers (no learning-about-learning). Carry the classification into the relabel and lifecycle below.
|
|
192
201
|
|
|
193
202
|
**On `rejection-reclaim`, reflect before re-implementing** (per `rejection-detection`): read the rejection evidence through the access layer — the Issue comments posted after the backward transition (the QA rejection comment) via `lisa-linear-access operation: list-comments` and the review threads on the rejected PR — assemble ONE candidate learning (rule, why, provenance linking the rejection comment + rejected PR, evidence links, scope hint, triggering issue, fingerprint `sll4-sha1(rule\ntriggering_issue)[:12]`), and route it to the `lisa-persist-learning` skill. If that skill is absent, record the candidate via `lisa-linear-access operation: save-comment` as a comment carrying a **visible prose line plus** the marker (a bare marker renders as an empty bubble) — `Recorded a candidate learning from this rejection (queued for the judgment gate): <one-line candidate rule>.` then `<!-- [lisa-rejection-candidate] key=<issue>-<transition-ts> -->` — and proceed. Dedupe on `<issue>-<backward-transition-timestamp>` — a second re-claim produces no duplicate. Unreadable/absent evidence → no candidate, still implement.
|
|
194
203
|
|
|
195
204
|
**Claim-time archaeology runs second — after rejection detection, still before the relabel below.** Classify this Issue per the vendor-neutral `claim-archaeology` rule, with the rejection classification above as its input. All shared semantics — ancestry signals, classification, learning-loop exclusion, cost budget, candidate derivation, marker dedupe, and the never-block degrade — live in that one slug; change them there, never here. Linear wiring only: the native relations are already in the read bundle; text-similarity searches run through `lisa-linear-access operation: list-issues` filtered to recently-closed Issues; the fallback candidate comment is posted via `lisa-linear-access operation: save-comment`.
|
|
196
205
|
|
|
197
|
-
|
|
206
|
+
Transition the Issue via `lisa-linear-access operation: save-issue` by setting `stateId` to the `$CLAIMED` state. Resolve state IDs via `list-workflow-states`; a missing `$CLAIMED` state is a setup defect (see the pre-flight check) — never create one here.
|
|
198
207
|
|
|
199
208
|
**Assign to the authenticated user when the Issue is unassigned.** A claim must be attributable. If the Issue has no assignee, set its `assigneeId` to the authenticated viewer (resolve the viewer's id via the Linear MCP identity — e.g. `get_user` for the current actor) through `lisa-linear-access operation: save-issue`. Leave an already-assigned Issue's assignee untouched — never reassign work that already has an owner.
|
|
200
209
|
|
|
201
210
|
Post a `[claude-build-intake]` comment via `save_comment`: `"Claimed by Claude. Starting build."`
|
|
202
211
|
|
|
203
|
-
This is the idempotency lock — a re-entrant cycle's `
|
|
212
|
+
This is the idempotency lock — a re-entrant cycle's `state: $READY` filter will not see this Issue again.
|
|
204
213
|
|
|
205
|
-
If the
|
|
214
|
+
If the transition fails (permission, race), record under "Errors" and skip. **Do not invoke the build flow on an Issue you didn't successfully claim.**
|
|
206
215
|
|
|
207
216
|
#### 3c. Run the per-Issue lifecycle in-session (never as a subagent)
|
|
208
217
|
|
|
@@ -234,8 +243,8 @@ If you are somehow running this skill as a spawned teammate inside an existing t
|
|
|
234
243
|
|
|
235
244
|
The lifecycle run returns one of the following outcomes; resume this scanner with it:
|
|
236
245
|
- **Success** — the build flow completed and a PR exists; evidence posted. The PR may already be **merged** or still **open** (auto-merge enabled, awaiting checks/merge). "Success" means the build work is sound — it does **not** assert the change reached an environment. The env transition in 3d gates on the PR actually being merged; an open PR does not advance the Issue to a `done` env status.
|
|
237
|
-
- **Blocked by linear-verify pre-flight gate** — the pre-flight gate (linear-agent workflow step 2)
|
|
238
|
-
- **Duplicate already fixed** — `lisa-ticket-triage` returned `DUPLICATE_ALREADY_FIXED` with a canonical Issue reference and empirical base-branch evidence. Post the triage finding, ensure the native `duplicates <canonical>` relationship exists when Linear exposes it (otherwise leave an explicit relation/comment reference),
|
|
246
|
+
- **Blocked by linear-verify pre-flight gate** — the pre-flight gate (linear-agent workflow step 2) transitions to `$BLOCKED` and assigns to creator. Let it stand. Record and move on.
|
|
247
|
+
- **Duplicate already fixed** — `lisa-ticket-triage` returned `DUPLICATE_ALREADY_FIXED` with a canonical Issue reference and empirical base-branch evidence. Post the triage finding, ensure the native `duplicates <canonical>` relationship exists when Linear exposes it (otherwise leave an explicit relation/comment reference), move the Issue to the configured canceled-as-duplicate state (falling back to the terminal `$DONE` state only when that is the project's duplicate-close convention), and do not open a PR. If the canonical fix is merged but not yet on the production branch, the close comment must say the production error can recur until the canonical Issue promotes and that recurrence is tracked by the canonical Issue; do not reopen this duplicate for that recurrence.
|
|
239
248
|
- **Blocked by ticket-triage ambiguities** — triage posts findings and the lifecycle stops. The Issue stays at `$CLAIMED`. Surface to human; do not auto-transition. Record under "Errors".
|
|
240
249
|
- **Errored** — exception, missing config, etc. Leave at `$CLAIMED`. Record with exception summary.
|
|
241
250
|
|
|
@@ -246,8 +255,8 @@ Run this only when the returned triage verdict is exactly `DUPLICATE_ALREADY_FIX
|
|
|
246
255
|
1. Verify the structured result includes a canonical Issue reference, the canonical PR/commit, and empirical evidence that the canonical fix is present on the base branch. If any piece is missing, treat the outcome as Held instead of closing.
|
|
247
256
|
2. Post or preserve the triage-finding comment that explains why this Issue is a duplicate and names the canonical Issue.
|
|
248
257
|
3. Ensure a native `duplicates <canonical>` relationship exists when Linear exposes one; if this workspace cannot create that relationship, leave an explicit relation/comment reference and record the limitation in the summary.
|
|
249
|
-
4. Resolve the terminal `$DONE` value exactly as in Phase 3d. For env-keyed workflows, duplicate closeout uses the production/final done
|
|
250
|
-
5.
|
|
258
|
+
4. Resolve the terminal `$DONE` value exactly as in Phase 3d. For env-keyed workflows, duplicate closeout uses the production/final done state, not an intermediate `On Dev`/`On Stg` waypoint.
|
|
259
|
+
5. Move the Issue from `$CLAIMED` to the configured canceled-as-duplicate state. If no duplicate/canceled state is configured, use the terminal `$DONE` state only when that is the project's duplicate-close convention; otherwise record setup as an Error rather than inventing a state.
|
|
251
260
|
6. Post a close comment naming the canonical Issue, PR/commit, and base-branch evidence.
|
|
252
261
|
|
|
253
262
|
If the canonical fix is merged but not yet present on the production branch, append the production-promotion caveat to the close comment: the production error can recur until the canonical Issue promotes, and recurrence is tracked by the canonical Issue rather than by reopening this duplicate.
|
|
@@ -270,23 +279,23 @@ Run this at the end of 3c, after the lifecycle outcome is recorded and before 3d
|
|
|
270
279
|
`confirmLearningEntry` advances ONLY `last_confirmed` — rule text, why, provenance, `first_learned`, and confidence are untouched — and is idempotent within a claim: a repeat same-date bump returns `unchanged`, so an entry that applied repeatedly during one claim is bumped once, not once per application.
|
|
271
280
|
3. **Never block on it.** A failed bump, an unwritable file, or a `not-found` result (the entry was pruned) is recorded under the cycle summary and the claim proceeds — shipping the Issue always outranks confirming a learning about it.
|
|
272
281
|
|
|
273
|
-
#### 3d.
|
|
282
|
+
#### 3d. Transition to $DONE (only after the PR is merged)
|
|
274
283
|
|
|
275
|
-
A `done` env state (`
|
|
284
|
+
A `done` env state (`On Dev`, `On Stg`, or the terminal value) asserts that the code has actually reached that environment. Never set it for a PR that is merely open: auto-merge can be blocked indefinitely (a required rebase / `BEHIND` branch, failing checks, an unaddressed review), and the change may never land. Moving an Issue to `On Stg` on an open PR makes it *claim* a deploy that never happened. Transition only after confirming the PR merged.
|
|
276
285
|
|
|
277
286
|
If the lifecycle run returned Success:
|
|
278
287
|
1. **Confirm the PR merged.** Read the live state of the Issue's PR — `gh pr view <pr> --json state,mergedAt,mergeStateStatus,url`:
|
|
279
|
-
- **Merged** (`state == MERGED`) → proceed to resolve and apply `$DONE` below. Where the env deploy is observable (a deploy workflow run / deployment status keyed to the merged-into branch via `deploy.branches`), confirm it did not fail before
|
|
288
|
+
- **Merged** (`state == MERGED`) → proceed to resolve and apply `$DONE` below. Where the env deploy is observable (a deploy workflow run / deployment status keyed to the merged-into branch via `deploy.branches`), confirm it did not fail before transitioning; a still-running deploy is treated like an open PR (leave at `$CLAIMED`), a failed deploy is recorded as an Error.
|
|
280
289
|
- **Open / not yet merged** → do **not** transition. The build is sound but the change has reached no environment yet. Record the Issue under **"PR open — awaiting merge"** in the summary (with the PR URL and its `mergeStateStatus`), leave it at `$CLAIMED`, and stop. A later `lisa-repair-intake` cycle drives the open PR to merge — re-syncing a `BEHIND` branch so the already-enabled auto-merge can land, or surfacing a real blocker — and, once merged, applies this same env transition. Do **not** comment "Build complete" or change the native state.
|
|
281
290
|
- **Closed without merging** → record an Error (the PR was abandoned unmerged); leave the Issue at `$CLAIMED`.
|
|
282
291
|
2. Resolve `$DONE` for this issue's PR base branch using the Workflow resolution algorithm above. If env can't be resolved and `done` is env-keyed, record an Error and skip this transition — never guess.
|
|
283
292
|
3. Determine whether `$DONE` is the true terminal done value per the `leaf-only-lifecycle` rule's Terminal native closure section:
|
|
284
|
-
- If `linear.
|
|
285
|
-
- If `linear.
|
|
293
|
+
- If `linear.workflow.done` is a string, that state is terminal.
|
|
294
|
+
- If `linear.workflow.done` is an object, only the production/final environment value is terminal (default: `Done`). Intermediate env states such as `On Dev` and `On Stg` are not terminal — they are typed `started`, so the Issue correctly stays open and on the board.
|
|
286
295
|
- If the project uses a different final environment name, resolve it from the configured deployment topology; if ambiguous, record an Error and do not change the native state.
|
|
287
|
-
4.
|
|
288
|
-
5.
|
|
289
|
-
6. Post a `[claude-build-intake]` comment: `"Build complete. PR <URL> merged. Transitioned to $DONE."`
|
|
296
|
+
4. Transition via `lisa-linear-access operation: save-issue`, setting `stateId` to the `$DONE` state (from `$CLAIMED`, or from `$REVIEW` if `lisa-linear-evidence` already moved it forward).
|
|
297
|
+
5. **That single write IS the native closure** when `$DONE` is terminal — there is no second step, because the role and the native state are now the same field. When `$DONE` is an intermediate env (`On Dev` / `On Stg`), the state is typed `started`, so the Issue stays open and active by construction rather than by a compensating repair. If a git automation or magic word has already front-run the Issue into a `completed` state while the resolved env is intermediate, reconcile it back via `lisa-linear-sync` Phase 4b — and treat a recurrence as a setup defect, since `/lisa:setup:linear` offers to remove the `merge → Done` automation that causes it.
|
|
298
|
+
6. Post a `[claude-build-intake]` comment: `"Build complete. PR <URL> merged. Transitioned to $DONE."`
|
|
290
299
|
|
|
291
300
|
For any non-Success outcome, do NOT transition. The Issue sits where the lifecycle left it — humans take it from there.
|
|
292
301
|
|
|
@@ -295,7 +304,7 @@ For any non-Success outcome, do NOT transition. The Issue sits where the lifecyc
|
|
|
295
304
|
Run this **only after a successful `$DONE` transition in 3d**. This is the **forward** arm of the `leaf-only-lifecycle` rule's *"rollup is evaluated whenever a child transitions"* requirement: a leaf reaching `$DONE` may complete its parent Issue, which may in turn complete its Project. Without this step a fully-built parent stays open until the recovery `lisa-repair-intake` cron happens to run.
|
|
296
305
|
|
|
297
306
|
1. Resolve the Issue's parent using the same hierarchy `lisa-linear-read-issue` uses — native parentage: a sub-issue's `parentId`, and Project membership via `projectId` for the Epic-equivalent. If the Issue has no parent, skip — nothing to roll up.
|
|
298
|
-
2. Walk **up the ancestor chain bottom-up** (sub-issue → parent Issue → Project): for each ancestor invoke `lisa-linear-sync <ANCESTOR-ID> --rollup`. That skill derives the ancestor's
|
|
307
|
+
2. Walk **up the ancestor chain bottom-up** (sub-issue → parent Issue → Project): for each ancestor invoke `lisa-linear-sync <ANCESTOR-ID> --rollup`. That skill derives the ancestor's lifecycle **state** from its children per `leaf-only-lifecycle`, applies it via `lisa-linear-access operation: save-issue` only when it differs (never `$READY` — a parent is never rolled into the human-owned ready lane), and because the terminal `$DONE` state is itself typed `completed`, reaching it *is* the native closure. It is idempotent and safe-defaults (suggests, does not guess) when the rolled state is ambiguous.
|
|
299
308
|
3. Stop walking up when an ancestor has no parent, or when `--rollup` reports no change. Record each rolled-up ancestor and its derived state in the summary.
|
|
300
309
|
|
|
301
310
|
This does not re-implement the state machine — it delegates to `lisa-linear-sync --rollup`, the single rollup implementation `lisa-repair-intake` also uses, so the forward and recovery paths can never drift. Children closed **outside** this flow are not observed here; `lisa-repair-intake` remains the recovery net for those.
|
|
@@ -322,7 +331,7 @@ Issues processed: <n>
|
|
|
322
331
|
- <ID> <title> — build-ready on a parent with open child work; lifecycle-repair comment posted
|
|
323
332
|
- Duplicate already fixed (closed as duplicate): <n>
|
|
324
333
|
- <ID> <title> — duplicate of <canonical>; no PR opened
|
|
325
|
-
-
|
|
334
|
+
- $BLOCKED (pre-flight verify failed): <n>
|
|
326
335
|
- <ID> <title> — see Issue comments
|
|
327
336
|
- Held (triage found ambiguities): <n>
|
|
328
337
|
- <ID> <title> — see Issue comments
|
|
@@ -336,31 +345,32 @@ Total PRs opened: <n>
|
|
|
336
345
|
|
|
337
346
|
- **Leaf-only claim gate runs first**: Phase 3a classifies each candidate before any claim; a container with open child work (or a childless Epic) is skipped/safe-blocked, never claimed (the `leaf-only-lifecycle` rule's claim-time arm). The safe-block comment is idempotent — a re-entrant cycle does not re-post it.
|
|
338
347
|
- **Claim-first ordering**: `$CLAIMED` set BEFORE the lifecycle dispatch — no double-pickup.
|
|
339
|
-
- **No writes outside the lifecycle**: this skill only
|
|
348
|
+
- **No writes outside the lifecycle**: this skill only moves the Issue between `$READY`, `$CLAIMED` and `$DONE`. Every label change, and every state change outside those three, is owned by the per-Issue lifecycle or `lisa-linear-evidence`.
|
|
340
349
|
- **Duplicate terminal exception**: `DUPLICATE_ALREADY_FIXED` is the only triage outcome that may close a claimed Issue without a PR from this cycle. It must include a canonical Issue reference and empirical base-branch evidence, and it closes through the configured duplicate/canceled terminal path rather than as completed build work.
|
|
341
|
-
- **Terminal native closure**:
|
|
350
|
+
- **Terminal native closure**: satisfied by the `$DONE` transition itself. Only the production/final `done` state is typed `completed`; intermediate env states are `started` and keep the Issue open.
|
|
342
351
|
- **One item per cycle**: per-Issue exceptions are caught and recorded, then the cycle exits. The scheduler owns retrying or moving on to the next ready item.
|
|
343
352
|
- **Single cycle per team**: do not run two concurrent cycles against the same team — concurrent claims could race.
|
|
344
|
-
- **Single-
|
|
353
|
+
- **Single-lane invariant**: an Issue holds exactly one workflow state, so the old "exactly one `status:*` label" check is now enforced by Linear's data model rather than by this skill. That is a real gain — the two-labels-at-once corruption it guarded against is unrepresentable.
|
|
345
354
|
- **Never pick an arbitrary env for `$DONE`**. If `done` is a map and env is ambiguous, fail loudly.
|
|
346
355
|
|
|
347
356
|
## Adoption (one-time per team)
|
|
348
357
|
|
|
349
|
-
Before this skill can run against a Linear team, the team must
|
|
358
|
+
Before this skill can run against a Linear team, the team must have the build-queue workflow states. Run `/lisa:setup:linear`, which resolves each role and offers to create what is missing — a stock Linear team ships `Todo`, `In Progress`, `In Review` and `Done` but **not** `Blocked`, `On Dev` or `On Stg`.
|
|
350
359
|
|
|
351
|
-
1.
|
|
352
|
-
2.
|
|
360
|
+
1. Ensure states exist for every role in `linear.workflow` (defaults `Todo`, `In Progress`, `In Review`, `Blocked`, `On Dev`, `On Stg`, `Done`). Override any role name in config rather than renaming to match.
|
|
361
|
+
2. Move Issues to `$READY` when they are ready for development.
|
|
353
362
|
3. Reserve `$CLAIMED`, `$REVIEW`, `$DONE` for Lisa — humans should not set them manually except to recover from an error.
|
|
363
|
+
4. Remove the team's `merge → Done` git automation. It is a second writer that jumps an Issue to terminal on a `dev` merge, skipping the env rungs; `/lisa:setup:linear` detects and offers to delete it.
|
|
354
364
|
|
|
355
|
-
If the team hasn't adopted these
|
|
365
|
+
If the team hasn't adopted these states, the first run exits with an adoption hint.
|
|
356
366
|
|
|
357
367
|
## Rules
|
|
358
368
|
|
|
359
|
-
- **Claim leaves only.** Per the `leaf-only-lifecycle` rule, never claim a container — an Issue with open child work, or a childless Epic — even if it
|
|
360
|
-
- Never
|
|
369
|
+
- **Claim leaves only.** Per the `leaf-only-lifecycle` rule, never claim a container — an Issue with open child work, or a childless Epic — even if it sits in the build-ready state. Skip or safe-block it (Phase 3a); never silently implement a container.
|
|
370
|
+
- Never transition an Issue the cycle didn't claim. The `$CLAIMED` transition is the signature of cycle ownership.
|
|
361
371
|
- Never do build work directly from this scanner — the per-Issue lifecycle (the `linear-agent` workflow culminating in `lisa-implement`) owns it. And never spawn that lifecycle as a subagent; run it in-session per Phase 3c so `lisa-implement` can create its agent team.
|
|
362
|
-
- Never auto-transition past `$DONE`. Downstream
|
|
363
|
-
- Never
|
|
364
|
-
- If the Issue has no Validation Journey or no sign-in credentials in its description, the pre-flight verify gate will catch it and
|
|
365
|
-
- On any unexpected outcome from the lifecycle run (
|
|
372
|
+
- Never auto-transition past `$DONE`. Downstream states are owned by QA / product / a future verification-intake skill.
|
|
373
|
+
- Never use a `completed`-typed state for an intermediate env rung. `On Dev` / `On Stg` (or configured equivalents) must be typed `started`; completion happens only at the terminal `done` value.
|
|
374
|
+
- If the Issue has no Validation Journey or no sign-in credentials in its description, the pre-flight verify gate will catch it and transition it to `$BLOCKED` — don't try to fix the Issue from here.
|
|
375
|
+
- On any unexpected outcome from the lifecycle run (a state it doesn't claim, missing PR URL on success, etc.), record as Error and surface — never assume.
|
|
366
376
|
- Never pick an arbitrary env for `$DONE` resolution. If `done` is a map and env is ambiguous, fail loudly.
|
|
@@ -8,7 +8,7 @@ allowed-tools: ["Bash", "Skill", "Read"]
|
|
|
8
8
|
|
|
9
9
|
Claim exactly one canonical Linear identifier such as `ENG-123`. All Linear access goes through `lisa-linear-access`. This is the reusable direct-session counterpart of `lisa-linear-build-intake` Phase 3b.
|
|
10
10
|
|
|
11
|
-
1. Resolve merged `linear.workspace`, `linear.teamKey`, and Linear build-label roles from local-over-global config. Require the identifier's team key to equal the configured team. Resolve ready and claimed roles from config (defaults `
|
|
11
|
+
1. Resolve merged `linear.workspace`, `linear.teamKey`, and Linear build-label roles from local-over-global config. Require the identifier's team key to equal the configured team. Resolve ready and claimed roles from config (defaults `Todo` and `In Progress`); do not hardcode lifecycle decisions.
|
|
12
12
|
2. Invoke `lisa-linear-read-issue <identifier>` immediately before mutation. Reject a completed/canceled issue, active blocker, `repo:<other>` issue, any issue with open sub-Issues, or an Epic per `repo-scope-split` and `leaf-only-lifecycle`.
|
|
13
13
|
3. Inspect live build labels/workflow state:
|
|
14
14
|
- Already claimed, review, environment-done, or another configured later non-terminal role -> preserve it and set `claim_outcome: reused`.
|
|
@@ -12,24 +12,24 @@ Post verification evidence to a Linear Issue and transition it from the configur
|
|
|
12
12
|
|
|
13
13
|
## Workflow resolution
|
|
14
14
|
|
|
15
|
-
The `claimed` and `review` build labels are read from `.lisa.config.json` `linear.
|
|
15
|
+
The `claimed` and `review` build labels are read from `.lisa.config.json` `linear.workflow.*`, falling back to the defaults documented in the `config-resolution` rule (`In Progress` and `In Review`).
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
18
|
read_role() {
|
|
19
19
|
local role="$1" default="$2"
|
|
20
20
|
local local_v global_v
|
|
21
|
-
local_v=$(jq -r ".linear.
|
|
22
|
-
global_v=$(jq -r ".linear.
|
|
21
|
+
local_v=$(jq -r ".linear.workflow.${role} // empty" .lisa.config.local.json 2>/dev/null)
|
|
22
|
+
global_v=$(jq -r ".linear.workflow.${role} // empty" .lisa.config.json 2>/dev/null)
|
|
23
23
|
echo "${local_v:-${global_v:-$default}}"
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
CLAIMED=$(read_role claimed "
|
|
27
|
-
REVIEW=$(read_role review "
|
|
26
|
+
CLAIMED=$(read_role claimed "In Progress")
|
|
27
|
+
REVIEW=$(read_role review "In Review")
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
## Configuration
|
|
31
31
|
|
|
32
|
-
Reads `linear.workspace`, `linear.teamKey`, and `linear.
|
|
32
|
+
Reads `linear.workspace`, `linear.teamKey`, and `linear.workflow.*` from `.lisa.config.json` (with `.local` override).
|
|
33
33
|
|
|
34
34
|
## Inputs (in `<evidence-dir>`)
|
|
35
35
|
|
|
@@ -108,6 +108,6 @@ Return:
|
|
|
108
108
|
## Rules
|
|
109
109
|
|
|
110
110
|
- Never modify the Issue description as part of evidence posting — comments only. Description edits go through `lisa-linear-write-issue`.
|
|
111
|
-
- Never skip the label transition. The build queue is keyed off the configured `linear.
|
|
111
|
+
- Never skip the label transition. The build queue is keyed off the configured `linear.workflow.*` labels; an item that ships without transitioning is invisible to monitoring.
|
|
112
112
|
- If `lisa-linear-access operation: save-comment` fails, retry once. If it fails again, surface the error — don't pretend the comment was posted.
|
|
113
113
|
- Do not delete prior comments. The history is the audit trail.
|
|
@@ -109,7 +109,7 @@ Invoke `lisa-linear-evidence` with `<IDENTIFIER> ./evidence` to:
|
|
|
109
109
|
1. Upload large evidence files to the GitHub `pr-assets` release (if files present in `evidence/files/`).
|
|
110
110
|
2. Update the GitHub PR description with an Evidence section (when a PR is open).
|
|
111
111
|
3. Post the Linear comment (`comment.txt` + collapsible `code-blocks.md`).
|
|
112
|
-
4. Transition labels: remove `
|
|
112
|
+
4. Transition labels: remove `In Progress`, add `In Review`.
|
|
113
113
|
|
|
114
114
|
### Step 6: Verify
|
|
115
115
|
|
|
@@ -144,4 +144,4 @@ The Issue may not have a Validation Journey section. Run `/linear-add-journey <I
|
|
|
144
144
|
|
|
145
145
|
### Label transition fails
|
|
146
146
|
|
|
147
|
-
Ensure `
|
|
147
|
+
Ensure `In Progress` and `In Review` exist on the team. `lisa-linear-evidence` creates them on demand if missing.
|
|
@@ -18,12 +18,12 @@ Callers (planning skills, lifecycle skills) invoke this skill at:
|
|
|
18
18
|
|
|
19
19
|
| Milestone | What to post |
|
|
20
20
|
|-----------|--------------|
|
|
21
|
-
| Plan created | Plan contents (sections + ordered tasks) as a comment, suggest transition `Backlog → Todo` (label: `
|
|
22
|
-
| Implementation in progress | Branch URL + first commit, suggest transition `Todo → In Progress` (label: `
|
|
23
|
-
| PR ready for review | PR URL + summary, the implementation handoff comment, suggest transition `In Progress → In Review` (label: `
|
|
24
|
-
| PR merged | Merge SHA + deploy environment (if known), suggest transition `In Review → Done` (label: `
|
|
21
|
+
| Plan created | Plan contents (sections + ordered tasks) as a comment, suggest transition `Backlog → Todo` (label: `Todo`) |
|
|
22
|
+
| Implementation in progress | Branch URL + first commit, suggest transition `Todo → In Progress` (label: `In Progress`) |
|
|
23
|
+
| PR ready for review | PR URL + summary, the implementation handoff comment, suggest transition `In Progress → In Review` (label: `In Review`) |
|
|
24
|
+
| PR merged | Merge SHA + deploy environment (if known), suggest transition `In Review → Done` (label: `Done`), **then run Phase 4b — mandatory when the merge target is a non-terminal env branch** |
|
|
25
25
|
|
|
26
|
-
This skill **suggests** transitions
|
|
26
|
+
This skill **suggests** transitions and applies them to the native Linear `state` field when the caller asks — the build queue is keyed off states, so the state IS the lane. Without `--update-state` it only suggests; an unasked-for transition remains a human / triage decision.
|
|
27
27
|
|
|
28
28
|
## Input
|
|
29
29
|
|
|
@@ -60,7 +60,7 @@ Sections:
|
|
|
60
60
|
|
|
61
61
|
Tasks: 7 ordered items.
|
|
62
62
|
|
|
63
|
-
Next: implementation begins. Suggested status: **Todo** (label: `
|
|
63
|
+
Next: implementation begins. Suggested status: **Todo** (label: `Todo`).
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
## Phase 3 — Post Comment
|
|
@@ -79,57 +79,61 @@ The PR branch/title/body identifier is the PR -> Linear side. This phase is the
|
|
|
79
79
|
|
|
80
80
|
## Phase 4 — Update Status Label (when caller requests)
|
|
81
81
|
|
|
82
|
-
If the caller passes `--update-
|
|
82
|
+
If the caller passes `--update-state`, set the Issue's `stateId` via `lisa-linear-access operation: save-issue`:
|
|
83
83
|
|
|
84
|
-
- `plan-created` → add `
|
|
85
|
-
- `implementation-in-progress` → remove `
|
|
86
|
-
- `pr-ready` → remove `
|
|
87
|
-
- `pr-merged` → remove `
|
|
84
|
+
- `plan-created` → add `Todo`
|
|
85
|
+
- `implementation-in-progress` → remove `Todo`, add `In Progress`
|
|
86
|
+
- `pr-ready` → remove `In Progress`, add `In Review`
|
|
87
|
+
- `pr-merged` → remove `In Review`, add `Done`
|
|
88
88
|
|
|
89
89
|
If the requested label doesn't exist on the team, create it via `lisa-linear-access operation: create-issue-label`.
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
No single-lane verification is needed: an Issue holds exactly one workflow state by construction, so the two-labels-at-once corruption the old label lane could produce is unrepresentable.
|
|
92
92
|
|
|
93
|
-
Without `--update-
|
|
93
|
+
Without `--update-state`, this skill posts the comment only and does NOT touch labels.
|
|
94
94
|
|
|
95
95
|
## Phase 4b — Reconcile Native Auto-Close (Linear-specific)
|
|
96
96
|
|
|
97
|
-
Linear's
|
|
97
|
+
Linear's per-team **git automations** complete a linked Issue on merge to **any branch** — unlike GitHub's default-branch-scoped `Closes` auto-close — so a magic word (`Closes`/`Fixes`/`Resolves ENG-123`) or branch-name linkage can move the Issue to a `completed` state at a **non-terminal** env merge, front-running the env-keyed ladder.
|
|
98
|
+
|
|
99
|
+
**Since the build lane moved to native states, this phase is a BACKSTOP, not the primary defence.** The lifecycle now writes the same field the automation writes, so the disagreement it repairs can only arise when something outside Lisa moves the Issue. The primary fix is upstream and structural: `/lisa:setup:linear` detects the team's `merge → Done` git automation and offers to delete it, because with this model that automation is a redundant second writer as well as a wrong one. Keep this phase — a workspace can always re-add the automation, and a magic word in a hand-written PR body still fires — but a recurrence here is a **setup defect to report**, not routine repair to absorb silently.
|
|
100
|
+
|
|
101
|
+
Run it whenever the resolved env is **intermediate** (below the production terminal `done`):
|
|
98
102
|
|
|
99
103
|
1. Resolve the merged PR's base branch to its env via `.lisa.config.json` `deploy.branches` (`config-resolution`). If it maps to the production/terminal `done`, this phase is a **no-op** — native completion is correct there.
|
|
100
|
-
2. **Uniform / single-environment no-op.** When the project's env-keyed `done` map is uniform — every environment resolves to the same `
|
|
101
|
-
3. Otherwise (non-uniform map, resolved env intermediate): re-read the Issue's
|
|
102
|
-
4. **Safe default.** If the true terminal cannot be resolved (ambiguous env or unresolvable `done` map), do not change the
|
|
104
|
+
2. **Uniform / single-environment no-op.** When the project's env-keyed `done` map is uniform — every environment resolves to the same `Done`, as in this repo (`production: main` only) — dev-merge == terminal, so native completion is correct. Do nothing. Only a **non-uniform** env→`done` map (distinct `On Dev`/`On Stg`/`Done` rungs) can desync.
|
|
105
|
+
3. Otherwise (non-uniform map, resolved env intermediate): re-read the Issue's `state`. If it sits in a `completed`-typed state while the derived role is a lower env rung, **re-open** the native state by moving it back to the correct env rung (via `lisa-linear-access operation: save-issue`) and post a short `[lisa-linear-sync]` reconciliation comment **naming the likely cause** — a live `merge → Done` git automation, or a magic word in the PR body. This applies the `leaf-only-lifecycle` "Terminal native closure" rule — closure fires **only** at the production terminal `done`. Cite the rule by slug; do not restate it.
|
|
106
|
+
4. **Safe default.** If the true terminal cannot be resolved (ambiguous env or unresolvable `done` map), do not change the `state` — post a `[lisa-linear-sync]` reconciliation-suggestion comment and leave it untouched, mirroring the Phase 5 safe default.
|
|
103
107
|
|
|
104
108
|
## Phase 5 — Parent Status Rollup (`--rollup`)
|
|
105
109
|
|
|
106
|
-
When the caller passes `--rollup`, this skill **derives a parent/container's
|
|
110
|
+
When the caller passes `--rollup`, this skill **derives a parent/container's workflow state from the roll-up of its children** instead of acting on a leaf. A **Project** (the Epic equivalent) rolls up from its Issues; an **Issue** rolls up from its sub-Issues. This implements the Linear child-issue-status arm of the **Parent status rollup (the state machine)** section of the `leaf-only-lifecycle` rule — cite that rule, do not restate the policy.
|
|
107
111
|
|
|
108
|
-
**Resolve the child set the same way `lisa-linear-read-issue` does** — `lisa-linear-access operation: list-issues({project: <id>})` for a Project's Issues, or `lisa-linear-access operation: get-issue` per child for an Issue's sub-Issues (via `parentId`). Capture each child's
|
|
112
|
+
**Resolve the child set the same way `lisa-linear-read-issue` does** — `lisa-linear-access operation: list-issues({project: <id>})` for a Project's Issues, or `lisa-linear-access operation: get-issue` per child for an Issue's sub-Issues (via `parentId`). Capture each child's workflow state. If the item has **no** children it is a leaf — rollup is N/A; behave as a normal milestone sync.
|
|
109
113
|
|
|
110
|
-
**Evaluate the required children over the env ladder `in-progress < dev < staging < production` (the ordered keys of the Linear env-keyed `done` map, e.g. `
|
|
114
|
+
**Evaluate the required children over the env ladder `in-progress < dev < staging < production` (the ordered keys of the Linear env-keyed `done` map, e.g. `On Dev < On Stg < Done`) and take the first match** (canonical roles from `config-resolution`; the Linear state map is `Blocked`, `In Progress`, `In Review`, env-keyed `done`):
|
|
111
115
|
|
|
112
116
|
| If among the required child leaves… | Derived parent role | Linear label |
|
|
113
117
|
|---|---|---|
|
|
114
|
-
| any child carries `
|
|
115
|
-
| else **every** required child has shipped to some env (each at a `done`-map label, e.g. `
|
|
116
|
-
| else any child has **started** (`
|
|
118
|
+
| any child carries `Blocked` | `blocked` | `Blocked` |
|
|
119
|
+
| else **every** required child has shipped to some env (each at a `done`-map label, e.g. `On Dev`/`On Stg`/`Done`) | `done[min-env]` | the **least-advanced** env label among them (all `On Stg` → `On Stg`; mixed dev+staging → `On Dev`; all production → `Done`) |
|
|
120
|
+
| else any child has **started** (`In Progress` / `In Review`, or shipped to an env while a sibling has not) | `claimed` | `In Progress` |
|
|
117
121
|
| else (children exist, none started) | — | unchanged — parent keeps its non-ready container label |
|
|
118
122
|
|
|
119
|
-
- **Blocked dominates** — one blocked child surfaces `
|
|
120
|
-
- **Least-advanced env wins** — the parent reaches an env only when every required child has reached at least that env; it never sits ahead of its laggard child. Native completion (moving the workflow `state` to Done) fires only when the resolved env is the production `
|
|
123
|
+
- **Blocked dominates** — one blocked child surfaces `Blocked` on the parent even while siblings progress.
|
|
124
|
+
- **Least-advanced env wins** — the parent reaches an env only when every required child has reached at least that env; it never sits ahead of its laggard child. Native completion (moving the workflow `state` to Done) fires only when the resolved env is the production `Done`, never at `On Dev`/`On Stg`.
|
|
121
125
|
- **"Required" children only** — won't-do / optional (e.g. `Canceled`) children do not hold the parent open.
|
|
122
126
|
- **Recursive** — a Project reaches an env only when its Issues have themselves rolled up to at least that env. Evaluate bottom-up.
|
|
123
|
-
- **Never set the parent to `
|
|
127
|
+
- **Never set the parent to `Todo`** — `ready` is leaf-only. Rollup only moves the parent between non-ready container labels.
|
|
124
128
|
|
|
125
|
-
**Single-environment collapse (this repo).** The env rungs resolve via the env-keyed `done` logic in `config-resolution`. In this repo `deploy.branches` declares only `production: main`, so `done` collapses to the single `
|
|
129
|
+
**Single-environment collapse (this repo).** The env rungs resolve via the env-keyed `done` logic in `config-resolution`. In this repo `deploy.branches` declares only `production: main`, so `done` collapses to the single `Done` state, the only env rung is production, and the lifecycle is `Todo → In Progress → In Review → Done` with **no** dev/staging promotion hops; the rollup never resolves a dev or staging `done`. Multi-environment projects keep the env-keyed map and roll a parent up to the intermediate env states (`On Dev`/`On Stg`).
|
|
126
130
|
|
|
127
|
-
**Apply the derived
|
|
131
|
+
**Apply the derived state** via `lisa-linear-access operation: save-issue` (Project or Issue), setting the parent's `stateId` to the derived role. Post an idempotent rollup comment naming the derived state and the child tally. Because the terminal `done` state is itself typed `completed`, a parent rolled to terminal is natively closed by the same write — there is no second closure step. **Safe default:** if the derived terminal cannot be resolved (ambiguous required-set or unresolvable env `done`), do not guess — post the derived suggestion as a comment and leave the parent's state untouched.
|
|
128
132
|
|
|
129
133
|
## Rules
|
|
130
134
|
|
|
131
|
-
- Never auto-transition the native Linear `state` — only the label, and only when the caller explicitly asks (`--update-
|
|
132
|
-
- Rollup derives a *parent's*
|
|
135
|
+
- Never auto-transition the native Linear `state` — only the label, and only when the caller explicitly asks (`--update-state`, or `--rollup` for parent derivation per the `leaf-only-lifecycle` rule).
|
|
136
|
+
- Rollup derives a *parent's* workflow state from its children and never rolls a parent into the human-owned ready lane (never `$READY`). It cites the `leaf-only-lifecycle` rule by slug rather than restating the state machine.
|
|
133
137
|
- Never post empty or minimal comments — if a milestone has no meaningful content, skip the post.
|
|
134
138
|
- Do not delete prior milestone comments. They are the audit trail.
|
|
135
139
|
- If `save_comment` fails, retry once. If it fails again, surface the error.
|