@codyswann/lisa 2.61.0 → 2.62.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/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/agents/confluence-prd-intake.md +1 -1
- package/plugins/lisa/agents/github-agent.md +4 -5
- package/plugins/lisa/agents/github-build-intake.md +1 -1
- package/plugins/lisa/agents/github-prd-intake.md +1 -1
- package/plugins/lisa/agents/linear-prd-intake.md +1 -1
- package/plugins/lisa/agents/notion-prd-intake.md +1 -1
- package/plugins/lisa/commands/intake.md +1 -1
- package/plugins/lisa/commands/project-ideation.md +3 -3
- package/plugins/lisa/commands/repair-intake.md +6 -0
- package/plugins/lisa/commands/research.md +3 -3
- package/plugins/lisa/commands/setup-automations.md +6 -0
- package/plugins/lisa/commands/tear-down-automations.md +6 -0
- package/plugins/lisa/commands/verify-prd.md +2 -2
- package/plugins/lisa/rules/config-resolution.md +63 -4
- package/plugins/lisa/rules/intent-routing.md +4 -3
- package/plugins/lisa/rules/leaf-only-lifecycle.md +1 -1
- package/plugins/lisa/rules/prd-lifecycle-rollup.md +10 -2
- package/plugins/lisa/rules/repo-scope-split.md +18 -1
- package/plugins/lisa/skills/confluence-prd-intake/SKILL.md +24 -14
- package/plugins/lisa/skills/confluence-prd-intake/agents/openai.yaml +2 -2
- package/plugins/lisa/skills/confluence-write-prd/SKILL.md +103 -0
- package/plugins/lisa/skills/confluence-write-prd/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/github-build-intake/SKILL.md +32 -21
- package/plugins/lisa/skills/github-evidence/SKILL.md +3 -26
- package/plugins/lisa/skills/github-evidence/agents/openai.yaml +2 -2
- package/plugins/lisa/skills/github-journey/SKILL.md +2 -2
- package/plugins/lisa/skills/github-prd-intake/SKILL.md +25 -11
- package/plugins/lisa/skills/github-prd-intake/agents/openai.yaml +2 -2
- package/plugins/lisa/skills/github-sync/SKILL.md +5 -5
- package/plugins/lisa/skills/github-write-issue/SKILL.md +15 -6
- package/plugins/lisa/skills/github-write-prd/SKILL.md +100 -0
- package/plugins/lisa/skills/github-write-prd/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/implement/SKILL.md +13 -6
- package/plugins/lisa/skills/intake/SKILL.md +13 -12
- package/plugins/lisa/skills/intake/agents/openai.yaml +2 -2
- package/plugins/lisa/skills/jira-build-intake/SKILL.md +24 -11
- package/plugins/lisa/skills/jira-write-ticket/SKILL.md +8 -0
- package/plugins/lisa/skills/linear-build-intake/SKILL.md +22 -9
- package/plugins/lisa/skills/linear-prd-intake/SKILL.md +23 -13
- package/plugins/lisa/skills/linear-prd-intake/agents/openai.yaml +2 -2
- package/plugins/lisa/skills/linear-write-issue/SKILL.md +10 -2
- package/plugins/lisa/skills/linear-write-prd/SKILL.md +90 -0
- package/plugins/lisa/skills/linear-write-prd/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/notion-access/SKILL.md +2 -0
- package/plugins/lisa/skills/notion-prd-intake/SKILL.md +22 -12
- package/plugins/lisa/skills/notion-prd-intake/agents/openai.yaml +2 -2
- package/plugins/lisa/skills/notion-write-prd/SKILL.md +107 -0
- package/plugins/lisa/skills/notion-write-prd/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/prd-source-write/SKILL.md +80 -0
- package/plugins/lisa/skills/prd-source-write/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/project-ideation/SKILL.md +183 -80
- package/plugins/lisa/skills/repair-intake/SKILL.md +403 -0
- package/plugins/lisa/skills/repair-intake/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/research/SKILL.md +19 -3
- package/plugins/lisa/skills/research/agents/openai.yaml +2 -2
- package/plugins/lisa/skills/setup-automations/SKILL.md +78 -0
- package/plugins/lisa/skills/setup-automations/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/setup-github/SKILL.md +0 -1
- package/plugins/lisa/skills/tear-down-automations/SKILL.md +34 -0
- package/plugins/lisa/skills/tear-down-automations/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/tracker-build-intake/SKILL.md +6 -2
- package/plugins/lisa/skills/tracker-build-intake/agents/openai.yaml +2 -2
- package/plugins/lisa/skills/tracker-evidence/SKILL.md +2 -2
- package/plugins/lisa/skills/tracker-sync/SKILL.md +1 -1
- package/plugins/lisa/skills/verify-prd/SKILL.md +41 -38
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/commands/exploratory-qa.md +3 -3
- package/plugins/lisa-expo/skills/exploratory-qa/SKILL.md +48 -18
- package/plugins/lisa-expo/skills/exploratory-qa/agents/openai.yaml +2 -2
- 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/commands/exploratory-qa.md +3 -3
- package/plugins/lisa-harper-fabric/skills/exploratory-qa/SKILL.md +48 -18
- package/plugins/lisa-harper-fabric/skills/exploratory-qa/agents/openai.yaml +2 -2
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-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-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/commands/exploratory-qa.md +3 -3
- package/plugins/lisa-rails/skills/exploratory-qa/SKILL.md +48 -18
- package/plugins/lisa-rails/skills/exploratory-qa/agents/openai.yaml +2 -2
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-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/skills/lisa-wiki-ingest/SKILL.md +30 -1
- package/plugins/src/base/agents/confluence-prd-intake.md +1 -1
- package/plugins/src/base/agents/github-agent.md +4 -5
- package/plugins/src/base/agents/github-build-intake.md +1 -1
- package/plugins/src/base/agents/github-prd-intake.md +1 -1
- package/plugins/src/base/agents/linear-prd-intake.md +1 -1
- package/plugins/src/base/agents/notion-prd-intake.md +1 -1
- package/plugins/src/base/commands/intake.md +1 -1
- package/plugins/src/base/commands/project-ideation.md +3 -3
- package/plugins/src/base/commands/repair-intake.md +6 -0
- package/plugins/src/base/commands/research.md +3 -3
- package/plugins/src/base/commands/setup-automations.md +6 -0
- package/plugins/src/base/commands/tear-down-automations.md +6 -0
- package/plugins/src/base/commands/verify-prd.md +2 -2
- package/plugins/src/base/rules/config-resolution.md +63 -4
- package/plugins/src/base/rules/intent-routing.md +4 -3
- package/plugins/src/base/rules/leaf-only-lifecycle.md +1 -1
- package/plugins/src/base/rules/prd-lifecycle-rollup.md +10 -2
- package/plugins/src/base/rules/repo-scope-split.md +18 -1
- package/plugins/src/base/skills/confluence-prd-intake/SKILL.md +24 -14
- package/plugins/src/base/skills/confluence-write-prd/SKILL.md +103 -0
- package/plugins/src/base/skills/github-build-intake/SKILL.md +32 -21
- package/plugins/src/base/skills/github-evidence/SKILL.md +3 -26
- package/plugins/src/base/skills/github-journey/SKILL.md +2 -2
- package/plugins/src/base/skills/github-prd-intake/SKILL.md +25 -11
- package/plugins/src/base/skills/github-sync/SKILL.md +5 -5
- package/plugins/src/base/skills/github-write-issue/SKILL.md +15 -6
- package/plugins/src/base/skills/github-write-prd/SKILL.md +100 -0
- package/plugins/src/base/skills/implement/SKILL.md +13 -6
- package/plugins/src/base/skills/intake/SKILL.md +13 -12
- package/plugins/src/base/skills/jira-build-intake/SKILL.md +24 -11
- package/plugins/src/base/skills/jira-write-ticket/SKILL.md +8 -0
- package/plugins/src/base/skills/linear-build-intake/SKILL.md +22 -9
- package/plugins/src/base/skills/linear-prd-intake/SKILL.md +23 -13
- package/plugins/src/base/skills/linear-write-issue/SKILL.md +10 -2
- package/plugins/src/base/skills/linear-write-prd/SKILL.md +90 -0
- package/plugins/src/base/skills/notion-access/SKILL.md +2 -0
- package/plugins/src/base/skills/notion-prd-intake/SKILL.md +22 -12
- package/plugins/src/base/skills/notion-write-prd/SKILL.md +107 -0
- package/plugins/src/base/skills/prd-source-write/SKILL.md +80 -0
- package/plugins/src/base/skills/project-ideation/SKILL.md +183 -80
- package/plugins/src/base/skills/repair-intake/SKILL.md +403 -0
- package/plugins/src/base/skills/research/SKILL.md +19 -3
- package/plugins/src/base/skills/setup-automations/SKILL.md +78 -0
- package/plugins/src/base/skills/setup-github/SKILL.md +0 -1
- package/plugins/src/base/skills/tear-down-automations/SKILL.md +34 -0
- package/plugins/src/base/skills/tracker-build-intake/SKILL.md +6 -2
- package/plugins/src/base/skills/tracker-evidence/SKILL.md +2 -2
- package/plugins/src/base/skills/tracker-sync/SKILL.md +1 -1
- package/plugins/src/base/skills/verify-prd/SKILL.md +41 -38
- package/plugins/src/expo/commands/exploratory-qa.md +3 -3
- package/plugins/src/expo/skills/exploratory-qa/SKILL.md +48 -18
- package/plugins/src/harper-fabric/commands/exploratory-qa.md +3 -3
- package/plugins/src/harper-fabric/skills/exploratory-qa/SKILL.md +48 -18
- package/plugins/src/rails/commands/exploratory-qa.md +3 -3
- package/plugins/src/rails/skills/exploratory-qa/SKILL.md +48 -18
- package/plugins/src/wiki/skills/lisa-wiki-ingest/SKILL.md +30 -1
|
@@ -81,7 +81,7 @@ Notes:
|
|
|
81
81
|
|
|
82
82
|
The terminal rollup state is whatever the project configures for `done` — which is **env-keyed** (`config-resolution` "Env-keyed `done`"): a `done` map keyed by environment (`dev`, `staging`, `production`), resolved from the merged PR's base branch. This rule does **not** hardcode a `dev → staging → prod` promotion chain as required — that is a project-specific deploy topology. A downstream project with dev/staging/prod environments rolls a parent up to whichever terminal `done` value matches the environment its leaves shipped to. The rule stays generic and multi-env capable.
|
|
83
83
|
|
|
84
|
-
**Single-environment collapse (this repo).** Lisa's own deploy has only `main`/`production` (no dev/staging), so `done` is a single value, not a map,
|
|
84
|
+
**Single-environment collapse (this repo).** Lisa's own deploy has only `main`/`production` (no dev/staging), so `done` is a single value, not a map. For GitHub, the build lifecycle collapses to one chain: `ready → claimed (in-progress) → done`. The rollup terminal state is simply `done`. This is the *collapsed* case of the generic rule, not a different rule — projects with more environments keep the env-keyed map.
|
|
85
85
|
|
|
86
86
|
## Terminal native closure
|
|
87
87
|
|
|
@@ -108,9 +108,17 @@ The PRD never advances to `shipped` on its own authority — it is **derived** f
|
|
|
108
108
|
**`/lisa:verify` (one work item) vs `/lisa:verify-prd` (the whole initiative).** These are deliberately separate scopes:
|
|
109
109
|
|
|
110
110
|
- **`/lisa:verify`** empirically verifies a **single work item** (a ticket / story / sub-task) in its target environment as part of that item's Build/Fix/Improve flow. It is what drives a build ticket to its `done` state; it operates at the *leaf/build* level (see `leaf-only-lifecycle`). It does **not** read the PRD or judge initiative-level acceptance, and it is **not** replaced by PRD verification.
|
|
111
|
-
- **`/lisa:verify-prd`** is the **initiative-level acceptance gate**. It runs *after* the PRD is `shipped` (all generated top-level work terminal), reads the PRD and its generated child set, confirms the children are terminal, then runs spec-conformance against the original PRD requirements plus empirical verification of the shipped surface. On pass it transitions the PRD `shipped → verified` and posts evidence; on fail it
|
|
111
|
+
- **`/lisa:verify-prd`** is the **initiative-level acceptance gate**. It runs *after* the PRD is `shipped` (all generated top-level work terminal), reads the PRD and its generated child set, confirms the children are terminal, then runs spec-conformance against the original PRD requirements plus empirical verification of the shipped surface. On pass it transitions the PRD `shipped → verified` and posts evidence; on fail it **re-opens the PRD `shipped → ticketed`** (never `blocked`), creates **build-ready fix tickets** for the missing or incorrect behavior (registered as the PRD's generated work) and posts a product-readable failure report — the fix tickets auto-build, rollup re-ships the PRD once they are terminal, and a later cycle re-verifies (self-healing; see "Closing the loop" below). Re-runs are idempotent (no duplicate evidence, fix tickets, or lifecycle transitions).
|
|
112
112
|
|
|
113
|
-
**No extra failure states
|
|
113
|
+
**No extra failure states, and verification never uses `blocked`.** A failed PRD verification does **not** move the PRD to `blocked`; it re-opens the PRD `shipped → ticketed` and creates build-ready fix tickets (see "Closing the loop"), forming a self-healing loop rather than parking the PRD for a human. It introduces no `prd-verifying` or `prd-verification-failed` state — the lifecycle stays intentionally small, and `blocked` remains the *intake* (ready-stage validation) failure state, not the verification one. `verified` is terminal and product-owned (like `draft` and `shipped`); a PRD is never moved to `verified` solely because its tickets are closed, and a PRD is never closed/archived before verification has passed. The vendor maps for the `verified` role (`prd-verified` label for GitHub/Linear, `Verified` status for Notion, the verified parent page for Confluence) live in the `config-resolution` rule.
|
|
114
|
+
|
|
115
|
+
### Closing the loop: PRD intake dispatches `/lisa:verify-prd` for shipped PRDs
|
|
116
|
+
|
|
117
|
+
`shipped → verified` does not happen on its own — something has to run the acceptance gate. The PRD-intake scanners close that loop: in addition to the `ticketed → shipped` rollup, each PRD-intake cycle **dispatches `/lisa:verify-prd` for a shipped PRD** so a shipped PRD does not sit unverified forever. This is a **dispatch**, not a transition — the intake scanner still never *sets* the verification outcome itself; `/lisa:verify-prd` (which it invokes) performs the `shipped → verified` (pass) or, on fail, the `shipped → ticketed` re-open (it **never** uses `blocked`), with its own guard, evidence, and build-ready fix-ticket creation.
|
|
118
|
+
|
|
119
|
+
**The self-healing FAIL loop.** When verify-prd fails it re-opens the PRD `shipped → ticketed` and creates **build-ready** fix tickets (registered as the PRD's generated work). Because the fix tickets are build-ready they are auto-claimed by the build queue with no human promotion; once they reach terminal, the `ticketed → shipped` rollup (Phase 3f) re-ships the PRD, and the next cycle's verify dispatch (Phase 3g) re-verifies. PASS ends at `verified`; FAIL starts another round. The loop **never auto-halts** (the failure report carries a verification-round count for human visibility) and **never** parks the PRD in `blocked`.
|
|
120
|
+
|
|
121
|
+
Bounded, like the ready claim: `/lisa:verify-prd` is a heavy full flow (spec-conformance + empirical verification + fix-ticket creation), so a scanner verifies **one shipped PRD per cycle** and lets the scheduler drain the rest — the same one-item-per-cycle discipline the `ready` claim uses. After verify-prd runs, the PRD leaves `shipped` (to `verified` on pass, or `ticketed` on fail), so it is not re-picked by the shipped query that cycle; a PRD whose generated work is not actually terminal is guard-stopped by verify-prd and left `shipped` (verify-prd's gate, not the scanner's). A PRD that the project chose to close on ship (`*.rollup.closeOnShipped = true`) is out of the open verification queue — closing on ship is an explicit opt-out of the open loop. This dispatch is **behaviorally identical across all four PRD-intake skills** (the `github` / `linear` / `notion` / `confluence` `*-prd-intake` Phase 3g); only the `shipped`-role query surface differs.
|
|
114
122
|
|
|
115
123
|
## Idempotency dedupe key
|
|
116
124
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Leaf work units are single-repo. A **leaf work unit** is an individually implementable ticket with no child tickets — issue types **Bug, Task, Sub-task, Improvement**. Each must name exactly one repository. **Epic, Story, Spike** are coordination containers and may span repos.
|
|
4
4
|
|
|
5
|
-
This invariant is enforced at
|
|
5
|
+
This invariant is enforced at four points: gate **S10** in the `*-validate-*` skills (write time), `task-decomposition` step 1.5 (PRD-decomposition time), **claim-time repo scoping** in the build-intake skills (when intake decides whether to claim a ready ticket for the current repo — see below), and the work-time split procedure below (when an agent picks up an existing ticket to implement it).
|
|
6
6
|
|
|
7
7
|
## Two splitting strategies, by phase
|
|
8
8
|
|
|
@@ -32,6 +32,23 @@ Auto-split only when the split is unambiguous. Fall back to the standard BLOCK +
|
|
|
32
32
|
- Splitting would strand stakeholder context that only the reporter can re-scope (e.g. the acceptance criteria describe a single indivisible cross-repo behavior with no clean per-repo boundary).
|
|
33
33
|
- The metadata required to clone (parent, environment, credentials) is itself missing — block on the missing metadata first; do not propagate gaps into the siblings.
|
|
34
34
|
|
|
35
|
+
## Claim-time repo scoping (build-intake)
|
|
36
|
+
|
|
37
|
+
A ticketing system can oversee multiple repos (one JIRA project / Linear team for `frontend`, `backend`, `infrastructure`). When `lisa:*-build-intake` runs inside one repo, it claims only tickets for **that** repo — it never pulls a ready ticket meant for a sibling repo. This is the fourth enforcement point of the single-repo-leaf invariant; it runs in each vendor build-intake's claim gate (Phase 3a), **before** the leaf-only container gate and the claim.
|
|
38
|
+
|
|
39
|
+
Resolve the current repo per the `config-resolution` "Repo scoping" section (config `repo` → `github.repo` → git remote basename; stop with a clear error if unresolvable). Then walk the ready candidates in priority order and apply the **repo-scope decision** to each before claiming:
|
|
40
|
+
|
|
41
|
+
1. **Read the candidate's repo marker** — the `repo:<name>` label (JIRA: also a component equal to a repo name).
|
|
42
|
+
- **Labeled for another repo** → **skip** cheaply (do not claim, do not re-determine); it stays `ready` for that repo's own intake. Move to the next candidate.
|
|
43
|
+
- **Labeled for the current repo** → proceed to the leaf-only gate + claim (the cheap, common path once labels exist).
|
|
44
|
+
- **Unlabeled** → **determine** the target repo(s) from the ticket (description, acceptance criteria, technical approach) confirmed against the actual code surfaces the change requires — the same detection as step 1 of the work-time split. Then **stamp** the resolved `repo:<name>` label(s) so future cycles filter cheaply, and re-apply this decision with the now-known repo.
|
|
45
|
+
2. **Multi-repo leaf → split, never claim.** If determination finds the leaf touches more than one repo, run the **work-time split procedure** below to break it into single-repo siblings — each created **build-ready** (`build_ready: true`, so the build queue auto-claims it) and stamped with its own `repo:<name>`. After the split, the current repo's sibling (if any) becomes a normal current-repo candidate; the others are separate single-repo `ready` leaves for their repos. A multi-repo leaf is never claimed as-is.
|
|
46
|
+
3. **Wrong repo → skip.** A single-repo leaf whose `repo:<name>` ≠ the current repo is left `ready` (and labeled) and skipped; intake moves on until it finds a claimable current-repo leaf, then stops (one item per cycle).
|
|
47
|
+
|
|
48
|
+
**Cost.** Only **unlabeled** candidates need content determination; once stamped, wrong-repo candidates are skipped by label alone. Prefer candidates already labeled `repo:<current>` first (cheap claim), falling through to unlabeled candidates (determine + stamp) only when no pre-labeled current-repo leaf is ready.
|
|
49
|
+
|
|
50
|
+
A container (Epic/Story/Spike) is handled by the leaf-only gate, not here — containers may span repos and are never claimed/built directly.
|
|
51
|
+
|
|
35
52
|
## Vendor mechanics
|
|
36
53
|
|
|
37
54
|
The procedure is vendor-neutral; the create + link + edit mechanics differ:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: confluence-prd-intake
|
|
3
|
-
description: "Scans a Confluence space (or a parent page) for PRD pages currently parented under the configured `ready` lifecycle page and runs
|
|
3
|
+
description: "Scans a Confluence space (or a parent page) for PRD pages currently parented under the configured `ready` lifecycle page and runs the first eligible one through the dry-run validation pipeline. A PRD that passes every gate gets tickets written and is re-parented under the `ticketed` lifecycle page; a PRD that fails gets clarifying-question comments and is re-parented under the `blocked` lifecycle page. Confluence counterpart of `lisa:notion-prd-intake` — the workflow is identical; only the source-of-truth tools and the state encoding differ (parent-page re-parenting instead of a status property). Composes existing skills (confluence-to-tracker, tracker-validate, tracker-source-artifacts, product-walkthrough)."
|
|
4
4
|
allowed-tools: ["Skill", "Bash"]
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -11,10 +11,10 @@ All Atlassian operations in this skill go through `lisa:atlassian-access`. Do no
|
|
|
11
11
|
`$ARGUMENTS` is one of:
|
|
12
12
|
|
|
13
13
|
- A Confluence **space** URL or space key — used as the surrounding scope sanity check. Example: `https://mycompany.atlassian.net/wiki/spaces/PRD` or `PRD`.
|
|
14
|
-
- A Confluence **parent page** URL or page ID — overrides the configured `confluence.parents.ready` page for this run (useful when an operator wants to
|
|
14
|
+
- A Confluence **parent page** URL or page ID — overrides the configured `confluence.parents.ready` page for this run (useful when an operator wants to target an alternative lifecycle parent). Example: `https://mycompany.atlassian.net/wiki/spaces/PRD/pages/123456789/PRDs`.
|
|
15
15
|
- Empty — use `confluence.parents.ready` from `.lisa.config.json` as the scope.
|
|
16
16
|
|
|
17
|
-
Run one intake cycle against the resolved `ready` lifecycle parent. Each direct child of that parent is treated as a PRD currently in the `ready` state.
|
|
17
|
+
Run one intake cycle against the resolved `ready` lifecycle parent. Each direct child of that parent is treated as a PRD currently in the `ready` state. The first eligible PRD is claimed (re-parented to `in_review`), validated, routed to either the `blocked` parent (with clarifying comments) or the `ticketed` parent (with destination tickets created), then the cycle exits. Remaining ready PRDs stay queued for later scheduler invocations.
|
|
18
18
|
|
|
19
19
|
## Why parent pages, not labels
|
|
20
20
|
|
|
@@ -82,7 +82,7 @@ The **PRD closure rollup phase (3f)** re-parents a `ticketed` PRD to the `shippe
|
|
|
82
82
|
|
|
83
83
|
## Confirmation policy
|
|
84
84
|
|
|
85
|
-
Do NOT ask the caller whether to proceed. Once invoked, run the cycle to completion — claim, validate, branch to the `blocked` or `ticketed` parent, write the summary. The caller (a human or a cron) has already authorized the run by invoking the skill; re-prompting defeats the purpose of a background
|
|
85
|
+
Do NOT ask the caller whether to proceed. Once invoked, run the cycle to completion for the first eligible PRD — claim, validate, branch to the `blocked` or `ticketed` parent, write the summary, and exit. The caller (a human or a cron) has already authorized the run by invoking the skill; re-prompting defeats the purpose of a background queue.
|
|
86
86
|
|
|
87
87
|
Specifically forbidden:
|
|
88
88
|
|
|
@@ -108,7 +108,7 @@ draft → ready → in_review → blocked | ticketed → shipped → verified
|
|
|
108
108
|
|
|
109
109
|
(Each role corresponds to a dedicated parent page; the `verified` parent is resolved from `confluence.parents.verified`.)
|
|
110
110
|
|
|
111
|
-
`verified` is the terminal state after `shipped`: it means the shipped product has been empirically checked against the PRD (set by `/lisa:verify-prd`, not by this intake skill). A failed post-ship verification re-parents
|
|
111
|
+
`verified` is the terminal state after `shipped`: it means the shipped product has been empirically checked against the PRD (set by `/lisa:verify-prd`, not by this intake skill). A failed post-ship verification does **not** use `blocked`; `/lisa:verify-prd` re-parents the PRD `shipped → ticketed` and creates build-ready fix tickets that auto-build and trigger a re-verify (the self-healing loop), introducing no `verifying` / `verification-failed` parent. Like `draft` and `shipped`, `verified` is **product-owned** — this intake skill never re-parents a PRD into or out of the `verified` parent. See the "PRD-level verification vs ticket verification" section of the `prd-lifecycle-rollup` rule.
|
|
112
112
|
|
|
113
113
|
A PRD's current state is determined entirely by which lifecycle parent it sits under. Re-parenting is the transition.
|
|
114
114
|
|
|
@@ -151,9 +151,9 @@ If the result set is empty, run a sanity probe across the other lifecycle parent
|
|
|
151
151
|
- If every lifecycle parent has zero direct children → the lifecycle scaffolding is provisioned but unused, OR PRDs are still parented under the legacy structure. Surface: `"No PRDs found under any of the configured lifecycle parent pages (ready, in_review, blocked, ticketed). If this is a new project, move PRDs that are ready for ticketing under the configured 'ready' parent page (see Adoption section)."` Exit with an error — this is a setup / migration issue, not a normal idle cycle.
|
|
152
152
|
- If any non-ready parent has children → the queue is genuinely empty (PRDs exist but are in `in_review`, `blocked`, `ticketed`, or `shipped`). Exit cleanly with `"No PRDs currently parented under the 'ready' lifecycle page. Nothing to do."`
|
|
153
153
|
|
|
154
|
-
### Phase 3 — Process
|
|
154
|
+
### Phase 3 — Process the first eligible ready PRD
|
|
155
155
|
|
|
156
|
-
|
|
156
|
+
Select the first ready PRD page returned by Phase 2 and process only that page. Later scheduler invocations process the remaining ready PRDs.
|
|
157
157
|
|
|
158
158
|
#### 3a. Claim
|
|
159
159
|
|
|
@@ -277,9 +277,9 @@ Use these exact badge labels — they are the validator's category values transl
|
|
|
277
277
|
|
|
278
278
|
After all comments are posted (anchored groups + the optional footer summary), re-parent the PRD to the `blocked` lifecycle parent: `transition_prd "$PRD_ID" blocked`. Do NOT write any destination tickets.
|
|
279
279
|
|
|
280
|
-
#### 3d.
|
|
280
|
+
#### 3d. Stop
|
|
281
281
|
|
|
282
|
-
|
|
282
|
+
Stop immediately after the claimed PRD is ticketed, blocked, or recorded as an error.
|
|
283
283
|
|
|
284
284
|
#### 3e. Coverage audit (mandatory after re-parenting to `ticketed`)
|
|
285
285
|
|
|
@@ -290,7 +290,7 @@ Per-ticket gates prove each ticket is well-formed; they do NOT prove the *set* o
|
|
|
290
290
|
|
|
291
291
|
| Verdict | Action |
|
|
292
292
|
|---------|--------|
|
|
293
|
-
| `COMPLETE` | Done. Leave PRD under `ticketed` parent.
|
|
293
|
+
| `COMPLETE` | Done. Leave PRD under `ticketed` parent. End the cycle. |
|
|
294
294
|
| `COMPLETE_WITH_SCOPE_CREEP` | Post an advisory footer comment naming the scope-creep tickets (so product can decide whether to close them as out-of-scope). Leave PRD under `ticketed` parent. |
|
|
295
295
|
| `GAPS_FOUND` | The created ticket set is incomplete. (a) For each gap, post a comment using the same product-facing template as Phase 3c.3 — inline-anchored when `prd_anchor` is non-null, footer otherwise; category badge from the gap's `category` field; `What's unclear` and `Recommendation` from the audit report's `what` and `recommendation` fields. Apply the same forbidden-language rules from Phase 3c.5. (b) Post one footer summary comment listing the tickets that *were* successfully created (so product knows what to keep vs. what to extend). (c) Re-parent the PRD from `ticketed` back to `blocked`: `transition_prd "$PRD_ID" blocked`. |
|
|
296
296
|
| `NO_TICKETS_FOUND` | Should not happen if step 2 succeeded. If it does, log it as an Error in the cycle summary and leave the PRD under `ticketed` with a comment flagging the audit failure for human review. |
|
|
@@ -352,9 +352,19 @@ The set of **required** children for the all-terminal check is the top-level chi
|
|
|
352
352
|
|
|
353
353
|
This phase implements exactly one PRD-lifecycle hop — `ticketed → shipped` — and the optional config-gated archive that follows it. All terminal-state semantics, the generated-top-level-work boundary, and the dedupe-by-child-ref idempotency come from the `prd-lifecycle-rollup` rule; this skill is its Confluence implementation, not a second source of truth.
|
|
354
354
|
|
|
355
|
+
#### 3g. PRD verification dispatch (close the loop on shipped PRDs)
|
|
356
|
+
|
|
357
|
+
`shipped` and `verified` are distinct facts about a PRD (see the `prd-lifecycle-rollup` rule's "PRD-level verification vs ticket verification" and "Closing the loop" sections). Rollup (3f) only reaches the shipped parent; the `shipped → verified` (pass) / `shipped → ticketed` (fail) hops are owned by `/lisa:verify-prd`. This phase **closes that loop** by dispatching the initiative-level acceptance gate for shipped PRDs. It never performs the verification transition itself — the "never sets the verification outcome" invariant holds: `lisa:verify-prd`, not this skill, sets `verified` (or, on failure, re-opens the PRD to `ticketed`).
|
|
358
|
+
|
|
359
|
+
Re-query the PRDs currently parented under the shipped lifecycle parent via `lisa:atlassian-access` `operation: read-page-descendants id: <confluence.parents.shipped>`. Pick the **first** one and invoke `lisa:verify-prd <page-url>`. Process **one shipped PRD per cycle** — `lisa:verify-prd` is a heavy full flow (spec-conformance + empirical verification + fix-issue creation), so it is bounded exactly like the single-ready-PRD claim in Phase 3; the scheduler drains the rest.
|
|
360
|
+
|
|
361
|
+
**Per-cycle combined bound:** each scheduler cycle dispatches at most one ready PRD (the Phase 3 single-ready-PRD claim) **and** at most one shipped PRD for verification (this Phase 3g dispatch), for a maximum of two PRD operations per cycle. Ready intake runs first (Phase 3), then shipped verify (Phase 3g).
|
|
362
|
+
|
|
363
|
+
`lisa:verify-prd` owns the outcome: on a CONFORMS verdict with all empirical checks passing it transitions the PRD to the verified parent and posts evidence; on a conformance miss or a failing/unavailable check it **re-parents the PRD shipped → ticketed** (never the blocked parent) and creates **build-ready** fix tickets registered as the PRD's generated work, then posts a failure report — the fix tickets auto-build, rollup (3f) re-ships the PRD once they are terminal, and a later cycle re-verifies (the self-healing loop). Either branch moves the PRD out of the shipped parent, so it is not re-picked this cycle; a PRD whose generated work is not actually terminal is guard-stopped by `lisa:verify-prd` (left under shipped) — that is verify-prd's gate, not this skill's. A PRD archived on ship (`confluence.rollup.closeOnShipped = true`) is out of the open verification queue. This phase, like 3f, is **behaviorally identical across all four intake skills** (`github-prd-intake`, `linear-prd-intake`, `notion-prd-intake`, `confluence-prd-intake`) — only the `$SHIPPED` query surface differs; keep them aligned. Record the dispatched PRD + verify-prd's verdict in the summary.
|
|
364
|
+
|
|
355
365
|
### Phase 4 — Summary report
|
|
356
366
|
|
|
357
|
-
After processing
|
|
367
|
+
After processing the single selected PRD, emit a summary:
|
|
358
368
|
|
|
359
369
|
```text
|
|
360
370
|
## confluence-prd-intake summary
|
|
@@ -379,10 +389,10 @@ Print to the agent's output. Do not write this summary to Confluence or the dest
|
|
|
379
389
|
|
|
380
390
|
## Idempotency & safety
|
|
381
391
|
|
|
382
|
-
- **
|
|
392
|
+
- **One item per cycle**: this skill processes the first eligible ready PRD from Phase 2, then exits. New or remaining PRDs under the `ready` parent are picked up by later scheduler invocations.
|
|
383
393
|
- **No writes outside the lifecycle**: this skill only ever writes to the destination tracker via `lisa:confluence-to-tracker` (which delegates to `lisa:tracker-write`), and only ever re-parents PRDs among `in_review`, `blocked`, `ticketed`, and `shipped` (the last via the rollup phase 3f only) via `lisa:atlassian-access` `operation: write-page`. It never edits PRD body content, never re-parents into or out of `draft`, never deletes pages. It re-parents under `shipped` and may archive the PRD page **only** through the config-gated rollup phase (3f).
|
|
384
394
|
- **Claim-first ordering**: the re-parent to `in_review` happens BEFORE validation runs, so a re-entrant call won't double-process.
|
|
385
|
-
- **Failure
|
|
395
|
+
- **Failure handling**: an exception processing the selected PRD is caught and recorded under "Errors" in the summary, then the cycle exits. The PRD that errored is left under whatever parent it currently occupies (usually `in_review` if claim succeeded) — the human investigates from there.
|
|
386
396
|
- **Single-parent invariant**: a page has exactly one parent by construction in Confluence — the multi-state ambiguity that label-based systems can hit (two `prd-*` labels simultaneously) cannot occur here. After every transition, re-read the page and confirm `parentId` matches the expected role; if not, surface as an Error and skip.
|
|
387
397
|
- **Rollup idempotency**: rollup (Phase 3f) is a no-op on a PRD already parented under `$SHIPPED_PARENT` (and already archived when `closeOnShipped` is `true`) — no duplicate re-parent, no duplicate archive, no duplicate comment. The all-terminal condition is a pure function of the children's current states (deduped by child-ref identity), so recomputing it is safe to re-run. Archival NEVER precedes the all-terminal condition.
|
|
388
398
|
|
|
@@ -425,4 +435,4 @@ Before this skill can run against a project, the project must adopt the lifecycl
|
|
|
425
435
|
3. Move each existing PRD page under the appropriate lifecycle parent based on its current state. Product moves PRDs from `draft` → `ready` when they are ready for ticketing (replaces the Notion `Status = Ready` flip).
|
|
426
436
|
4. Reserve the `in_review`, `blocked`, and `ticketed` parents for this skill — humans should not re-parent PRDs into them manually except to recover from an error.
|
|
427
437
|
|
|
428
|
-
If the project hasn't adopted these parent pages, the first run exits with a provisioning error (not the idle empty-set message) — this distinguishes a setup issue from a genuinely empty queue so operators know to run `/lisa:setup:confluence` rather than assuming
|
|
438
|
+
If the project hasn't adopted these parent pages, the first run exits with a provisioning error (not the idle empty-set message) — this distinguishes a setup issue from a genuinely empty queue so operators know to run `/lisa:setup:confluence` rather than assuming there is no work. See Phase 2 for how the skill detects this case.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: confluence-write-prd
|
|
3
|
+
description: "Creates or idempotently updates a PRD as a Confluence page parented under the configured lifecycle parent page (the draft parent by default, or the ready parent when initial_role is ready so lisa:confluence-prd-intake auto-claims it). The Confluence PRD-source writer behind lisa:prd-source-write. Confluence models PRD state by PARENT PAGE (not labels), per config-resolution. Dedupes by a stable marker embedded in the page body, found via CQL (matched by marker, never by title). All Atlassian access goes through lisa:atlassian-access."
|
|
4
|
+
allowed-tools: ["Skill", "Bash"]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Write Confluence PRD: $ARGUMENTS
|
|
8
|
+
|
|
9
|
+
Create (or update) a PRD page in Confluence. Invoked by `lisa:prd-source-write` when
|
|
10
|
+
`source = confluence`; do not call directly from a vendor-neutral caller. **All Confluence
|
|
11
|
+
operations go through `lisa:atlassian-access`** — never call the Atlassian API/MCP or `acli` directly.
|
|
12
|
+
|
|
13
|
+
Confluence's PRD lifecycle uses **parent pages**, not labels (scoped API tokens can't write
|
|
14
|
+
Confluence labels — see `config-resolution` "Confluence PRD lifecycle uses parent pages"). A PRD's
|
|
15
|
+
state is which lifecycle parent it lives under; "promote to ready" = re-parent to the ready parent.
|
|
16
|
+
|
|
17
|
+
`$ARGUMENTS` carries the `lisa:prd-source-write` spec: `title`, `body` (full PRD markdown),
|
|
18
|
+
`initial_role` (`draft` | `ready`, default `draft`), `dedupe_key`, `marker`, optional `source_ref`.
|
|
19
|
+
|
|
20
|
+
## Phase 1 — Resolve lifecycle parents
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
read_g() { local lv gv; lv=$(jq -r "$1 // empty" .lisa.config.local.json 2>/dev/null); gv=$(jq -r "$1 // empty" .lisa.config.json 2>/dev/null); echo "${lv:-${gv:-$2}}"; }
|
|
24
|
+
SPACE=$(read_g '.confluence.spaceKey' '')
|
|
25
|
+
CLOUDID=$(read_g '.atlassian.cloudId' '')
|
|
26
|
+
[ -z "$CLOUDID" ] && { echo "Error: atlassian.cloudId not set in .lisa.config.json."; exit 1; }
|
|
27
|
+
# Resolve the FULL set of lifecycle parents from config (never hard-code) — needed for the target
|
|
28
|
+
# parent, the past-ready reverse-lookup, and to derive the space when spaceKey is absent.
|
|
29
|
+
DRAFT_PARENT=$(read_g '.confluence.parents.draft' '')
|
|
30
|
+
READY_PARENT=$(read_g '.confluence.parents.ready' '')
|
|
31
|
+
IN_REVIEW_PARENT=$(read_g '.confluence.parents.in_review' '')
|
|
32
|
+
BLOCKED_PARENT=$(read_g '.confluence.parents.blocked' '')
|
|
33
|
+
TICKETED_PARENT=$(read_g '.confluence.parents.ticketed' '')
|
|
34
|
+
SHIPPED_PARENT=$(read_g '.confluence.parents.shipped' '')
|
|
35
|
+
VERIFIED_PARENT=$(read_g '.confluence.parents.verified' '')
|
|
36
|
+
# "Progressed past ready" parents (never re-parent a PRD down from these):
|
|
37
|
+
PROGRESSED_PARENTS=("$IN_REVIEW_PARENT" "$BLOCKED_PARENT" "$TICKETED_PARENT" "$SHIPPED_PARENT" "$VERIFIED_PARENT")
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Resolve the target parent from `initial_role`: `ready` → `$READY_PARENT`, otherwise `$DRAFT_PARENT`.
|
|
41
|
+
If the needed parent id is unset, stop and report that `/lisa:setup:confluence` must provision the
|
|
42
|
+
lifecycle parent pages — do not create a PRD outside the lifecycle scaffolding.
|
|
43
|
+
|
|
44
|
+
**Resolve the space (config allows parent-page-only setups with no `spaceKey`).** If `$SPACE` is
|
|
45
|
+
empty, derive it from the target lifecycle parent: `lisa:atlassian-access` `operation: read-page id:
|
|
46
|
+
<target parent>` and read its space key from the response. If neither `confluence.spaceKey` is set nor
|
|
47
|
+
a space can be derived from the parent, **stop and report** that a space could not be established —
|
|
48
|
+
do not attempt a CQL search or create without it.
|
|
49
|
+
|
|
50
|
+
## Phase 2 — Dedupe by marker (CQL search before create)
|
|
51
|
+
|
|
52
|
+
The `marker` is embedded in the page body. Find an existing PRD page carrying it — match the marker,
|
|
53
|
+
**never** the title:
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
lisa:atlassian-access operation: search-pages cql: 'space = "<SPACE>" AND text ~ "<marker>"'
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
If `source_ref` was passed, target that page directly. If a page with the marker exists → **update**;
|
|
60
|
+
else → **create**.
|
|
61
|
+
|
|
62
|
+
## Phase 3 — Create or update
|
|
63
|
+
|
|
64
|
+
**Storage-format body + marker (both paths).** Convert the PRD markdown to Confluence **storage
|
|
65
|
+
format** (XHTML): `#`/`##`/`###` → `<h1>/<h2>/<h3>`, paragraphs → `<p>`, lists → `<ul>/<ol><li>`,
|
|
66
|
+
fenced code → `<ac:structured-macro ac:name="code">`. Embed the marker as a storage comment
|
|
67
|
+
(`<!-- $MARKER -->`) or a small `<p>` so future CQL dedupe finds it. The body must always contain
|
|
68
|
+
**exactly one** marker; never write a markerless page (CREATE or UPDATE).
|
|
69
|
+
|
|
70
|
+
**CREATE:** `lisa:atlassian-access` `operation: write-page` (create form) with a payload that sets:
|
|
71
|
+
- `title`: `$TITLE`
|
|
72
|
+
- `space`: `$SPACE` (resolved in Phase 1)
|
|
73
|
+
- `ancestors`/`parentId`: the resolved lifecycle parent (`$DRAFT_PARENT` or `$READY_PARENT`)
|
|
74
|
+
- `body`: the storage-format, marker-normalized body above.
|
|
75
|
+
|
|
76
|
+
**UPDATE** (existing page or `source_ref`):
|
|
77
|
+
1. **GET-then-PUT** (load-bearing, as `confluence-prd-intake` documents): first `operation: read-page
|
|
78
|
+
id: <page-id>` to read the current `version.number`, then `operation: write-page` (edit form) with
|
|
79
|
+
the marker-normalized storage body and `version.number` bumped to current+1. A PUT without the
|
|
80
|
+
current version is rejected; never drop the marker on the edit.
|
|
81
|
+
2. Re-parent to the target lifecycle parent if the role changed — **unless** the page's current
|
|
82
|
+
parent is in the resolved `${PROGRESSED_PARENTS[@]}` set (already past `ready`). If so, leave it
|
|
83
|
+
and report `reused (already past ready)`. Reverse-lookup the current parent in
|
|
84
|
+
`confluence.parents.*` to determine its role before re-parenting.
|
|
85
|
+
|
|
86
|
+
## Phase 4 — Return
|
|
87
|
+
|
|
88
|
+
```yaml
|
|
89
|
+
ref: "<confluence-page-id>"
|
|
90
|
+
url: "<page url>"
|
|
91
|
+
role: draft | ready # derived from the lifecycle parent the page now lives under (or its current role when reused past ready)
|
|
92
|
+
marker: "<MARKER>"
|
|
93
|
+
outcome: created | reused
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Rules
|
|
97
|
+
|
|
98
|
+
- All access via `lisa:atlassian-access`; never call Atlassian directly.
|
|
99
|
+
- State is the parent page, not a label — never attempt Confluence label writes (they 401 on scoped
|
|
100
|
+
tokens; see `config-resolution`).
|
|
101
|
+
- Match dedupe by marker, never by title.
|
|
102
|
+
- Never re-parent a PRD already past `ready` down to draft/ready.
|
|
103
|
+
- Resolve parents from config (`confluence.parents.{draft,ready}`) — never hardcode page ids.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: github-build-intake
|
|
3
|
-
description: "GitHub counterpart to lisa:jira-build-intake. Scans a GitHub repository for issues carrying the configured `ready` build label,
|
|
3
|
+
description: "GitHub counterpart to lisa:jira-build-intake. Scans a GitHub repository for issues carrying the configured `ready` build label, processes the first eligible issue, runs leaf work via lisa:github-agent, relabels to the configured `done` label on completion, then exits. Enforces the claim-time arm of the `leaf-only-lifecycle` rule: a parent/container with open child work (or a childless Epic/Story/Spike) that still carries a stale build-ready label is moved out of the ready pickup queue into the configured `claimed` label with a lifecycle-repair comment, never dispatched to lisa:github-agent. The `ready` label is the human-flipped signal that an issue is truly ready for direct development pickup — mirroring how Notion PRDs work product Draft → Ready → (us) In Review → Blocked|Ticketed."
|
|
4
4
|
allowed-tools: ["Skill", "Bash"]
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -12,7 +12,7 @@ allowed-tools: ["Skill", "Bash"]
|
|
|
12
12
|
2. A full GitHub repo URL (e.g., `https://github.com/acme/frontend-v2`).
|
|
13
13
|
3. The literal token `github` — falls back to `.lisa.config.json` (`github.org` / `github.repo`).
|
|
14
14
|
|
|
15
|
-
Run one build-intake cycle.
|
|
15
|
+
Run one build-intake cycle. The first eligible issue in the configured `ready` build label is claimed, built via the `lisa:github-agent` flow, relabeled to the configured `done` label (env-aware — see Workflow resolution), then the cycle exits. Remaining ready issues stay queued for later scheduler invocations.
|
|
16
16
|
|
|
17
17
|
## Workflow resolution
|
|
18
18
|
|
|
@@ -30,7 +30,6 @@ read_role() {
|
|
|
30
30
|
|
|
31
31
|
READY=$(read_role ready "status:ready")
|
|
32
32
|
CLAIMED=$(read_role claimed "status:in-progress")
|
|
33
|
-
REVIEW=$(read_role review "status:code-review")
|
|
34
33
|
```
|
|
35
34
|
|
|
36
35
|
For env-keyed `done`, resolve the env first, then look up `done[<env>]`:
|
|
@@ -51,10 +50,12 @@ fi
|
|
|
51
50
|
DONE_TYPE=$(jq -r '.github.labels.build.done | type' .lisa.config.json 2>/dev/null)
|
|
52
51
|
if [ "$DONE_TYPE" = "string" ]; then
|
|
53
52
|
DONE=$(jq -r '.github.labels.build.done' .lisa.config.json)
|
|
53
|
+
DONE_LABELS_JSON=$(jq -c '[.github.labels.build.done]' .lisa.config.json)
|
|
54
54
|
elif [ "$DONE_TYPE" = "object" ]; then
|
|
55
55
|
[ -z "$TARGET_ENV" ] && { echo "ERROR: github.labels.build.done is env-keyed but env not resolvable"; exit 1; }
|
|
56
56
|
DONE=$(jq -r --arg e "$TARGET_ENV" '.github.labels.build.done[$e] // empty' .lisa.config.json)
|
|
57
57
|
[ -z "$DONE" ] && { echo "ERROR: github.labels.build.done has no entry for env '$TARGET_ENV'"; exit 1; }
|
|
58
|
+
DONE_LABELS_JSON=$(jq -c '[.github.labels.build.done[]]' .lisa.config.json)
|
|
58
59
|
else
|
|
59
60
|
case "$TARGET_ENV" in
|
|
60
61
|
dev) DONE="status:on-dev" ;;
|
|
@@ -62,6 +63,7 @@ else
|
|
|
62
63
|
production) DONE="status:done" ;;
|
|
63
64
|
*) echo "ERROR: cannot resolve done label without env"; exit 1 ;;
|
|
64
65
|
esac
|
|
66
|
+
DONE_LABELS_JSON=$(jq -cn --arg d "$DONE" '[$d]')
|
|
65
67
|
fi
|
|
66
68
|
```
|
|
67
69
|
|
|
@@ -69,7 +71,7 @@ In prose below, the role names refer to the resolved labels: e.g. "the `ready` l
|
|
|
69
71
|
|
|
70
72
|
## Confirmation policy
|
|
71
73
|
|
|
72
|
-
Do NOT ask the caller whether to proceed. Once invoked with a repo, run the cycle to completion — claim
|
|
74
|
+
Do NOT ask the caller whether to proceed. Once invoked with a repo, run the cycle to completion — claim and dispatch the first eligible issue through `lisa:github-agent`, relabel a successful build to `$DONE`, write the summary, and exit. The caller (a human or a cron) has already authorized the run by invoking the skill; re-prompting defeats the purpose of a background queue.
|
|
73
75
|
|
|
74
76
|
Specifically forbidden:
|
|
75
77
|
|
|
@@ -81,7 +83,7 @@ Specifically forbidden:
|
|
|
81
83
|
The only legitimate reasons to stop early:
|
|
82
84
|
|
|
83
85
|
- Missing repo or required configuration. Surface the missing value and exit.
|
|
84
|
-
- Label namespace not adopted (no issue carries any of `$READY` / `$CLAIMED` / `$
|
|
86
|
+
- Label namespace not adopted (no issue carries any of `$READY` / `$CLAIMED` / `$DONE`). Surface a label-convention error and exit (this is setup, not a normal idle cycle — see "Adoption" at the bottom).
|
|
85
87
|
- Empty ready set. Exit cleanly with `"No GitHub issues labeled $READY in <org>/<repo>. Nothing to do."`
|
|
86
88
|
|
|
87
89
|
## Lifecycle assumed
|
|
@@ -89,22 +91,20 @@ The only legitimate reasons to stop early:
|
|
|
89
91
|
The GitHub Issues build lifecycle uses **labels** (we deliberately do NOT key off open/closed alone — closed issues aren't always the right post-build state):
|
|
90
92
|
|
|
91
93
|
```text
|
|
92
|
-
ready → claimed →
|
|
93
|
-
(human) (us claim) (us
|
|
94
|
+
ready → claimed → done(env-keyed)
|
|
95
|
+
(human) (us claim) (us done; PR ready)
|
|
94
96
|
```
|
|
95
97
|
|
|
96
|
-
(Defaults: `status:ready` / `status:in-progress` / `status:
|
|
98
|
+
(Defaults: `status:ready` / `status:in-progress` / `status:on-dev`/`status:on-stg`/`status:done`.)
|
|
97
99
|
|
|
98
100
|
This skill ONLY transitions:
|
|
99
101
|
|
|
100
102
|
- `$READY` → `$CLAIMED` (claim)
|
|
101
103
|
- `$CLAIMED` → `$DONE` (build complete, PR ready)
|
|
102
104
|
|
|
103
|
-
|
|
105
|
+
A "transition" means: remove the old role label and add the new one, in two `gh issue edit` calls (`--remove-label` + `--add-label`) or one combined call. The skill MUST verify exactly one build-lifecycle label (from the resolved `$READY`/`$CLAIMED`/`$DONE` set) is present after the update — having two simultaneously breaks idempotency.
|
|
104
106
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
**Pre-flight check**: at the start of each cycle, confirm at least one of the resolved role labels (`$READY`, `$CLAIMED`, `$REVIEW`, or any `$DONE` value) exists on the repo via `gh label list --repo <org>/<repo> --json name`. If none exist, the convention has not been adopted — surface the label-convention error and exit.
|
|
107
|
+
**Pre-flight check**: at the start of each cycle, confirm at least one of the resolved role labels (`$READY`, `$CLAIMED`, or any `$DONE` value) exists on the repo via `gh label list --repo <org>/<repo> --json name`. If none exist, the convention has not been adopted — surface the label-convention error and exit.
|
|
108
108
|
|
|
109
109
|
## Phases
|
|
110
110
|
|
|
@@ -127,19 +127,32 @@ If empty, run a secondary check to distinguish a genuinely empty queue from an u
|
|
|
127
127
|
|
|
128
128
|
```bash
|
|
129
129
|
gh label list --repo <org>/<repo> --json name \
|
|
130
|
-
| jq -r --arg r "$READY" --arg c "$CLAIMED" --
|
|
131
|
-
'[.[] | .name | select(. == $r or . == $c or .
|
|
130
|
+
| jq -r --arg r "$READY" --arg c "$CLAIMED" --argjson d "$DONE_LABELS_JSON" \
|
|
131
|
+
'[.[] | .name | select(. == $r or . == $c or (. as $n | $d | index($n)))] | length'
|
|
132
132
|
```
|
|
133
133
|
|
|
134
134
|
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 → genuinely empty queue, exit cleanly with `"No GitHub issues labeled $READY. Nothing to do."`
|
|
135
135
|
|
|
136
|
-
### Phase 3 — Process
|
|
136
|
+
### Phase 3 — Process the first eligible ready issue
|
|
137
|
+
|
|
138
|
+
#### 3a.0 Repo-scope gate (claim only current-repo issues)
|
|
139
|
+
|
|
140
|
+
GitHub Issues live in one repo by definition, so the scanned repo's issues are usually inherently current-repo. But a planning/umbrella repo's issues can target sibling repos, so this skill still claims only issues for the repo it is running in. Run this gate **before** the leaf-only gate (3a) and the claim (3b), per the `repo-scope-split` rule's "Claim-time repo scoping" section (cite it by slug; do not restate its decision table).
|
|
141
|
+
|
|
142
|
+
1. **Resolve the current repo** per `config-resolution` "Repo scoping" (`.repo` → `.github.repo` → `git remote get-url origin` basename). If unresolvable, stop and report.
|
|
143
|
+
2. **Cheap path first.** Prefer candidates already carrying the `repo:<current>` label. Keep the Phase 2 scan broad so unlabeled issues are still seen, determined, and stamped.
|
|
144
|
+
3. **Per candidate, apply the repo-scope decision (`repo-scope-split`):**
|
|
145
|
+
- Carries `repo:<other>` → **skip** (leave it `ready` for that repo's own intake); next candidate.
|
|
146
|
+
- **Unlabeled** → determine the target repo(s) from the issue + code surfaces, then **stamp** `repo:<name>` via `gh issue edit <n> --add-label "repo:<name>"` (create the label lazily) so later cycles filter cheaply; re-apply with the now-known repo. (An issue whose work is entirely in the scanned repo is simply labeled `repo:<current>`.)
|
|
147
|
+
- **Multi-repo leaf → split, never claim.** Run the `repo-scope-split` work-time procedure into single-repo siblings, each created **build-ready** (`build_ready: true`) and stamped with its own `repo:<name>`; the current repo's sibling becomes a normal candidate.
|
|
148
|
+
- **Single-repo leaf for the current repo** → fall through to 3a (leaf-only gate) and 3b (claim).
|
|
149
|
+
4. Continue until a claimable current-repo leaf is found (claim it; one per cycle) or the ready set is exhausted — exit cleanly with `"No ready issues for repo <current>. Nothing to do."`.
|
|
137
150
|
|
|
138
151
|
#### 3a. Leaf-only claim gate (repair containers)
|
|
139
152
|
|
|
140
153
|
Build intake dispatches **only independently implementable leaf work units** to the build agent. This enforces the claim-time arm of the vendor-neutral `leaf-only-lifecycle` rule: a parent/container that still carries a stale build-ready role (e.g. `status:ready` applied before this rule existed, or hand-applied to an Epic/Story) is **never dispatched** — intake moves it out of the pickup queue by replacing `$READY` with `$CLAIMED`, then posts a clear lifecycle-repair message. It is the claim-time complement to the write-time labeling in `lisa:github-write-issue` and the validate-time S15 gate in `lisa:github-validate-issue`; all three cite the same rule so the classification never drifts. **Never silently implement a container.**
|
|
141
154
|
|
|
142
|
-
Run this gate **before** the leaf claim relabel,
|
|
155
|
+
Run this gate **before** the leaf claim relabel, starting with the oldest/highest-priority ready candidate. Do NOT comment "Claimed" or invoke `lisa:github-agent` for an issue that fails the gate. A container repair still changes labels: remove `$READY`, add `$CLAIMED`, explain that parent/container `$CLAIMED` means rollup/build-lane progress through child/leaf work rather than direct implementation, record it, and end the cycle.
|
|
143
156
|
|
|
144
157
|
**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:github-read-issue` uses — native sub-issues first, then body parentage (task-list checkboxes referencing other issues, `Parent: #<n>` references). Dependency links such as `Blocked by:` are not parentage; they are handled by the active dependency hold gate below.
|
|
145
158
|
|
|
@@ -254,9 +267,9 @@ This close is idempotent: if the issue is already closed, record that native clo
|
|
|
254
267
|
|
|
255
268
|
For any non-Success outcome, do NOT transition. The issue sits in `$CLAIMED` (or wherever `lisa:github-agent` left it) — humans take it from there.
|
|
256
269
|
|
|
257
|
-
#### 3e.
|
|
270
|
+
#### 3e. Stop
|
|
258
271
|
|
|
259
|
-
|
|
272
|
+
Stop immediately after the first claimed, skipped, blocked, held, or errored issue. Later scheduler invocations process the remaining ready issues.
|
|
260
273
|
|
|
261
274
|
### Phase 4 — Summary report
|
|
262
275
|
|
|
@@ -291,7 +304,7 @@ Total PRs opened: <n>
|
|
|
291
304
|
- **Claim-first ordering**: `$CLAIMED` set BEFORE `lisa:github-agent` invocation for leaves; containers are also moved to `$CLAIMED` to leave the ready pickup queue, but are not dispatched.
|
|
292
305
|
- **No writes outside the lifecycle**: this skill only relabels `$READY → $CLAIMED` and `$CLAIMED → $DONE`. For containers, `$READY → $CLAIMED` is a lifecycle repair, not a direct build claim. Every other label change is owned by `lisa:github-agent`.
|
|
293
306
|
- **Terminal native closure**: after `$CLAIMED → $DONE`, close the GitHub issue only when `$DONE` is the true terminal done value per `leaf-only-lifecycle`; intermediate env labels stay open.
|
|
294
|
-
- **
|
|
307
|
+
- **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.
|
|
295
308
|
- **Single cycle per repo**: do not run two `lisa:github-build-intake` cycles in parallel against the same repo — concurrent claims could race. The scheduling layer is responsible for serialization.
|
|
296
309
|
- **Single-label invariant**: after every transition, verify exactly one `status:*` label is present on the issue. If two are present (rare race), surface as an Error and skip — do NOT auto-resolve.
|
|
297
310
|
- **Never pick an arbitrary env for `$DONE`**. If `done` is a map and env is ambiguous, fail loudly.
|
|
@@ -304,7 +317,6 @@ Total PRs opened: <n>
|
|
|
304
317
|
| `.lisa.config.json` `github.repo` | (from `$ARGUMENTS`) | GitHub repo for the default queue |
|
|
305
318
|
| `.lisa.config.json` `github.labels.build.ready` | `status:ready` | The label that signals "human says this is buildable" |
|
|
306
319
|
| `.lisa.config.json` `github.labels.build.claimed` | `status:in-progress` | The label set on pickup |
|
|
307
|
-
| `.lisa.config.json` `github.labels.build.review` | `status:code-review` | The label set when the PR opens (owned by `lisa:github-evidence`) |
|
|
308
320
|
| `.lisa.config.json` `github.labels.build.done` | env-keyed map or string | The label set after a successful build; env-aware |
|
|
309
321
|
| `.lisa.config.json` `deploy.branches` | — | Reverse-lookup map for env inference from PR base branch |
|
|
310
322
|
|
|
@@ -329,7 +341,6 @@ Before this skill can run, the repo must adopt the `status:*` label namespace. U
|
|
|
329
341
|
```bash
|
|
330
342
|
gh label create status:ready --color FBCA04 --description "Ready for build" --repo <org>/<repo>
|
|
331
343
|
gh label create status:in-progress --color 0E8A16 --description "Build in progress" --repo <org>/<repo>
|
|
332
|
-
gh label create status:code-review --color 5319E7 --description "PR open, awaiting review" --repo <org>/<repo>
|
|
333
344
|
gh label create status:on-dev --color 1D76DB --description "Built, deployed to dev" --repo <org>/<repo>
|
|
334
345
|
gh label create status:done --color 0E8A16 --description "Shipped" --repo <org>/<repo>
|
|
335
346
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: github-evidence
|
|
3
|
-
description: "Upload text evidence to the GitHub `pr-assets` release, update PR description, post a GitHub Issue comment with code blocks
|
|
3
|
+
description: "Upload text evidence to the GitHub `pr-assets` release, update PR description, and post a GitHub Issue comment with code blocks. Reusable by any skill that captures evidence and generates evidence/comment.md (and optionally evidence/comment.txt). The GitHub counterpart of lisa:jira-evidence."
|
|
4
4
|
allowed-tools: ["Bash"]
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -8,23 +8,6 @@ allowed-tools: ["Bash"]
|
|
|
8
8
|
|
|
9
9
|
Upload captured evidence and generated templates to the GitHub PR description and the originating GitHub Issue. This skill is the posting step — it assumes evidence files and a comment template already exist in the evidence directory.
|
|
10
10
|
|
|
11
|
-
## Workflow resolution
|
|
12
|
-
|
|
13
|
-
The `claimed` and `review` build labels are read from `.lisa.config.json` `github.labels.build.*`, falling back to the defaults documented in the `config-resolution` rule (`status:in-progress` and `status:code-review`).
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
read_role() {
|
|
17
|
-
local role="$1" default="$2"
|
|
18
|
-
local local_v global_v
|
|
19
|
-
local_v=$(jq -r ".github.labels.build.${role} // empty" .lisa.config.local.json 2>/dev/null)
|
|
20
|
-
global_v=$(jq -r ".github.labels.build.${role} // empty" .lisa.config.json 2>/dev/null)
|
|
21
|
-
echo "${local_v:-${global_v:-$default}}"
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
CLAIMED=$(read_role claimed "status:in-progress")
|
|
25
|
-
REVIEW=$(read_role review "status:code-review")
|
|
26
|
-
```
|
|
27
|
-
|
|
28
11
|
## Arguments
|
|
29
12
|
|
|
30
13
|
`$ARGUMENTS`: `<ISSUE_REF> <EVIDENCE_DIR> <PR_NUMBER>`
|
|
@@ -92,15 +75,9 @@ REVIEW=$(read_role review "status:code-review")
|
|
|
92
75
|
gh issue comment <issue-number> --repo <issue-org>/<issue-repo> --body-file "$EVIDENCE_DIR/comment.md"
|
|
93
76
|
```
|
|
94
77
|
|
|
95
|
-
6. **
|
|
96
|
-
|
|
97
|
-
```bash
|
|
98
|
-
gh issue edit <issue-number> --repo <issue-org>/<issue-repo> \
|
|
99
|
-
--remove-label "$CLAIMED" \
|
|
100
|
-
--add-label "$REVIEW"
|
|
101
|
-
```
|
|
78
|
+
6. **Leave lifecycle labels unchanged**
|
|
102
79
|
|
|
103
|
-
|
|
80
|
+
GitHub evidence posting is evidence-only. The caller that owns the build lifecycle (`lisa:github-build-intake` / `lisa:github-agent`) transitions the issue from the configured `claimed` label directly to the configured `done` label after a successful build. Do not apply `status:code-review` here.
|
|
104
81
|
|
|
105
82
|
## Evidence Naming Convention
|
|
106
83
|
|
|
@@ -89,7 +89,7 @@ Use `lisa:github-evidence` to post everything:
|
|
|
89
89
|
# Equivalent of: bash scripts/post-evidence.sh — but invoked via the Skill tool
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
-
Invoke the skill with `<ISSUE_REF> ./evidence <PR_NUMBER>`. It uploads the files to the `pr-assets` release, edits the PR's `## Evidence` section, posts a comment on the issue
|
|
92
|
+
Invoke the skill with `<ISSUE_REF> ./evidence <PR_NUMBER>`. It uploads the files to the `pr-assets` release, edits the PR's `## Evidence` section, and posts a comment on the issue. The build-intake owner performs the direct `claimed` → `done` lifecycle transition after success.
|
|
93
93
|
|
|
94
94
|
### Step 6: Verify
|
|
95
95
|
|
|
@@ -97,7 +97,7 @@ Confirm:
|
|
|
97
97
|
- Evidence files exist as release assets named `pr-<PR>-NN-name.{txt,json}`.
|
|
98
98
|
- The PR description contains the `## Evidence` section with code-block embeds.
|
|
99
99
|
- The issue has a new comment whose body matches `comment.md`.
|
|
100
|
-
- The issue
|
|
100
|
+
- The issue has the evidence comment, and the build-intake owner can transition it from `status:in-progress` directly to the configured `done` label.
|
|
101
101
|
|
|
102
102
|
## Verification Patterns Reference
|
|
103
103
|
|