@codyswann/lisa 2.21.0 → 2.23.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 +3 -2
- 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 +11 -9
- package/plugins/lisa/agents/github-agent.md +18 -10
- package/plugins/lisa/agents/github-build-intake.md +10 -8
- package/plugins/lisa/agents/github-prd-intake.md +11 -9
- package/plugins/lisa/agents/jira-agent.md +12 -8
- package/plugins/lisa/agents/jira-build-intake.md +9 -7
- package/plugins/lisa/agents/linear-agent.md +15 -9
- package/plugins/lisa/agents/linear-build-intake.md +13 -11
- package/plugins/lisa/agents/linear-prd-intake.md +11 -9
- package/plugins/lisa/agents/notion-prd-intake.md +11 -9
- package/plugins/lisa/commands/setup/atlassian.md +7 -0
- package/plugins/lisa/commands/setup/confluence.md +7 -0
- package/plugins/lisa/commands/setup/jira.md +7 -0
- package/plugins/lisa/commands/setup/notion.md +7 -0
- package/plugins/lisa/rules/base-rules.md +2 -2
- package/plugins/lisa/rules/config-resolution.md +242 -24
- package/plugins/lisa/rules/repo-scope-split.md +41 -0
- package/plugins/lisa/rules/verification.md +13 -0
- package/plugins/lisa/skills/atlassian-access/SKILL.md +260 -0
- package/plugins/lisa/skills/confluence-prd-intake/SKILL.md +167 -82
- package/plugins/lisa/skills/confluence-to-tracker/SKILL.md +39 -26
- package/plugins/lisa/skills/git-submit-pr/SKILL.md +1 -1
- package/plugins/lisa/skills/github-add-journey/SKILL.md +1 -0
- package/plugins/lisa/skills/github-build-intake/SKILL.md +104 -40
- package/plugins/lisa/skills/github-evidence/SKILL.md +22 -5
- package/plugins/lisa/skills/github-prd-intake/SKILL.md +87 -51
- package/plugins/lisa/skills/github-to-tracker/SKILL.md +2 -2
- package/plugins/lisa/skills/github-validate-issue/SKILL.md +11 -1
- package/plugins/lisa/skills/jira-add-journey/SKILL.md +1 -0
- package/plugins/lisa/skills/jira-build-intake/SKILL.md +110 -45
- package/plugins/lisa/skills/jira-create/SKILL.md +5 -3
- package/plugins/lisa/skills/jira-evidence/SKILL.md +19 -2
- package/plugins/lisa/skills/jira-journey/SKILL.md +3 -1
- package/plugins/lisa/skills/jira-read-ticket/SKILL.md +10 -8
- package/plugins/lisa/skills/jira-sync/SKILL.md +11 -5
- package/plugins/lisa/skills/jira-validate-ticket/SKILL.md +22 -10
- package/plugins/lisa/skills/jira-verify/SKILL.md +5 -3
- package/plugins/lisa/skills/jira-write-ticket/SKILL.md +16 -14
- package/plugins/lisa/skills/linear-add-journey/SKILL.md +1 -0
- package/plugins/lisa/skills/linear-build-intake/SKILL.md +90 -32
- package/plugins/lisa/skills/linear-evidence/SKILL.md +22 -5
- package/plugins/lisa/skills/linear-prd-intake/SKILL.md +92 -57
- package/plugins/lisa/skills/linear-validate-issue/SKILL.md +10 -0
- package/plugins/lisa/skills/notion-access/SKILL.md +193 -0
- package/plugins/lisa/skills/notion-prd-intake/SKILL.md +105 -46
- package/plugins/lisa/skills/notion-to-tracker/SKILL.md +7 -5
- package/plugins/lisa/skills/setup-atlassian/SKILL.md +316 -0
- package/plugins/lisa/skills/setup-confluence/SKILL.md +245 -0
- package/plugins/lisa/skills/setup-jira/SKILL.md +198 -0
- package/plugins/lisa/skills/setup-notion/SKILL.md +283 -0
- package/plugins/lisa/skills/task-decomposition/SKILL.md +2 -0
- package/plugins/lisa/skills/ticket-triage/SKILL.md +4 -1
- package/plugins/lisa/skills/tracker-evidence/SKILL.md +1 -0
- package/plugins/lisa/skills/verification-lifecycle/SKILL.md +2 -0
- 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-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-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-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-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/src/base/agents/confluence-prd-intake.md +11 -9
- package/plugins/src/base/agents/github-agent.md +18 -10
- package/plugins/src/base/agents/github-build-intake.md +10 -8
- package/plugins/src/base/agents/github-prd-intake.md +11 -9
- package/plugins/src/base/agents/jira-agent.md +12 -8
- package/plugins/src/base/agents/jira-build-intake.md +9 -7
- package/plugins/src/base/agents/linear-agent.md +15 -9
- package/plugins/src/base/agents/linear-build-intake.md +13 -11
- package/plugins/src/base/agents/linear-prd-intake.md +11 -9
- package/plugins/src/base/agents/notion-prd-intake.md +11 -9
- package/plugins/src/base/commands/setup/atlassian.md +7 -0
- package/plugins/src/base/commands/setup/confluence.md +7 -0
- package/plugins/src/base/commands/setup/jira.md +7 -0
- package/plugins/src/base/commands/setup/notion.md +7 -0
- package/plugins/src/base/rules/base-rules.md +2 -2
- package/plugins/src/base/rules/config-resolution.md +242 -24
- package/plugins/src/base/rules/repo-scope-split.md +41 -0
- package/plugins/src/base/rules/verification.md +13 -0
- package/plugins/src/base/skills/atlassian-access/SKILL.md +260 -0
- package/plugins/src/base/skills/confluence-prd-intake/SKILL.md +167 -82
- package/plugins/src/base/skills/confluence-to-tracker/SKILL.md +39 -26
- package/plugins/src/base/skills/git-submit-pr/SKILL.md +1 -1
- package/plugins/src/base/skills/github-add-journey/SKILL.md +1 -0
- package/plugins/src/base/skills/github-build-intake/SKILL.md +104 -40
- package/plugins/src/base/skills/github-evidence/SKILL.md +22 -5
- package/plugins/src/base/skills/github-prd-intake/SKILL.md +87 -51
- package/plugins/src/base/skills/github-to-tracker/SKILL.md +2 -2
- package/plugins/src/base/skills/github-validate-issue/SKILL.md +11 -1
- package/plugins/src/base/skills/jira-add-journey/SKILL.md +1 -0
- package/plugins/src/base/skills/jira-build-intake/SKILL.md +110 -45
- package/plugins/src/base/skills/jira-create/SKILL.md +5 -3
- package/plugins/src/base/skills/jira-evidence/SKILL.md +19 -2
- package/plugins/src/base/skills/jira-journey/SKILL.md +3 -1
- package/plugins/src/base/skills/jira-read-ticket/SKILL.md +10 -8
- package/plugins/src/base/skills/jira-sync/SKILL.md +11 -5
- package/plugins/src/base/skills/jira-validate-ticket/SKILL.md +22 -10
- package/plugins/src/base/skills/jira-verify/SKILL.md +5 -3
- package/plugins/src/base/skills/jira-write-ticket/SKILL.md +16 -14
- package/plugins/src/base/skills/linear-add-journey/SKILL.md +1 -0
- package/plugins/src/base/skills/linear-build-intake/SKILL.md +90 -32
- package/plugins/src/base/skills/linear-evidence/SKILL.md +22 -5
- package/plugins/src/base/skills/linear-prd-intake/SKILL.md +92 -57
- package/plugins/src/base/skills/linear-validate-issue/SKILL.md +10 -0
- package/plugins/src/base/skills/notion-access/SKILL.md +193 -0
- package/plugins/src/base/skills/notion-prd-intake/SKILL.md +105 -46
- package/plugins/src/base/skills/notion-to-tracker/SKILL.md +7 -5
- package/plugins/src/base/skills/setup-atlassian/SKILL.md +316 -0
- package/plugins/src/base/skills/setup-confluence/SKILL.md +245 -0
- package/plugins/src/base/skills/setup-jira/SKILL.md +198 -0
- package/plugins/src/base/skills/setup-notion/SKILL.md +283 -0
- package/plugins/src/base/skills/task-decomposition/SKILL.md +2 -0
- package/plugins/src/base/skills/ticket-triage/SKILL.md +4 -1
- package/plugins/src/base/skills/tracker-evidence/SKILL.md +1 -0
- package/plugins/src/base/skills/verification-lifecycle/SKILL.md +2 -0
- package/scripts/check-plugins-sync.sh +45 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: setup-jira
|
|
3
|
+
description: "Configure JIRA as the destination tracker for this project. Writes `jira.project` into `.lisa.config.json` and offers to set top-level `tracker: \"jira\"`. Depends on /lisa:setup:atlassian — atlassian.cloudId must already be present, otherwise this skill stops and instructs the user to run setup-atlassian first. Idempotent."
|
|
4
|
+
allowed-tools: ["Bash", "Read", "Write", "Edit", "Skill", "AskUserQuestion"]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Setup JIRA: $ARGUMENTS
|
|
8
|
+
|
|
9
|
+
Set the JIRA project key and (optionally) make JIRA this project's destination tracker.
|
|
10
|
+
|
|
11
|
+
## Workflow
|
|
12
|
+
|
|
13
|
+
### Step 1 — Verify atlassian prerequisite
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
cloudid=$(jq -r '.atlassian.cloudId // empty' .lisa.config.json 2>/dev/null)
|
|
17
|
+
if [ -z "$cloudid" ]; then
|
|
18
|
+
echo "Error: atlassian.cloudId not set. Run /lisa:setup:atlassian first." >&2
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
If `atlassian` is missing, invoke `/lisa:setup-atlassian` via the Skill tool first, then resume.
|
|
24
|
+
|
|
25
|
+
### Step 2 — Resolve the JIRA project key
|
|
26
|
+
|
|
27
|
+
Honor any `--project=KEY` argument. Otherwise, list projects via the active access substrate:
|
|
28
|
+
|
|
29
|
+
- CLI: `acli jira project list --output json`
|
|
30
|
+
- MCP: `mcp__plugin_atlassian_atlassian__getVisibleJiraProjects` with `cloudId=$cloudid`
|
|
31
|
+
|
|
32
|
+
If only one project is returned, pick it. If multiple, present them via `AskUserQuestion` (label = project key, description = project name) so the user picks the one this project should target.
|
|
33
|
+
|
|
34
|
+
### Step 3 — Probe workflow & resolve role mapping
|
|
35
|
+
|
|
36
|
+
Lisa's build lifecycle uses three role-keyed statuses (`ready` → `claimed` → `done[env]`). Defaults are `Ready`, `In Progress`, and `{dev: "On Dev", staging: "On Stg", production: "Done"}`. Probe the project's workflow to confirm these exist; prompt for overrides if not.
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Fetch a sample ticket's available transitions (or use a project-level workflow scheme query)
|
|
40
|
+
# via lisa:atlassian-access operation: transitions key: <SAMPLE-KEY>
|
|
41
|
+
# Collect the union of status names visible in the project.
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
For each role:
|
|
45
|
+
|
|
46
|
+
1. If a status with the **default name** exists in the workflow → use the default; no config entry needed.
|
|
47
|
+
2. If the default name is missing but a status with a similar role exists (e.g. `Resolved` instead of `Done`) → present the project's status list via `AskUserQuestion` and let the user pick which maps to the role.
|
|
48
|
+
3. If nothing plausible exists → stop and tell the user to either add the missing status to their JIRA workflow (admin task) or pick a fallback that's close enough.
|
|
49
|
+
|
|
50
|
+
Collect overrides as a partial workflow map. ONLY write keys that differ from defaults — don't bloat the config with redundant values.
|
|
51
|
+
|
|
52
|
+
### Step 4 — Write `jira.project` + overrides
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Always write project key.
|
|
56
|
+
jq --arg key "$PROJECT_KEY" \
|
|
57
|
+
'.jira = ((.jira // {}) | .project = $key)' \
|
|
58
|
+
.lisa.config.json > .lisa.config.json.tmp \
|
|
59
|
+
&& mv .lisa.config.json.tmp .lisa.config.json
|
|
60
|
+
|
|
61
|
+
# Conditionally write workflow overrides (only if any role differs from default).
|
|
62
|
+
if [ -n "$WORKFLOW_OVERRIDES_JSON" ]; then
|
|
63
|
+
jq --argjson wf "$WORKFLOW_OVERRIDES_JSON" \
|
|
64
|
+
'.jira.workflow = $wf' \
|
|
65
|
+
.lisa.config.json > .lisa.config.json.tmp \
|
|
66
|
+
&& mv .lisa.config.json.tmp .lisa.config.json
|
|
67
|
+
fi
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Step 5 — Verify transition reachability (A → B → C cascade)
|
|
71
|
+
|
|
72
|
+
Confirm every resolved role's status name is actually reachable in the JIRA workflow. **acli has no non-destructive workflow inspector** (the `workitem view --json` `transitions` key is always `null` because acli doesn't pass `expand=transitions`), so verification cascades through three modes, each more invasive than the last.
|
|
73
|
+
|
|
74
|
+
#### Cache check (skip if unchanged)
|
|
75
|
+
|
|
76
|
+
Hash the workflow-mapping object plus the project key:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
WORKFLOW_HASH=$(jq -c '{project: .jira.project, workflow: .jira.workflow}' .lisa.config.json | shasum -a 256 | awk '{print $1}')
|
|
80
|
+
CACHED=$(jq -r '.jira.verified_workflow_hash // empty' .lisa.config.local.json 2>/dev/null)
|
|
81
|
+
if [ "$WORKFLOW_HASH" = "$CACHED" ]; then
|
|
82
|
+
echo "Workflow already verified; skipping probe."
|
|
83
|
+
# proceed to Step 6
|
|
84
|
+
fi
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The verification cache key lives in `.lisa.config.local.json` (per-developer, gitignored) because the workflow on disk might be stale relative to the user's local view of it.
|
|
88
|
+
|
|
89
|
+
#### Mode A — Changelog inspection (non-destructive, fast)
|
|
90
|
+
|
|
91
|
+
Aggregate every status name ever observed in the project's ticket histories:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
acli jira workitem search --jql "project = $PROJECT" --paginate --json \
|
|
95
|
+
| jq -r '[.[].changelog.histories[]?.items[]? | select(.field == "status") | .fromString, .toString] | unique[]' \
|
|
96
|
+
| sort -u > /tmp/lisa-observed-statuses.txt
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
For each role (`ready`, `claimed`, `blocked`, each `done.<env>`), check whether the resolved status name appears in the observed set. Roles that match are **verified**. Roles that don't are **unverified** — but that doesn't yet mean they're missing; freshly-added statuses simply haven't accumulated history.
|
|
100
|
+
|
|
101
|
+
If every role is verified, cache the hash and proceed to Step 6.
|
|
102
|
+
|
|
103
|
+
#### Mode B — Create-probe-delete (synthetic ticket)
|
|
104
|
+
|
|
105
|
+
For any role left unverified by Mode A, fall back to creating a throwaway ticket and walking it through.
|
|
106
|
+
|
|
107
|
+
Prompt via `AskUserQuestion`:
|
|
108
|
+
|
|
109
|
+
> Mode A could not verify these roles: `<list>`. Create a synthetic probe ticket to verify (recommended), or designate an existing ticket to use as the probe?
|
|
110
|
+
|
|
111
|
+
If user picks synthetic:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# Create a probe ticket. Title makes its purpose obvious so a teammate doesn't act on it.
|
|
115
|
+
PROBE_KEY=$(acli jira workitem create \
|
|
116
|
+
--project "$PROJECT" \
|
|
117
|
+
--type Task \
|
|
118
|
+
--summary "lisa-setup-probe (DELETE ME)" \
|
|
119
|
+
--description "Created by /lisa:setup:jira to verify workflow status reachability. Safe to delete." \
|
|
120
|
+
--json \
|
|
121
|
+
| jq -r '.key')
|
|
122
|
+
|
|
123
|
+
trap 'acli jira workitem delete --key "$PROBE_KEY" --yes 2>/dev/null || acli jira workitem archive --key "$PROBE_KEY" --yes 2>/dev/null' EXIT
|
|
124
|
+
|
|
125
|
+
# Walk through every unverified role's status in order.
|
|
126
|
+
for status in $UNVERIFIED_STATUSES; do
|
|
127
|
+
if ! acli jira workitem transition --key "$PROBE_KEY" --status "$status" --yes 2>/dev/null; then
|
|
128
|
+
echo "Error: status '$status' is not reachable in the workflow from the probe ticket's current state." >&2
|
|
129
|
+
# Mark this role as missing; continue with remaining roles where possible (skip if transition is from a state we couldn't reach).
|
|
130
|
+
break
|
|
131
|
+
fi
|
|
132
|
+
done
|
|
133
|
+
|
|
134
|
+
# trap handles cleanup
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
If `acli jira workitem create` fails because the project has required fields, surface the field list and instruct the user to either (a) fill them in via `--field`/`--from-json`, (b) temporarily relax the required-fields configuration, or (c) fall through to Mode C.
|
|
138
|
+
|
|
139
|
+
#### Mode C — Probe an existing ticket (last resort)
|
|
140
|
+
|
|
141
|
+
If Mode B can't create a probe (mandatory fields, permission, etc.), prompt the user to designate a low-stakes existing ticket:
|
|
142
|
+
|
|
143
|
+
> Provide a ticket key in `To Do` (or wherever you want to start the probe). I'll walk it through the unverified statuses and revert.
|
|
144
|
+
|
|
145
|
+
Capture the ticket's starting status; walk through the unverified chain; revert to the starting status at the end. Use `trap` to ensure revert runs even on error.
|
|
146
|
+
|
|
147
|
+
#### Outcome
|
|
148
|
+
|
|
149
|
+
Once all roles are verified by some combination of A/B/C, cache the result:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
jq --arg hash "$WORKFLOW_HASH" \
|
|
153
|
+
'.jira = ((.jira // {}) | .verified_workflow_hash = $hash)' \
|
|
154
|
+
.lisa.config.local.json > .lisa.config.local.json.tmp \
|
|
155
|
+
&& mv .lisa.config.local.json.tmp .lisa.config.local.json
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
If any role is **unreachable** after all three modes, stop and surface a concrete admin-task message (which transition needs to be added, between which two statuses, in which workflow). Do not write a config that points at unreachable transitions.
|
|
159
|
+
|
|
160
|
+
### Step 6 — Verify mid-build transition chain
|
|
161
|
+
|
|
162
|
+
Independent of role reachability, confirm the lifecycle's required *transition graph* is intact: `ready → claimed`, `claimed → done.<env>` for each env. Mode A's changelog includes paired `(fromString, toString)` entries — count any pair that matches a required transition as verified. Modes B and C do this implicitly by walking the chain in order. If any required transition is missing (e.g., `claimed → done.staging` requires going through `review` first per the workflow), surface and stop with an admin remediation.
|
|
163
|
+
|
|
164
|
+
### Step 7 — Offer to set top-level `tracker`
|
|
165
|
+
|
|
166
|
+
If `.tracker` is unset or differs from `"jira"`, ask via `AskUserQuestion`:
|
|
167
|
+
|
|
168
|
+
> JIRA project `<KEY>` written. Set top-level `tracker: "jira"` so all vendor-neutral skills target JIRA?
|
|
169
|
+
|
|
170
|
+
Recommend "Yes" unless the project is using a different destination (e.g., GitHub Issues for build-queue but JIRA for read-only mirror — rare).
|
|
171
|
+
|
|
172
|
+
If yes:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
jq '.tracker = "jira"' .lisa.config.json > .lisa.config.json.tmp \
|
|
176
|
+
&& mv .lisa.config.json.tmp .lisa.config.json
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Step 8 — Verify
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
jq -e '.jira.project' .lisa.config.json >/dev/null
|
|
183
|
+
jq -e '.tracker' .lisa.config.json >/dev/null # if user said yes in step 7
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Report success with the resolved project key, the workflow mapping (defaults vs. overrides), and whether `tracker` was set.
|
|
187
|
+
|
|
188
|
+
## Idempotency
|
|
189
|
+
|
|
190
|
+
- Re-running replaces `jira.project` cleanly (jq merge, not append).
|
|
191
|
+
- Re-running does not re-prompt for `tracker` if it's already `"jira"`.
|
|
192
|
+
- Re-running with an unchanged workflow mapping skips the verification probe (Mode A/B/C) entirely via the `verified_workflow_hash` cache in `.lisa.config.local.json`. Editing any role name invalidates the cache and re-runs the probe.
|
|
193
|
+
|
|
194
|
+
## Rules
|
|
195
|
+
|
|
196
|
+
- Never invent a project key. If discovery fails (no projects visible), surface the error and ask the user to verify their JIRA permissions.
|
|
197
|
+
- Never set `tracker` without explicit user confirmation — `tracker` is project-wide and switching it changes every downstream skill's behavior.
|
|
198
|
+
- Never write env-level config (api tokens, server URLs) into `.lisa.config.json`.
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: setup-notion
|
|
3
|
+
description: "Configure Notion as the PRD source for this project. Walks the user through creating an internal integration in the target workspace, sharing the PRD database with it, stores the resulting `ntn_*` token in OS keychain (multi-workspace-safe — keyed by workspaceId), validates against the Notion API, and writes `notion.workspaceId`, `notion.prdDatabaseId`, and `notion.values` into `.lisa.config.json`. Idempotent — re-runs update the existing section rather than duplicating it. Offers to set top-level `source: \"notion\"`."
|
|
4
|
+
allowed-tools: ["Bash", "Read", "Write", "Edit", "Skill", "AskUserQuestion"]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Setup Notion: $ARGUMENTS
|
|
8
|
+
|
|
9
|
+
Provision Notion access for this project. After this skill runs, `.lisa.config.json` contains `notion.workspaceId` and `notion.prdDatabaseId`, and the OS keychain has a `lisa-notion` entry keyed by the workspaceId.
|
|
10
|
+
|
|
11
|
+
## Workflow
|
|
12
|
+
|
|
13
|
+
### Step 0 — Pick a setup path
|
|
14
|
+
|
|
15
|
+
Ask via `AskUserQuestion`:
|
|
16
|
+
|
|
17
|
+
> How do you want lisa to talk to Notion for this project?
|
|
18
|
+
>
|
|
19
|
+
> 1. **MCP-only (simplest)** — authenticate the Notion MCP once via browser OAuth; lisa uses it for every operation. Best for: single-workspace developers on a personal laptop. New developers onboard with one OAuth flow, no token sharing, no rotation pain when someone leaves. Skip the rest of this setup.
|
|
20
|
+
> 2. **MCP + internal-integration token (recommended for teams)** — MCP for interactive dev, internal-integration token in keychain for headless / CI / multi-workspace. Continue through token-create steps.
|
|
21
|
+
> 3. **Token-only (headless / CI)** — store a workspace-scoped internal-integration token in the OS keychain; lisa uses curl for everything. Best for: CI pipelines, headless containers. Continue through token-create steps.
|
|
22
|
+
|
|
23
|
+
If the user picks (1) and the MCP is already authenticated to the right workspace (verify by attempting to fetch the configured `prdDatabaseId` via the MCP — success means identity match), write only `notion.workspaceId`, `notion.prdDatabaseId`, and `notion.statusProperty` into `.lisa.config.json` and skip to Step 8 (top-level source offer). If the MCP isn't authed yet, instruct the user to run `mcp__claude_ai_Notion__authenticate` (or the plugin equivalent) and complete the OAuth flow, then re-verify. The PRD database still needs to be shared with the OAuth-granted access — Notion's per-page sharing model applies to OAuth identities the same way it does to internal-integration tokens.
|
|
24
|
+
|
|
25
|
+
If the user picks (2) or (3), continue through the rest of the steps; the token gets stored in addition to (or instead of) the MCP session.
|
|
26
|
+
|
|
27
|
+
### Step 1 — Open the Notion integration page
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
case "$(uname -s)" in
|
|
31
|
+
Darwin) open "https://www.notion.so/profile/integrations" ;;
|
|
32
|
+
Linux) xdg-open "https://www.notion.so/profile/integrations" 2>/dev/null ;;
|
|
33
|
+
MINGW*|MSYS*|CYGWIN*) start "https://www.notion.so/profile/integrations" ;;
|
|
34
|
+
esac
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Print instructions for the user:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
1. Click "New integration".
|
|
41
|
+
2. Name it: lisa-<project-name> (e.g., lisa-gemini, lisa-acme — pick something descriptive).
|
|
42
|
+
3. Associated workspace: pick the workspace your PRDs live in.
|
|
43
|
+
4. Type: "Internal integration".
|
|
44
|
+
5. Capabilities: leave defaults (Read content, Update content, Insert content). No comment / user-info capabilities needed.
|
|
45
|
+
6. Click Save.
|
|
46
|
+
7. On the integration's detail page, click "Show" next to "Internal Integration Token".
|
|
47
|
+
8. Copy the token — starts with `ntn_`. Atlassian-style scoped tokens have a `=<CRC>` suffix; Notion's do NOT, but tokens are still 50+ chars. Watch for clipboard-truncation in some terminals.
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Step 2 — Identify the workspace
|
|
51
|
+
|
|
52
|
+
The user picks a stable identifier for this workspace. Two options:
|
|
53
|
+
|
|
54
|
+
- **Workspace name** (human-readable, e.g., `Gemini Sports`). Easy to recognize, can be ambiguous if a workspace is renamed in Notion. Recommended.
|
|
55
|
+
- **Workspace UUID** (returned by Notion's API). Stable but opaque.
|
|
56
|
+
|
|
57
|
+
Default to the workspace name. After the user stores the token (Step 4), Step 5's `/users/me` call surfaces the actual `bot.workspace_name`; if it differs from what the user typed (capitalization, trailing whitespace), prompt to confirm.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
WORKSPACE=$(jq -r '.notion.workspaceId // empty' .lisa.config.json 2>/dev/null)
|
|
61
|
+
if [ -z "$WORKSPACE" ]; then
|
|
62
|
+
# Prompt the user — accept any non-empty string. They pick the slug; we just store it.
|
|
63
|
+
read -p "Workspace identifier (any stable slug, e.g. 'gemini-sports'): " WORKSPACE
|
|
64
|
+
fi
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Step 3 — Share the PRD database with the integration
|
|
68
|
+
|
|
69
|
+
This is **non-optional**. Notion's permission model is share-based — the integration cannot see any pages or databases until the user explicitly grants access.
|
|
70
|
+
|
|
71
|
+
Print instructions:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
1. In Notion, navigate to your PRD database (or the parent page containing it).
|
|
75
|
+
2. Click the "..." menu in the top right of the database.
|
|
76
|
+
3. Click "Connections".
|
|
77
|
+
4. Find "lisa-<project>" in the list and click "Connect".
|
|
78
|
+
5. Confirm any prompts about granting access.
|
|
79
|
+
|
|
80
|
+
The connection cascades to all child pages of the database by default. New PRDs added under the database automatically inherit access.
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
If `--database=<uuid>` was passed in `$ARGUMENTS`, use it; otherwise prompt:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
DATABASE_ID=$(jq -r '.notion.prdDatabaseId // empty' .lisa.config.json 2>/dev/null)
|
|
87
|
+
if [ -z "$DATABASE_ID" ]; then
|
|
88
|
+
cat <<EOF
|
|
89
|
+
Paste the PRD database URL or ID:
|
|
90
|
+
- URL form: https://notion.so/<workspace>/<database-id>?v=...
|
|
91
|
+
- ID form: 32-char UUID with or without dashes
|
|
92
|
+
EOF
|
|
93
|
+
read -p "Database: " DB_INPUT
|
|
94
|
+
# Extract UUID from URL if needed.
|
|
95
|
+
DATABASE_ID=$(echo "$DB_INPUT" | grep -oE '[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}' | head -1)
|
|
96
|
+
if [ -z "$DATABASE_ID" ]; then
|
|
97
|
+
echo "Error: could not extract a UUID from '$DB_INPUT'." >&2
|
|
98
|
+
exit 1
|
|
99
|
+
fi
|
|
100
|
+
fi
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Step 4 — Store the token via OS keychain (token never enters chat)
|
|
104
|
+
|
|
105
|
+
Same security posture as `setup-atlassian`. Print a platform-specific clipboard-pipe command for the user to run **in their own terminal**:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
case "$(uname -s)" in
|
|
109
|
+
Darwin)
|
|
110
|
+
cat <<EOF
|
|
111
|
+
1. Copy the integration token from the Notion page.
|
|
112
|
+
2. Run this single line in your terminal (leading space keeps it out of zsh history):
|
|
113
|
+
|
|
114
|
+
security delete-generic-password -s lisa-notion -a "$WORKSPACE" 2>/dev/null; TOK="\$(pbpaste)"; security add-generic-password -U -s lisa-notion -a "$WORKSPACE" -w "\$TOK"; unset TOK
|
|
115
|
+
|
|
116
|
+
The token is piped from clipboard straight to keychain — never enters the prompt or chat.
|
|
117
|
+
EOF
|
|
118
|
+
;;
|
|
119
|
+
Linux)
|
|
120
|
+
if command -v secret-tool >/dev/null 2>&1; then
|
|
121
|
+
if command -v wl-paste >/dev/null 2>&1; then CLIP=wl-paste
|
|
122
|
+
elif command -v xclip >/dev/null 2>&1; then CLIP="xclip -selection clipboard -o"
|
|
123
|
+
elif command -v xsel >/dev/null 2>&1; then CLIP="xsel --clipboard --output"
|
|
124
|
+
else CLIP="cat"
|
|
125
|
+
fi
|
|
126
|
+
cat <<EOF
|
|
127
|
+
1. Copy the integration token.
|
|
128
|
+
2. Run this in your terminal:
|
|
129
|
+
|
|
130
|
+
secret-tool clear service lisa-notion account "$WORKSPACE" 2>/dev/null; printf '%s' "\$($CLIP)" | secret-tool store --label="Lisa Notion ($WORKSPACE)" service lisa-notion account "$WORKSPACE"
|
|
131
|
+
|
|
132
|
+
(If no clipboard tool is installed: the command reads from stdin — paste, Ctrl-D.)
|
|
133
|
+
EOF
|
|
134
|
+
else
|
|
135
|
+
cat <<EOF
|
|
136
|
+
libsecret / secret-tool not installed. Options:
|
|
137
|
+
1. Install: sudo apt install libsecret-tools (then re-run /lisa:setup:notion).
|
|
138
|
+
2. Env-var fallback (headless / CI / Docker):
|
|
139
|
+
export NOTION_API_TOKEN_$(echo "$WORKSPACE" | tr '[:upper:]-' '[:lower:]_')="<paste-token>"
|
|
140
|
+
Plaintext on disk — only acceptable on ephemeral / CI environments.
|
|
141
|
+
EOF
|
|
142
|
+
fi
|
|
143
|
+
;;
|
|
144
|
+
MINGW*|MSYS*|CYGWIN*)
|
|
145
|
+
cat <<EOF
|
|
146
|
+
PowerShell:
|
|
147
|
+
|
|
148
|
+
\$tok = Get-Clipboard; cmdkey /generic:"lisa-notion-$WORKSPACE" /user:"$WORKSPACE" /pass:"\$tok"; Remove-Variable tok
|
|
149
|
+
EOF
|
|
150
|
+
;;
|
|
151
|
+
esac
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Never accept the token via chat or stdin into this skill.** Wait for the user to confirm storage.
|
|
155
|
+
|
|
156
|
+
### Step 5 — Verify the token + workspace match
|
|
157
|
+
|
|
158
|
+
Use the same lookup ladder `notion-access` uses:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
read_notion_token() {
|
|
162
|
+
local workspace="$1"
|
|
163
|
+
[ -n "$NOTION_API_TOKEN" ] && { echo "$NOTION_API_TOKEN"; return; }
|
|
164
|
+
local slug=$(echo "$workspace" | tr '[:upper:]-' '[:lower:]_')
|
|
165
|
+
local varname="NOTION_API_TOKEN_${slug}"
|
|
166
|
+
[ -n "${!varname}" ] && { echo "${!varname}"; return; }
|
|
167
|
+
case "$(uname -s)" in
|
|
168
|
+
Darwin) security find-generic-password -s lisa-notion -a "$workspace" -w 2>/dev/null ;;
|
|
169
|
+
Linux) command -v secret-tool >/dev/null && secret-tool lookup service lisa-notion account "$workspace" 2>/dev/null ;;
|
|
170
|
+
MINGW*|MSYS*|CYGWIN*) cmdkey /list:"lisa-notion-${workspace}" 2>/dev/null | grep Password | awk '{print $NF}' ;;
|
|
171
|
+
esac
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
TOKEN=$(read_notion_token "$WORKSPACE")
|
|
175
|
+
if [ -z "$TOKEN" ]; then
|
|
176
|
+
echo "Error: token not retrievable after store. Re-run Step 4." >&2
|
|
177
|
+
exit 1
|
|
178
|
+
fi
|
|
179
|
+
|
|
180
|
+
# Notion tokens — sanity length check. Internal-integration tokens are ~50+ chars; if drastically shorter, a paste truncation happened.
|
|
181
|
+
if [ ${#TOKEN} -lt 40 ]; then
|
|
182
|
+
echo "Warning: token is ${#TOKEN} chars — Notion tokens are typically 50+. Possible truncation." >&2
|
|
183
|
+
fi
|
|
184
|
+
|
|
185
|
+
ME=$(curl -s -H "Authorization: Bearer $TOKEN" \
|
|
186
|
+
-H "Notion-Version: 2022-06-28" \
|
|
187
|
+
"https://api.notion.com/v1/users/me")
|
|
188
|
+
ME_WORKSPACE=$(echo "$ME" | jq -r '.bot.workspace_name // empty')
|
|
189
|
+
|
|
190
|
+
if [ -z "$ME_WORKSPACE" ]; then
|
|
191
|
+
echo "Error: token failed Notion /users/me probe. Response: $ME" >&2
|
|
192
|
+
exit 1
|
|
193
|
+
fi
|
|
194
|
+
|
|
195
|
+
# If the user typed a workspace name that differs from what Notion returns, prompt to align.
|
|
196
|
+
if [ "$ME_WORKSPACE" != "$WORKSPACE" ]; then
|
|
197
|
+
cat <<EOF
|
|
198
|
+
The token belongs to workspace '$ME_WORKSPACE', but you provided '$WORKSPACE' as the identifier.
|
|
199
|
+
Use the Notion-returned name for consistency? (recommended — connection-match check uses this string)
|
|
200
|
+
EOF
|
|
201
|
+
# AskUserQuestion: replace WORKSPACE with $ME_WORKSPACE? recommended yes
|
|
202
|
+
fi
|
|
203
|
+
|
|
204
|
+
# Verify database visibility too (Notion's share model means the token sees only what's been shared).
|
|
205
|
+
DB_PROBE=$(curl -s -o /tmp/setup-notion-db -w "%{http_code}" \
|
|
206
|
+
-H "Authorization: Bearer $TOKEN" -H "Notion-Version: 2022-06-28" \
|
|
207
|
+
"https://api.notion.com/v1/databases/$DATABASE_ID")
|
|
208
|
+
if [ "$DB_PROBE" != "200" ]; then
|
|
209
|
+
cat >&2 <<EOF
|
|
210
|
+
Error: integration cannot see database $DATABASE_ID (HTTP $DB_PROBE).
|
|
211
|
+
The most likely cause is that you skipped Step 3 — sharing the database with the integration.
|
|
212
|
+
Open the database in Notion → "..." → Connections → add 'lisa-<project>' → retry.
|
|
213
|
+
EOF
|
|
214
|
+
exit 1
|
|
215
|
+
fi
|
|
216
|
+
|
|
217
|
+
echo "Token validated. Workspace: $ME_WORKSPACE. Database visible."
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Step 6 — Detect lifecycle value names
|
|
221
|
+
|
|
222
|
+
Read the database schema and find the `Status` property's value list. Compare to lisa defaults and prompt for overrides if names differ.
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
DB_SCHEMA=$(curl -s -H "Authorization: Bearer $TOKEN" -H "Notion-Version: 2022-06-28" \
|
|
226
|
+
"https://api.notion.com/v1/databases/$DATABASE_ID")
|
|
227
|
+
STATUS_PROP=$(jq -r '.properties | to_entries[] | select(.value.type == "status" or .value.type == "select") | .key' <<<"$DB_SCHEMA" | head -1)
|
|
228
|
+
STATUS_VALUES=$(jq -r --arg p "$STATUS_PROP" '.properties[$p] | (.status.options // .select.options) | .[].name' <<<"$DB_SCHEMA")
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
For each lisa role (`draft`, `ready`, `in_review`, `blocked`, `ticketed`, `shipped`), check if its default name (`Draft`, `Ready`, etc.) appears in `$STATUS_VALUES`. If a role's default is missing but a similar-looking value exists, prompt the user to map it via `AskUserQuestion`. If a role has no plausible match, prompt to either create the value in Notion or accept that the lifecycle stage is unrepresented.
|
|
232
|
+
|
|
233
|
+
Collect overrides as a partial values map. Only write keys that differ from defaults.
|
|
234
|
+
|
|
235
|
+
### Step 7 — Write `.lisa.config.json`
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
jq --arg ws "$WORKSPACE" --arg db "$DATABASE_ID" --arg sp "$STATUS_PROP" --argjson values "$VALUES_JSON" '
|
|
239
|
+
.notion = ((.notion // {})
|
|
240
|
+
| .workspaceId = $ws
|
|
241
|
+
| .prdDatabaseId = $db
|
|
242
|
+
| (if $sp != "" then .statusProperty = $sp else . end)
|
|
243
|
+
| (if $values != {} then .values = $values else . end))
|
|
244
|
+
' .lisa.config.json > .lisa.config.json.tmp \
|
|
245
|
+
&& mv .lisa.config.json.tmp .lisa.config.json
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
`VALUES_JSON` is `{}` if all roles use the default names; otherwise contains only the overrides.
|
|
249
|
+
|
|
250
|
+
### Step 8 — Offer to set top-level `source`
|
|
251
|
+
|
|
252
|
+
If `.source` is unset or differs from `"notion"`, ask via `AskUserQuestion`:
|
|
253
|
+
|
|
254
|
+
> Notion is configured. Set top-level `source: "notion"` so `/lisa:intake` (with no args) scans this database for PRDs?
|
|
255
|
+
|
|
256
|
+
If yes:
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
jq '.source = "notion"' .lisa.config.json > .lisa.config.json.tmp \
|
|
260
|
+
&& mv .lisa.config.json.tmp .lisa.config.json
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Step 9 — Verify
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
jq -e '.notion.workspaceId and .notion.prdDatabaseId' .lisa.config.json >/dev/null
|
|
267
|
+
echo "Token validated (${#TOKEN} chars). Workspace: $ME_WORKSPACE. Database: $DATABASE_ID."
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Report success with the resolved workspace, database, status property name, and value overrides (if any). Direct the user to `/lisa:intake` to test.
|
|
271
|
+
|
|
272
|
+
## Idempotency
|
|
273
|
+
|
|
274
|
+
- Re-running this skill replaces fields in the `notion` section without disturbing others. The keychain entry update in Step 4 is the user's manual action — they re-run the same `security` / `secret-tool` / `cmdkey` command.
|
|
275
|
+
- If `notion.workspaceId` and `notion.prdDatabaseId` already exist in config, skip the prompts in Steps 2–3 and go straight to verification.
|
|
276
|
+
|
|
277
|
+
## Rules
|
|
278
|
+
|
|
279
|
+
- Never write the token to `.lisa.config.json`. Tokens stay in keychain or env.
|
|
280
|
+
- Never accept a token via this skill's stdin. Always go through the platform's clipboard-pipe pattern so the value never enters the LLM context.
|
|
281
|
+
- Never auto-create the Notion integration via API — Notion offers no programmatic creation flow, and adding one would require building lisa as a public OAuth app (out of scope here).
|
|
282
|
+
- Never proceed past Step 5 with an unverified token + workspace. Silent cross-workspace operations are exactly the multi-account hazard this design exists to prevent.
|
|
283
|
+
- If the user has multiple Notion accounts, each project's `.lisa.config.local.json` `notion.workspaceId` is the sole disambiguator. There is no "active workspace" concept on the Notion side — the token IS the workspace binding.
|
|
@@ -36,6 +36,8 @@ If a candidate work unit naturally touches multiple repos (e.g., "add field to b
|
|
|
36
36
|
|
|
37
37
|
Reject any work unit whose acceptance criteria reference behavior in a different repo from the one it's scoped to. If you find yourself writing "and the frontend should also...", that's a signal to split.
|
|
38
38
|
|
|
39
|
+
This is the **decomposition-time** strategy (greenfield — you are creating the tickets now, so a parent Story + per-repo children is the natural shape). It is distinct from the **work-time** strategy in the `repo-scope-split` rule, which applies when an agent picks up an *already-existing* ticket to implement and discovers it spans repos: there it narrows the original in place and spins off sibling work units rather than introducing a new parent. Use the phase-appropriate one; do not mix them.
|
|
40
|
+
|
|
39
41
|
### 2. Define Acceptance Criteria
|
|
40
42
|
|
|
41
43
|
For each task, define what "done" looks like:
|
|
@@ -19,11 +19,14 @@ Before any analytical work, confirm the ticket carries the content an implemente
|
|
|
19
19
|
- Validation Journey missing on a runtime-behavior ticket
|
|
20
20
|
- Target backend environment missing on a runtime-behavior ticket
|
|
21
21
|
- Sign-in credentials missing on a ticket that touches authenticated surfaces
|
|
22
|
-
- Single-repo scope violated (Bug / Task / Sub-task spanning repos)
|
|
23
22
|
- Relationship discovery missing (no links AND no documented git + tracker-search outcome)
|
|
24
23
|
|
|
24
|
+
Single-repo scope is handled separately — see below.
|
|
25
|
+
|
|
25
26
|
The caller (jira-agent or github-agent) is responsible for transitioning the ticket to `Blocked` (JIRA status) or relabeling to `status:blocked` (GitHub), reassigning to the **Reporter / original author**, and posting a comment listing the missing requirements. This skill only emits the verdict and the missing-requirements list.
|
|
26
27
|
|
|
28
|
+
**Single-repo scope is split, not blocked.** A cross-repo leaf work unit (Bug / Task / Sub-task / Improvement spanning repos) is a decomposition error the agent owns, not a terminal BLOCKED. Do not emit BLOCKED for it. The caller runs the **work-time split procedure** in the `repo-scope-split` rule (narrow the original to one repo, spin off a sibling per additional repo, link the producer→consumer dependency), then re-runs `tracker-verify` and re-enters triage on the now single-repo ticket. Only fall back to BLOCKED if the split is ambiguous (see "When to block instead of split" in that rule).
|
|
29
|
+
|
|
27
30
|
If `lisa:tracker-verify` returned `PASS` for all the above, proceed to Phase 1.
|
|
28
31
|
|
|
29
32
|
## Phase 1 -- Relevance Check
|
|
@@ -24,6 +24,7 @@ See the `config-resolution` rule for configuration and dispatch table.
|
|
|
24
24
|
|
|
25
25
|
- The GitHub `pr-assets` release lives on the implementation repo (the one with the PR), regardless of which tracker hosts the ticket/issue. All vendor skills upload there.
|
|
26
26
|
- Never post evidence to a different ticket than the one named — `$ARGUMENTS` is the source of truth.
|
|
27
|
+
- **Evidence-manifest gate (leaf work units).** Before dispatching to a vendor skill that transitions the ticket, confirm `EVIDENCE_DIR` contains a non-empty artifact for every `[EVIDENCE: name]` marker declared in the ticket's Validation Journey. If any declared marker has no captured artifact (or only an empty one), stop and report the missing markers by name instead of posting — a leaf work unit (Bug / Task / Sub-task / Improvement) may not advance to its review/Done state with an unsatisfied manifest (see the "Per-Work-Unit Evidence Contract" in the `verification` rule). Epics / Stories / Spikes, and leaf units without a Validation Journey, are exempt.
|
|
27
28
|
|
|
28
29
|
## UI Evidence Checklist (when work is UI-visible)
|
|
29
30
|
|
|
@@ -228,6 +228,7 @@ Agents must follow this sequence unless explicitly instructed otherwise:
|
|
|
228
228
|
3. **If verification fails**: Fix and re-run, don't mark complete
|
|
229
229
|
4. **If verification blocked** (missing tools, services, etc.): Mark as blocked, not complete
|
|
230
230
|
5. **Must not be dependent on CI/CD** if necessary, you may use local deploy methods found in the project manifest, but the verification methods must be listed in the pull request and therefore cannot be dependent on CI/CD completing
|
|
231
|
+
6. **Evidence manifest satisfied (leaf work units)**: For a leaf work unit (Bug / Task / Sub-task / Improvement) whose ticket carries a Validation Journey, do not mark the ticket complete or transition it out of in-progress until every `[EVIDENCE: name]` marker declared on the ticket has a corresponding captured, non-empty artifact attached to the ticket. A missing or empty artifact for any declared marker blocks completion exactly like a failed verification — fix and re-capture, or escalate; never close with an unsatisfied manifest. Epics / Stories / Spikes are exempt (coordination containers, not work units).
|
|
231
232
|
|
|
232
233
|
---
|
|
233
234
|
|
|
@@ -330,6 +331,7 @@ A task is done only when:
|
|
|
330
331
|
- Required verification surfaces and tooling surfaces are used or explicitly unavailable
|
|
331
332
|
- Proof artifacts are captured
|
|
332
333
|
- Every passing empirical verification is codified as a regression test (or has an explicit, documented skip reason from the allowed set)
|
|
334
|
+
- For a leaf work unit, every `[EVIDENCE: name]` marker declared in its Validation Journey has a captured, non-empty artifact attached to the ticket (the evidence manifest is fully satisfied)
|
|
333
335
|
- Spec conformance verdict is `CONFORMS` (not `PARTIAL`, not `DIVERGES`)
|
|
334
336
|
- Verification level is declared
|
|
335
337
|
- Risks and gaps are documented
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Fails if the generated plugin directories (plugins/lisa, plugins/lisa-*) are
|
|
3
|
+
# out of sync with their source (plugins/src). This guards against editing the
|
|
4
|
+
# build artifact directly: build-plugins.sh runs `rm -rf plugins/lisa && cp -r
|
|
5
|
+
# plugins/src/base`, so any artifact-only edit is silently discarded on the next
|
|
6
|
+
# build/release. Two real PRs (#471, #478) were wiped this way before this check
|
|
7
|
+
# existed.
|
|
8
|
+
#
|
|
9
|
+
# The check is a reproducibility test: regenerate the artifacts from source and
|
|
10
|
+
# assert the working tree is unchanged. A diff means either (a) someone edited
|
|
11
|
+
# plugins/lisa** directly instead of plugins/src**, or (b) someone edited
|
|
12
|
+
# plugins/src** but forgot to run `bun run build:plugins` and commit the result.
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
|
|
15
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
16
|
+
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
17
|
+
cd "$ROOT_DIR"
|
|
18
|
+
|
|
19
|
+
if [ -n "$(git status --porcelain -- plugins/ 2>/dev/null)" ]; then
|
|
20
|
+
echo "✗ plugins/ has uncommitted changes before the sync check could run." >&2
|
|
21
|
+
echo " Commit or stash them, then re-run: bun run check:plugins" >&2
|
|
22
|
+
exit 1
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
bun run build:plugins >/dev/null
|
|
26
|
+
|
|
27
|
+
if ! git diff --quiet -- plugins/; then
|
|
28
|
+
echo "✗ Generated plugin artifacts are out of sync with plugins/src." >&2
|
|
29
|
+
echo "" >&2
|
|
30
|
+
echo " Files that changed after rebuilding from source:" >&2
|
|
31
|
+
git --no-pager diff --name-only -- plugins/ | sed 's/^/ /' >&2
|
|
32
|
+
echo "" >&2
|
|
33
|
+
echo " plugins/lisa and plugins/lisa-* are GENERATED from plugins/src by" >&2
|
|
34
|
+
echo " 'bun run build:plugins'. Never edit them directly — the next build" >&2
|
|
35
|
+
echo " overwrites the change." >&2
|
|
36
|
+
echo "" >&2
|
|
37
|
+
echo " Fix:" >&2
|
|
38
|
+
echo " 1. Make your edit under plugins/src/<base|stack>/..." >&2
|
|
39
|
+
echo " 2. Run: bun run build:plugins" >&2
|
|
40
|
+
echo " 3. Commit both plugins/src and the regenerated plugins/lisa*." >&2
|
|
41
|
+
# Leave the tree dirty so the diff is inspectable locally; CI fails on exit.
|
|
42
|
+
exit 1
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
echo "✓ Plugin artifacts are in sync with plugins/src."
|