@codyswann/lisa 3.0.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +50 -23
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/expo/copy-overwrite/scripts/bdd/baseline.mjs +211 -121
- package/expo/copy-overwrite/scripts/bdd/contract.mjs +10 -2
- package/expo/copy-overwrite/scripts/bdd/envelope.mjs +3 -2
- package/expo/copy-overwrite/scripts/bdd/render.mjs +2 -2
- package/expo/copy-overwrite/scripts/check-bdd-coverage.mjs +45 -8
- package/expo/copy-overwrite/scripts/classify-maestro-failures.mjs +775 -0
- package/expo/create-only/.github/workflows/nightly-e2e-report.yml +71 -0
- package/expo/create-only/.maestro/flake-classification.json +21 -0
- package/expo/create-only/bdd/coverage-map.json +1 -2
- package/expo/package-lisa/package.lisa.json +1 -0
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-atlassian-access/SKILL.md +75 -64
- package/plugins/lisa/.codex-plugin/skills/lisa-jam-access/SKILL.md +13 -5
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-access/SKILL.md +30 -10
- package/plugins/lisa/.codex-plugin/skills/lisa-notion-access/SKILL.md +36 -23
- package/plugins/lisa/.codex-plugin/skills/lisa-posthog-access/SKILL.md +16 -6
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/SKILL.md +8 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-sentry-access/SKILL.md +16 -6
- package/plugins/lisa/.codex-plugin/skills/lisa-sonarcloud-access/SKILL.md +7 -1
- package/plugins/lisa/hooks/threshold-ratchet-families.mjs +24 -0
- package/plugins/lisa/rules/eager/credential-substrate-precedence.md +52 -0
- package/plugins/lisa/rules/eager/integration-access-layer.md +7 -3
- package/plugins/lisa/rules/reference/bdd-e2e-coverage.md +19 -8
- package/plugins/lisa/rules/reference/credential-substrate-precedence.md +166 -0
- package/plugins/lisa/rules/reference/integration-access-layer.md +27 -15
- package/plugins/lisa/skills/lisa-atlassian-access/SKILL.md +76 -65
- package/plugins/lisa/skills/lisa-jam-access/SKILL.md +14 -6
- package/plugins/lisa/skills/lisa-linear-access/SKILL.md +31 -11
- package/plugins/lisa/skills/lisa-notion-access/SKILL.md +37 -24
- package/plugins/lisa/skills/lisa-posthog-access/SKILL.md +17 -7
- package/plugins/lisa/skills/lisa-secrets-access/SKILL.md +8 -0
- package/plugins/lisa/skills/lisa-sentry-access/SKILL.md +17 -7
- package/plugins/lisa/skills/lisa-sonarcloud-access/SKILL.md +7 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-atlassian-access/SKILL.md +76 -65
- package/plugins/lisa-agy/skills/lisa-jam-access/SKILL.md +14 -6
- package/plugins/lisa-agy/skills/lisa-linear-access/SKILL.md +31 -11
- package/plugins/lisa-agy/skills/lisa-notion-access/SKILL.md +37 -24
- package/plugins/lisa-agy/skills/lisa-posthog-access/SKILL.md +17 -7
- package/plugins/lisa-agy/skills/lisa-secrets-access/SKILL.md +8 -0
- package/plugins/lisa-agy/skills/lisa-sentry-access/SKILL.md +17 -7
- package/plugins/lisa-agy/skills/lisa-sonarcloud-access/SKILL.md +7 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/hooks/threshold-ratchet-families.mjs +24 -0
- package/plugins/lisa-copilot/rules/eager/credential-substrate-precedence.md +52 -0
- package/plugins/lisa-copilot/rules/eager/integration-access-layer.md +7 -3
- package/plugins/lisa-copilot/rules/reference/bdd-e2e-coverage.md +19 -8
- package/plugins/lisa-copilot/rules/reference/credential-substrate-precedence.md +166 -0
- package/plugins/lisa-copilot/rules/reference/integration-access-layer.md +27 -15
- package/plugins/lisa-copilot/skills/lisa-atlassian-access/SKILL.md +76 -65
- package/plugins/lisa-copilot/skills/lisa-jam-access/SKILL.md +14 -6
- package/plugins/lisa-copilot/skills/lisa-linear-access/SKILL.md +31 -11
- package/plugins/lisa-copilot/skills/lisa-notion-access/SKILL.md +37 -24
- package/plugins/lisa-copilot/skills/lisa-posthog-access/SKILL.md +17 -7
- package/plugins/lisa-copilot/skills/lisa-secrets-access/SKILL.md +8 -0
- package/plugins/lisa-copilot/skills/lisa-sentry-access/SKILL.md +17 -7
- package/plugins/lisa-copilot/skills/lisa-sonarcloud-access/SKILL.md +7 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/threshold-ratchet-families.mjs +24 -0
- package/plugins/lisa-cursor/rules/bdd-e2e-coverage-reference.mdc +19 -8
- package/plugins/lisa-cursor/rules/credential-substrate-precedence-reference.mdc +171 -0
- package/plugins/lisa-cursor/rules/credential-substrate-precedence.mdc +57 -0
- package/plugins/lisa-cursor/rules/integration-access-layer-reference.mdc +27 -15
- package/plugins/lisa-cursor/rules/integration-access-layer.mdc +7 -3
- package/plugins/lisa-cursor/skills/lisa-atlassian-access/SKILL.md +76 -65
- package/plugins/lisa-cursor/skills/lisa-jam-access/SKILL.md +14 -6
- package/plugins/lisa-cursor/skills/lisa-linear-access/SKILL.md +31 -11
- package/plugins/lisa-cursor/skills/lisa-notion-access/SKILL.md +37 -24
- package/plugins/lisa-cursor/skills/lisa-posthog-access/SKILL.md +17 -7
- package/plugins/lisa-cursor/skills/lisa-secrets-access/SKILL.md +8 -0
- package/plugins/lisa-cursor/skills/lisa-sentry-access/SKILL.md +17 -7
- package/plugins/lisa-cursor/skills/lisa-sonarcloud-access/SKILL.md +7 -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-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/hooks/threshold-ratchet-families.mjs +24 -0
- package/plugins/src/base/rules/eager/credential-substrate-precedence.md +52 -0
- package/plugins/src/base/rules/eager/integration-access-layer.md +7 -3
- package/plugins/src/base/rules/reference/bdd-e2e-coverage.md +19 -8
- package/plugins/src/base/rules/reference/credential-substrate-precedence.md +166 -0
- package/plugins/src/base/rules/reference/integration-access-layer.md +27 -15
- package/plugins/src/base/skills/lisa-atlassian-access/SKILL.md +76 -65
- package/plugins/src/base/skills/lisa-jam-access/SKILL.md +14 -6
- package/plugins/src/base/skills/lisa-linear-access/SKILL.md +31 -11
- package/plugins/src/base/skills/lisa-notion-access/SKILL.md +37 -24
- package/plugins/src/base/skills/lisa-posthog-access/SKILL.md +17 -7
- package/plugins/src/base/skills/lisa-secrets-access/SKILL.md +8 -0
- package/plugins/src/base/skills/lisa-sentry-access/SKILL.md +17 -7
- package/plugins/src/base/skills/lisa-sonarcloud-access/SKILL.md +7 -1
- package/rails/copy-overwrite/scripts/threshold-ratchet-families.mjs +24 -0
- package/typescript/copy-overwrite/scripts/check-nightly-e2e-health.mjs +631 -5
- package/typescript/copy-overwrite/scripts/threshold-ratchet-families.mjs +24 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: lisa-atlassian-access
|
|
3
|
-
description: "Vendor-neutral access layer for Atlassian (JIRA + Confluence). Every jira-* and confluence-* skill MUST delegate through this skill rather than calling Atlassian directly.
|
|
3
|
+
description: "Vendor-neutral access layer for Atlassian (JIRA + Confluence). Every jira-* and confluence-* skill MUST delegate through this skill rather than calling Atlassian directly. Per the credential-substrate-precedence contract, resolves a substrate per operation with the ATLASSIAN_API_TOKEN curl path first for reads and writes alike whenever the token is present and identity-matched — binding JIRA writes to the configured cloudId — then acli, then the Atlassian MCP as fallbacks. acli is used when installed and switchable to a profile matching the configured site; mismatched active profiles are skipped only after switch plus re-verification fails, and acli writes are a guarded fallback with post-write tenant assertions."
|
|
4
4
|
allowed-tools: ["Bash", "Read", "Skill"]
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -35,46 +35,17 @@ EMAIL=$(jq -r '.atlassian.email // empty' .lisa.config.local.json 2>/dev/null)
|
|
|
35
35
|
[ -z "$CLOUDID" ] && { echo "Error: atlassian.cloudId not set. Run /lisa:setup:atlassian." >&2; exit 1; }
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
Probe each tier in order; the first that's ready AND identity-matches is the substrate for this operation. Identity-match is verified before any operation; substrates authenticated as a different Atlassian account are switched to the configured profile when one exists, then skipped only if the switch fails or re-verification still mismatches.
|
|
38
|
+
Probe each tier in order; the first that's ready AND identity-matches is the substrate for this operation. The ordering is the shared `credential-substrate-precedence` contract — the configured-provider token substrate leads for **reads and writes alike**, with acli and the MCP as identity-matched fallbacks — not an Atlassian-local choice. Identity-match is verified before any operation; substrates authenticated as a different Atlassian account are switched to the configured profile when one exists, then skipped only if the switch fails or re-verification still mismatches.
|
|
39
39
|
|
|
40
40
|
```bash
|
|
41
41
|
substrate=""
|
|
42
42
|
|
|
43
|
-
# Tier 1:
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
if [ "$OP_KIND" != "jira-write" ] && command -v acli >/dev/null 2>&1 && acli auth status >/dev/null 2>&1; then
|
|
50
|
-
current_site=$(acli auth status 2>/dev/null | awk '/^ Site:/{print $2}')
|
|
51
|
-
if [ "$current_site" != "$SITE" ]; then
|
|
52
|
-
# acli installed but pointing at a different site. Try switching profiles.
|
|
53
|
-
acli auth switch --site "$SITE" ${EMAIL:+--email "$EMAIL"} >/dev/null 2>&1 || true
|
|
54
|
-
current_site=$(acli auth status 2>/dev/null | awk '/^ Site:/{print $2}')
|
|
55
|
-
fi
|
|
56
|
-
if [ "$current_site" = "$SITE" ]; then
|
|
57
|
-
substrate="acli"
|
|
58
|
-
fi
|
|
59
|
-
fi
|
|
60
|
-
|
|
61
|
-
# Tier 2: Atlassian MCP (if acli not ready OR the operation isn't acli-covered)
|
|
62
|
-
# $OP_REQUIRES is a conceptual variable set by the dispatch table to "non-acli" for
|
|
63
|
-
# operations that have no acli adapter (e.g. read-page-descendants). It is not a real
|
|
64
|
-
# shell variable initialized here — the condition is illustrative pseudo-code.
|
|
65
|
-
if [ -z "$substrate" ] || [ "$OP_REQUIRES" = "non-acli" ]; then
|
|
66
|
-
# Probe via mcp__plugin_atlassian_atlassian__getAccessibleAtlassianResources.
|
|
67
|
-
# (Pseudo-code; actual call is the MCP tool invocation, not a bash command.)
|
|
68
|
-
# If the MCP returns a list and $CLOUDID is in it, MCP is identity-matched.
|
|
69
|
-
# If the MCP is unauthenticated or $CLOUDID is NOT in the list, MCP is skipped.
|
|
70
|
-
if mcp_atlassian_authenticated_and_matches_cloudid "$CLOUDID"; then
|
|
71
|
-
: ${substrate:=mcp}
|
|
72
|
-
# Mark MCP as available even if acli already won tier 1 — used for ops acli can't do.
|
|
73
|
-
mcp_available=true
|
|
74
|
-
fi
|
|
75
|
-
fi
|
|
76
|
-
|
|
77
|
-
# Tier 3: curl + API token (headless / multi-account / scoped-token path)
|
|
43
|
+
# Tier 1: curl + API token — the configured-provider substrate, resolved through
|
|
44
|
+
# lisa-secrets-access. Leads for every operation because it is per-invocation-bound:
|
|
45
|
+
# the cloudId-scoped gateway URL and the token's own account carry the tenant inside
|
|
46
|
+
# the request, so no ambient machine-global state can redirect it. acli (one global
|
|
47
|
+
# active account) and the MCP (browser OAuth session) are ambient-bound and therefore
|
|
48
|
+
# TOCTOU-exposed — see credential-substrate-precedence, "tenant safety".
|
|
78
49
|
read_atlassian_token() {
|
|
79
50
|
local email="$1"
|
|
80
51
|
[ -n "$ATLASSIAN_API_TOKEN" ] && { echo "$ATLASSIAN_API_TOKEN"; return; }
|
|
@@ -136,13 +107,50 @@ public static class LisaCred {
|
|
|
136
107
|
esac
|
|
137
108
|
}
|
|
138
109
|
TOKEN=$(read_atlassian_token "$EMAIL")
|
|
139
|
-
[ -n "$TOKEN" ]
|
|
140
|
-
|
|
110
|
+
if [ -n "$TOKEN" ]; then
|
|
111
|
+
# Identity-match before use: /rest/api/3/myself must report the configured account
|
|
112
|
+
# (Step 2). A present-but-wrong token fails the gate loudly instead of quietly
|
|
113
|
+
# deferring to an acli profile or MCP session authenticated somewhere else — that
|
|
114
|
+
# silent success is the bug class the precedence contract exists to surface.
|
|
115
|
+
if atlassian_token_matches_config "$TOKEN" "$EMAIL" "$CLOUDID"; then
|
|
116
|
+
curl_available=true
|
|
141
117
|
substrate="curl"
|
|
142
118
|
else
|
|
143
|
-
:
|
|
119
|
+
echo "Warning: ATLASSIAN_API_TOKEN does not match the configured account/site. Skipping curl tier." >&2
|
|
144
120
|
fi
|
|
145
|
-
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
# Tier 2: acli — identity-matched fallback. Used when no token is available, or for
|
|
124
|
+
# operations with no curl adapter. Never the primary path for JIRA writes when token
|
|
125
|
+
# auth is available: acli stores one machine-global active account and workitem writes
|
|
126
|
+
# cannot pin a cloudId per invocation, so switch-then-write is a TOCTOU risk in
|
|
127
|
+
# multi-account or concurrent sessions. When a write does land here it is the *guarded*
|
|
128
|
+
# fallback documented in the dispatch table (assert, write, re-read, assert, roll back).
|
|
129
|
+
if command -v acli >/dev/null 2>&1 && acli auth status >/dev/null 2>&1; then
|
|
130
|
+
current_site=$(acli auth status 2>/dev/null | awk '/^ Site:/{print $2}')
|
|
131
|
+
if [ "$current_site" != "$SITE" ]; then
|
|
132
|
+
# acli installed but pointing at a different site. Try switching profiles.
|
|
133
|
+
acli auth switch --site "$SITE" ${EMAIL:+--email "$EMAIL"} >/dev/null 2>&1 || true
|
|
134
|
+
current_site=$(acli auth status 2>/dev/null | awk '/^ Site:/{print $2}')
|
|
135
|
+
fi
|
|
136
|
+
if [ "$current_site" = "$SITE" ]; then
|
|
137
|
+
acli_available=true
|
|
138
|
+
# Mark acli available even if curl already won tier 1 — used for ops curl can't do.
|
|
139
|
+
: ${substrate:=acli}
|
|
140
|
+
fi
|
|
141
|
+
fi
|
|
142
|
+
|
|
143
|
+
# Tier 3: Atlassian MCP — first-class interactive fallback, for when neither tier above
|
|
144
|
+
# is available or covers the operation (e.g. an op with no curl and no acli adapter).
|
|
145
|
+
# Probe via mcp__plugin_atlassian_atlassian__getAccessibleAtlassianResources.
|
|
146
|
+
# (Pseudo-code; actual call is the MCP tool invocation, not a bash command.)
|
|
147
|
+
# If the MCP returns a list and $CLOUDID is in it, MCP is identity-matched.
|
|
148
|
+
# If the MCP is unauthenticated or $CLOUDID is NOT in the list, MCP is skipped.
|
|
149
|
+
if mcp_atlassian_authenticated_and_matches_cloudid "$CLOUDID"; then
|
|
150
|
+
: ${substrate:=mcp}
|
|
151
|
+
# Mark MCP as available even if an earlier tier won — used for ops they can't do.
|
|
152
|
+
mcp_available=true
|
|
153
|
+
fi
|
|
146
154
|
|
|
147
155
|
# Fail loudly with actionable remediation if nothing works.
|
|
148
156
|
if [ -z "$substrate" ]; then
|
|
@@ -154,15 +162,25 @@ if [ -z "$substrate" ]; then
|
|
|
154
162
|
cat >&2 <<EOF
|
|
155
163
|
Error: no Atlassian access substrate available for site $SITE.
|
|
156
164
|
|
|
157
|
-
Attempted:
|
|
165
|
+
Attempted (in credential-substrate-precedence order):
|
|
166
|
+
curl — no ATLASSIAN_API_TOKEN found for $EMAIL (env, slug-suffixed env, or keychain) OR the token does not match the configured account/site
|
|
158
167
|
acli — $(command -v acli >/dev/null && echo "installed but identity mismatch or unauthenticated" || echo "not installed")
|
|
159
168
|
MCP — $([ "$plugin_enabled_global" = "true" ] || [ "$plugin_enabled_project" = "true" ] || [ "$plugin_enabled_local" = "true" ] && echo "plugin enabled but not authenticated or cloudId $CLOUDID not in accessible resources" || echo "plugin not enabled in any settings.json scope")
|
|
160
|
-
curl — no ATLASSIAN_API_TOKEN found for $EMAIL (env, slug-suffixed env, or keychain)
|
|
161
169
|
|
|
162
|
-
Remediation paths (
|
|
170
|
+
Remediation paths (the first is the contract's primary path):
|
|
171
|
+
|
|
172
|
+
1. Provision an API token — works headless, in CI, in subagents, and in
|
|
173
|
+
multi-account setups, and is the substrate this project resolves first.
|
|
174
|
+
|
|
175
|
+
Run /lisa:setup:atlassian — guided flow with clipboard-piped keychain store.
|
|
176
|
+
|
|
177
|
+
2. Install acli and authenticate (identity-matched fallback for multi-account developers).
|
|
163
178
|
|
|
164
|
-
|
|
165
|
-
|
|
179
|
+
brew tap atlassian/homebrew-acli && brew install acli
|
|
180
|
+
acli auth login # OAuth as the account matching $EMAIL
|
|
181
|
+
|
|
182
|
+
3. Install the Atlassian MCP plugin (local scope — per-developer, gitignored).
|
|
183
|
+
The supported fallback when no credentials provider is configured.
|
|
166
184
|
|
|
167
185
|
Run in your terminal:
|
|
168
186
|
|
|
@@ -174,25 +192,16 @@ Remediation paths (pick one):
|
|
|
174
192
|
Then restart Claude Code (or run /restart-mcp) to load the plugin, and
|
|
175
193
|
invoke 'mcp__plugin_atlassian_atlassian__authenticate' to complete OAuth.
|
|
176
194
|
|
|
177
|
-
2. Install acli and authenticate (best for multi-account developers).
|
|
178
|
-
|
|
179
|
-
brew tap atlassian/homebrew-acli && brew install acli
|
|
180
|
-
acli auth login # OAuth as the account matching $EMAIL
|
|
181
|
-
|
|
182
|
-
3. Provision an API token (headless / CI / scoped-token environments).
|
|
183
|
-
|
|
184
|
-
Run /lisa:setup:atlassian — guided flow with clipboard-piped keychain store.
|
|
185
|
-
|
|
186
195
|
EOF
|
|
187
196
|
exit 1
|
|
188
197
|
fi
|
|
189
198
|
```
|
|
190
199
|
|
|
191
|
-
Operation dispatch then uses `$substrate` for the primary route. If the operation has no
|
|
200
|
+
Operation dispatch then uses `$substrate` for the primary route. If the operation has no adapter for the selected substrate, fall through in contract order — `$curl_available`, then `$acli_available`, then `$mcp_available` — skipping the tier already tried. The fall-through stops at the first available tier that can perform the operation. A tier that failed identity-match is never in the fall-through set.
|
|
192
201
|
|
|
193
202
|
### Step 2 — Connection-match check
|
|
194
203
|
|
|
195
|
-
The active connection MUST point at the cloudId/site declared in `.lisa.config.json`. Step 1's substrate selection already tries to switch mismatched acli profiles
|
|
204
|
+
The active connection MUST point at the cloudId/site declared in `.lisa.config.json`. Identity-match is mandatory on **every** substrate, tier 1 included (`credential-substrate-precedence`); the "curl mode check" below *is* the tier-1 gate referenced as `atlassian_token_matches_config` in Step 1. Step 1's substrate selection already validates the token account and tries to switch mismatched acli profiles before selection. This step repeats the assertion before any operation runs — defensive in case the substrate state changed since selection.
|
|
196
205
|
|
|
197
206
|
Read configured site:
|
|
198
207
|
|
|
@@ -291,12 +300,12 @@ Rules:
|
|
|
291
300
|
|
|
292
301
|
### Step 3 — Operation dispatch
|
|
293
302
|
|
|
294
|
-
Substrate column meanings:
|
|
303
|
+
Substrate column meanings (ordering per `credential-substrate-precedence`):
|
|
295
304
|
|
|
296
|
-
- **`
|
|
297
|
-
- **`
|
|
298
|
-
- **`
|
|
299
|
-
- Multiple cells filled means tier ordering applies — try
|
|
305
|
+
- **`curl`**: routes through curl + Basic auth + `ATLASSIAN_API_TOKEN` — the configured-provider substrate. Preferred for every operation, read or write, whenever the token is present and identity-matched.
|
|
306
|
+
- **`acli`**: routes through `acli`. Identity-matched fallback — used when no token is available or the op has no curl adapter. For JIRA writes it is the *guarded* fallback (see the tenant-safety rule below).
|
|
307
|
+
- **`MCP`**: routes through the Atlassian MCP. First-class fallback for ops neither tier above covers, when identity-matched (cloudId in `getAccessibleAtlassianResources`).
|
|
308
|
+
- Multiple cells filled means tier ordering applies — try curl, then acli, then MCP, taking the first that has an adapter for the op AND is identity-matched.
|
|
300
309
|
- One cell means only that substrate can perform the op.
|
|
301
310
|
|
|
302
311
|
`<SITE>` = `.atlassian.site` (e.g. `acme.atlassian.net`). `<CLOUDID>` = `.atlassian.cloudId`. `<AUTH>` = `Basic $(printf '%s:%s' "$email" "$ATLASSIAN_API_TOKEN" | base64)`. JIRA curl writes use the cloudId-bound Atlassian gateway `https://api.atlassian.com/ex/jira/<CLOUDID>/rest/api/3/...`; JIRA curl reads may use either that gateway or `https://<SITE>/rest/api/3/...` after the token account check. Confluence uses `/wiki/rest/api/...` (v1) or `/api/v2/...` (v2).
|
|
@@ -334,7 +343,7 @@ Substrate column meanings:
|
|
|
334
343
|
|
|
335
344
|
**acli flag note:** acli's `--output` flag does not exist; the correct flag is `--json`. List commands require `--paginate` or `--limit` (no implicit fetch-all). `acli jira workitem view` defaults to a restricted field set (`key,issuetype,summary,status,assignee,description`), so `read-ticket` MUST pass `--fields '*all'` or an explicit equivalent that includes every downstream dependency: parent, subtasks, issue links, components, labels, priority, status, issue type, summary, description, fix versions, affected versions, attachments, comments, estimates, sprint/story-point fields, and project-required custom fields. Never rely on the default view fields; they hide parent/components/labels and corrupt leaf-only, relationship-search, build-ready, and required-custom-field gates. Several documented adapters are nominal — verify against `acli <subcmd> --help` before relying on them. When acli's adapter is broken or missing for a specific op, fall through to MCP (if identity-matched) then curl per the tier ordering.
|
|
336
345
|
|
|
337
|
-
**JIRA write tenant-safety rule
|
|
346
|
+
**JIRA write tenant-safety rule** — the Atlassian instance of the shared guarded-fallback protocol in `credential-substrate-precedence` (which states the general rule: prefer the per-invocation-bound substrate over the ambient-bound one, for reads and writes alike; the rationale is not restated here). Create, edit, transition, comment, and link are write operations. They MUST use the curl adapter whenever token auth is available because the URL includes `<CLOUDID>` and cannot be redirected by the user-global acli active account. If the flow must fall back to acli for a write, it is a guarded fallback, not the normal path:
|
|
338
347
|
|
|
339
348
|
1. Switch and assert the active `acli auth status` site/email matches config immediately before the write.
|
|
340
349
|
2. Execute the write.
|
|
@@ -378,15 +387,17 @@ Do not paraphrase substrate output beyond JSON normalization.
|
|
|
378
387
|
## Invariants
|
|
379
388
|
|
|
380
389
|
- Caller skills never invoke `acli` or `curl` against Atlassian directly. They only invoke this skill.
|
|
390
|
+
- Tier order is the shared `credential-substrate-precedence` contract — token curl first (reads **and** writes), then acli, then the Atlassian MCP. Do not restate or locally override the ordering here.
|
|
391
|
+
- acli and the Atlassian MCP remain first-class **fallbacks**, not removed tiers: every adapter stays in the dispatch table, and a project with no credentials provider is fully functional on them.
|
|
381
392
|
- Substrate is decided once per skill invocation and never switches mid-operation.
|
|
382
|
-
- Connection match is mandatory. Operations that bypass it (because "the user obviously meant the configured site") are forbidden.
|
|
393
|
+
- Connection match is mandatory on every tier, including the token tier. Operations that bypass it (because "the user obviously meant the configured site") are forbidden. A present-but-wrong token fails the gate rather than deferring to another substrate.
|
|
383
394
|
- Profile mutations (`acli auth switch`) are allowed when acli is the active substrate. The curl substrate never mutates the token — if `ATLASSIAN_API_TOKEN` doesn't match the configured account, fail loud rather than silently substituting.
|
|
384
395
|
- JIRA writes are cloudId-bound by default. `acli` write adapters are fallback-only and must perform post-write tenant assertions plus safe rollback on mismatch.
|
|
385
396
|
- `.lisa.config.local.json` overrides `.lisa.config.json` per-key — the same precedence rule as every other consumer of project config.
|
|
386
397
|
|
|
387
398
|
## Headless behavior
|
|
388
399
|
|
|
389
|
-
In a headless / non-interactive context, the MCP tier is unavailable (its OAuth flow needs a browser). The
|
|
400
|
+
In a headless / non-interactive context, the MCP tier is unavailable (its OAuth flow needs a browser). The ladder collapses to: curl + `ATLASSIAN_API_TOKEN` → acli (if pre-authenticated, e.g., a CI image baked with a service-account token). Because curl is already tier 1 interactively, headless and interactive sessions take the **same primary path** — that is the "headless parity" arm of `credential-substrate-precedence`, and it is why a credential problem reproduces on a laptop instead of only in cron. Never block on interactive prompts. If both fail readiness checks, exit non-zero with a deterministic error.
|
|
390
401
|
|
|
391
402
|
Treat all four of these as headless:
|
|
392
403
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: lisa-jam-access
|
|
3
|
-
description: "Vendor-neutral access layer for Jam. Jam triage rules and skills MUST delegate through this skill rather than calling Jam MCP tools directly.
|
|
3
|
+
description: "Vendor-neutral access layer for Jam. Jam triage rules and skills MUST delegate through this skill rather than calling Jam MCP tools directly. Per the credential-substrate-precedence contract, resolves the JAM_PAT-authenticated Jam CLI first when the PAT is present and identity-matched, then falls back to the Jam MCP."
|
|
4
4
|
allowed-tools: ["Bash", "Read", "Skill"]
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -21,13 +21,20 @@ Return parsed JSON or a concise structured summary in a `<result>` block.
|
|
|
21
21
|
|
|
22
22
|
## Substrate Selection
|
|
23
23
|
|
|
24
|
-
Probe in order
|
|
24
|
+
Probe in order — the ordering is the shared `credential-substrate-precedence`
|
|
25
|
+
contract, not a Jam-local choice. The first tier that is ready **and**
|
|
26
|
+
identity-matches the configured Jam account is used; one authenticated elsewhere
|
|
27
|
+
is skipped, never used.
|
|
25
28
|
|
|
26
|
-
1.
|
|
27
|
-
|
|
29
|
+
1. **Tier 1 — configured-provider substrate: Jam CLI authenticated with `JAM_PAT`**,
|
|
30
|
+
resolved through `lisa-secrets-access`.
|
|
31
|
+
2. **Tier 2 — interactive MCP fallback: Jam MCP**, if the tool is available and
|
|
32
|
+
authenticated. Used when tier 1 is genuinely unavailable: no `JAM_PAT`, no CLI
|
|
33
|
+
adapter for the operation, or a Jam outage.
|
|
28
34
|
|
|
29
35
|
Jam documents a PAT-authenticated CLI that is cleaner for remote routines than
|
|
30
|
-
editing `.mcp.json` headers
|
|
36
|
+
editing `.mcp.json` headers, and it is the same substrate interactively and
|
|
37
|
+
headlessly — which is why it leads. The CLI tier uses:
|
|
31
38
|
|
|
32
39
|
```bash
|
|
33
40
|
curl -fsSL https://native.jam.dev/install | bash
|
|
@@ -44,7 +51,8 @@ Error: no Jam access substrate available. Authenticate the Jam MCP or set JAM_PA
|
|
|
44
51
|
|
|
45
52
|
## Invariants
|
|
46
53
|
|
|
47
|
-
-
|
|
54
|
+
- Tier order is `credential-substrate-precedence`: `JAM_PAT` CLI first, Jam MCP as
|
|
55
|
+
a preserved first-class fallback. Do not retry a failed tier blindly.
|
|
48
56
|
- Never commit a Jam PAT into `.mcp.json` or any generated setup artifact.
|
|
49
57
|
- Headless Jam access requires `native.jam.dev` for the installer and
|
|
50
58
|
`api.jam.dev` for CLI/API calls in any custom remote network allowlist.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: lisa-linear-access
|
|
3
|
-
description: "Vendor-neutral access layer for Linear. Linear skills MUST delegate through this skill rather than calling Linear MCP tools or Linear GraphQL directly.
|
|
3
|
+
description: "Vendor-neutral access layer for Linear. Linear skills MUST delegate through this skill rather than calling Linear MCP tools or Linear GraphQL directly. Per the credential-substrate-precedence contract, resolves LINEAR_API_KEY + Linear GraphQL first — ahead of the Linear MCP — whenever the key is present and identity-matches the configured workspace/team, and falls back to the Linear MCP when the token path is unavailable. Identity-match is mandatory on both substrates."
|
|
4
4
|
allowed-tools: ["Bash", "Read", "Skill"]
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -46,15 +46,27 @@ WORKSPACE=$(jq -r '.linear.workspace // empty' .lisa.config.json 2>/dev/null)
|
|
|
46
46
|
TEAM_KEY=$(jq -r '.linear.teamKey // empty' .lisa.config.json 2>/dev/null)
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
Probe in order
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
Probe in order — the ordering is the shared `credential-substrate-precedence`
|
|
50
|
+
contract, not a Linear-local choice. The first tier that is ready **and**
|
|
51
|
+
identity-matches wins; a substrate authenticated against a different
|
|
52
|
+
workspace/team is **skipped, never used**, at either tier.
|
|
53
|
+
|
|
54
|
+
1. **Tier 1 — configured-provider substrate: `LINEAR_API_KEY` with Linear GraphQL**
|
|
55
|
+
(`https://api.linear.app/graphql`), resolved through `lisa-secrets-access`.
|
|
56
|
+
Identity-match by querying `viewer { organization { urlKey } }` (plus
|
|
57
|
+
`teams` when `linear.teamKey` is configured) and comparing to
|
|
58
|
+
`.lisa.config.json`. A key that resolves to a different organization fails the
|
|
59
|
+
gate — warn, skip the tier, and continue down the ladder.
|
|
60
|
+
2. **Tier 2 — interactive MCP fallback: Linear MCP**, if
|
|
61
|
+
`mcp__linear-server__list_teams` is available and can list the configured
|
|
62
|
+
workspace/team (that listing *is* the identity match). Used when tier 1 is
|
|
63
|
+
genuinely unavailable: no `LINEAR_API_KEY`, no GraphQL adapter for the
|
|
64
|
+
operation, or a Linear API outage.
|
|
54
65
|
|
|
55
66
|
The Linear GraphQL docs support personal API keys for scripts and authenticate
|
|
56
|
-
with an `Authorization: <API_KEY>` header
|
|
57
|
-
|
|
67
|
+
with an `Authorization: <API_KEY>` header, so the token tier works identically on
|
|
68
|
+
a developer laptop, in CI, in a cloud routine, and in a subagent — which is why it
|
|
69
|
+
leads. If neither tier works, fail with:
|
|
58
70
|
|
|
59
71
|
```text
|
|
60
72
|
Error: no Linear access substrate available. Authenticate the Linear MCP or set LINEAR_API_KEY.
|
|
@@ -204,8 +216,16 @@ query($id:String!){
|
|
|
204
216
|
|
|
205
217
|
## Invariants
|
|
206
218
|
|
|
207
|
-
-
|
|
208
|
-
|
|
209
|
-
|
|
219
|
+
- Tier order is the shared `credential-substrate-precedence` contract:
|
|
220
|
+
`LINEAR_API_KEY` + GraphQL first when present and identity-matched, then the
|
|
221
|
+
Linear MCP. Do not restate or locally override the ordering here.
|
|
222
|
+
- The Linear MCP remains a first-class **fallback**, not a removed tier: it stays
|
|
223
|
+
the substrate whenever `LINEAR_API_KEY` is absent, the operation has no GraphQL
|
|
224
|
+
adapter, or the token path is failing.
|
|
225
|
+
- Identity-match is mandatory on **both** substrates. A substrate authenticated
|
|
226
|
+
against a different Linear organization or team is skipped, never used — that
|
|
227
|
+
includes a present-but-wrong `LINEAR_API_KEY`, which fails the gate loudly
|
|
228
|
+
instead of silently deferring to an authenticated MCP.
|
|
229
|
+
- Missing token plus missing MCP is a hard failure naming `LINEAR_API_KEY`.
|
|
210
230
|
- Mutations send only the fields being changed, matching existing Linear skill
|
|
211
231
|
guidance that `save_*` style updates should not clobber unrelated fields.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: lisa-notion-access
|
|
3
|
-
description: "Vendor-neutral access layer for Notion. Every notion-* skill MUST delegate through this skill rather than invoking the Notion REST API or any Notion MCP directly.
|
|
3
|
+
description: "Vendor-neutral access layer for Notion. Every notion-* skill MUST delegate through this skill rather than invoking the Notion REST API or any Notion MCP directly. Per the credential-substrate-precedence contract, resolves a substrate per operation in this order: (1) curl + Bearer auth + internal-integration token when the token is present and identity-matches the configured workspace, (2) Notion MCP as fallback if authenticated and the configured prdDatabaseId is fetchable through it. Verifies the active connection matches `.lisa.config.json` before every operation — substrates authenticated as a different Notion workspace are skipped, not used."
|
|
4
4
|
allowed-tools: ["Bash", "Read", "Skill"]
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -38,20 +38,15 @@ DB_ID=$(jq -r '.notion.prdDatabaseId // empty' .lisa.config.json)
|
|
|
38
38
|
[ -z "$DB_ID" ] && { echo "Error: notion.prdDatabaseId not set. Run /lisa:setup:notion." >&2; exit 1; }
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
Probe each tier in order; the first that's ready AND identity-matches is the substrate for this operation. Identity-match is verified before any operation; substrates authenticated as a different workspace are skipped, not used.
|
|
41
|
+
Probe each tier in order; the first that's ready AND identity-matches is the substrate for this operation. The ordering is the shared `credential-substrate-precedence` contract — the configured-provider token substrate leads, the interactive MCP is the fallback — not a Notion-local choice. Identity-match is verified before any operation; substrates authenticated as a different workspace are skipped, not used, at **every** tier.
|
|
42
42
|
|
|
43
43
|
```bash
|
|
44
44
|
substrate=""
|
|
45
45
|
|
|
46
|
-
# Tier 1:
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
if mcp_notion_can_fetch_database "$DB_ID"; then
|
|
51
|
-
substrate="mcp"
|
|
52
|
-
fi
|
|
53
|
-
|
|
54
|
-
# Tier 2: curl + API token
|
|
46
|
+
# Tier 1: curl + API token — the configured-provider substrate, resolved through
|
|
47
|
+
# lisa-secrets-access. Leads because it is identical on a laptop, in CI, in a cloud
|
|
48
|
+
# routine, and in a subagent, and because its workspace binding travels with the
|
|
49
|
+
# request instead of coming from ambient browser-session state.
|
|
55
50
|
read_notion_token() {
|
|
56
51
|
local workspace="$1"
|
|
57
52
|
[ -n "$NOTION_API_TOKEN" ] && { echo "$NOTION_API_TOKEN"; return; }
|
|
@@ -120,12 +115,28 @@ if [ -n "$TOKEN" ]; then
|
|
|
120
115
|
"https://api.notion.com/v1/users/me")
|
|
121
116
|
me_workspace=$(echo "$me" | jq -r '.bot.workspace_name // .bot.workspace_id // empty')
|
|
122
117
|
if [ -n "$me_workspace" ] && [ "$me_workspace" = "$WORKSPACE" ]; then
|
|
123
|
-
|
|
118
|
+
substrate="curl"
|
|
124
119
|
elif [ -n "$me_workspace" ]; then
|
|
120
|
+
# A present-but-wrong token fails the gate rather than deferring to the MCP.
|
|
121
|
+
# Silently succeeding through an MCP authenticated elsewhere is the exact bug
|
|
122
|
+
# the precedence contract exists to surface.
|
|
125
123
|
echo "Warning: Notion token belongs to workspace '$me_workspace' but config declares '$WORKSPACE'. Skipping curl tier." >&2
|
|
126
124
|
fi
|
|
127
125
|
fi
|
|
128
126
|
|
|
127
|
+
# Tier 2: Notion MCP — first-class fallback, used when tier 1 is genuinely
|
|
128
|
+
# unavailable (no token, no curl adapter for the operation, or Notion API outage).
|
|
129
|
+
# Identity-matched by fetching the configured PRD database.
|
|
130
|
+
# Pseudo-code; actual call is the MCP tool invocation.
|
|
131
|
+
# Try to fetch DB_ID through the MCP. Success → MCP is authed to the right workspace.
|
|
132
|
+
# 404 / object_not_found → MCP is authed elsewhere (or unauthenticated). Skip.
|
|
133
|
+
if mcp_notion_can_fetch_database "$DB_ID"; then
|
|
134
|
+
: ${substrate:=mcp}
|
|
135
|
+
# Mark the MCP available even when curl already won tier 1 — the dispatch table
|
|
136
|
+
# falls through to it for operations curl has no adapter for.
|
|
137
|
+
mcp_available=true
|
|
138
|
+
fi
|
|
139
|
+
|
|
129
140
|
# Fail loudly with actionable remediation if nothing works.
|
|
130
141
|
if [ -z "$substrate" ]; then
|
|
131
142
|
# Detect plugin enablement state for the suggestion.
|
|
@@ -136,14 +147,19 @@ if [ -z "$substrate" ]; then
|
|
|
136
147
|
cat >&2 <<EOF
|
|
137
148
|
Error: no Notion access substrate available for workspace '$WORKSPACE'.
|
|
138
149
|
|
|
139
|
-
Attempted:
|
|
140
|
-
MCP — $([ "$plugin_enabled_global" = "true" ] || [ "$plugin_enabled_project" = "true" ] || [ "$plugin_enabled_local" = "true" ] && echo "plugin enabled but not authenticated or cannot fetch configured prdDatabaseId" || echo "plugin not enabled in any settings.json scope")
|
|
150
|
+
Attempted (in credential-substrate-precedence order):
|
|
141
151
|
curl — no NOTION_API_TOKEN found for $WORKSPACE (env, slug-suffixed env, or keychain) OR token belongs to a different workspace
|
|
152
|
+
MCP — $([ "$plugin_enabled_global" = "true" ] || [ "$plugin_enabled_project" = "true" ] || [ "$plugin_enabled_local" = "true" ] && echo "plugin enabled but not authenticated or cannot fetch configured prdDatabaseId" || echo "plugin not enabled in any settings.json scope")
|
|
153
|
+
|
|
154
|
+
Remediation paths (the first is the contract's primary path):
|
|
142
155
|
|
|
143
|
-
|
|
156
|
+
1. Provision an internal-integration API token — works headless, in CI, and in
|
|
157
|
+
multi-workspace setups, and is the substrate this project resolves first.
|
|
144
158
|
|
|
145
|
-
|
|
146
|
-
|
|
159
|
+
Run /lisa:setup:notion — guided flow with clipboard-piped keychain store.
|
|
160
|
+
|
|
161
|
+
2. Install the Notion MCP plugin (local scope — per-developer, gitignored).
|
|
162
|
+
The supported fallback when no credentials provider is configured.
|
|
147
163
|
|
|
148
164
|
Run in your terminal:
|
|
149
165
|
|
|
@@ -157,10 +173,6 @@ Remediation paths (pick one):
|
|
|
157
173
|
Also share the configured prdDatabaseId with the integration via
|
|
158
174
|
the page's '•••' menu → Connections.
|
|
159
175
|
|
|
160
|
-
2. Provision an internal-integration API token (headless / CI / multi-workspace).
|
|
161
|
-
|
|
162
|
-
Run /lisa:setup:notion — guided flow with clipboard-piped keychain store.
|
|
163
|
-
|
|
164
176
|
EOF
|
|
165
177
|
exit 1
|
|
166
178
|
fi
|
|
@@ -225,14 +237,15 @@ exec_op() {
|
|
|
225
237
|
## Invariants
|
|
226
238
|
|
|
227
239
|
- Caller skills never call `curl https://api.notion.com/...` or any `mcp__*notion*` tool directly. They invoke this skill via the Skill tool with an operation name and arguments.
|
|
228
|
-
- Substrate is selected per skill invocation following the tier ladder. The first tier that's available AND identity-matches `notion.workspaceId` wins.
|
|
229
|
-
- The
|
|
240
|
+
- Substrate is selected per skill invocation following the tier ladder defined by the shared `credential-substrate-precedence` contract — internal-integration token first, Notion MCP as fallback. The first tier that's available AND identity-matches `notion.workspaceId` wins. Do not restate or locally override the ordering here.
|
|
241
|
+
- The Notion MCP stays a first-class fallback, not a removed tier: it is the substrate whenever no token is configured, the operation has no curl adapter, or the Notion API is failing.
|
|
242
|
+
- The connection-match check is mandatory at every tier. Skipping it (because "the user obviously meant this workspace") is forbidden — silent cross-workspace operations are exactly the multi-account hazard this design exists to prevent. A present-but-wrong token fails the gate rather than deferring to an MCP authenticated somewhere else.
|
|
230
243
|
- API tokens never mutate. If the configured workspace's token is wrong or missing, fail loudly and tell the user to run `/lisa:setup:notion`.
|
|
231
244
|
- `Notion-Version` is pinned to `2022-06-28` — the version every existing notion-* skill targets. Bumping it is a coordinated change across the access skill and all callers.
|
|
232
245
|
|
|
233
246
|
## Headless behavior
|
|
234
247
|
|
|
235
|
-
In a headless / non-interactive context (no TTY, `CI=true`, or `-p` mode), the MCP tier is unavailable (its OAuth flow needs a browser)
|
|
248
|
+
In a headless / non-interactive context (no TTY, `CI=true`, or `-p` mode), the MCP tier is unavailable (its OAuth flow needs a browser) and the ladder collapses to curl + `NOTION_API_TOKEN` — which is already tier 1 interactively. That is the point of the ordering: headless and interactive sessions take the **same primary path**, so a credential problem reproduces on a laptop instead of only in cron (`credential-substrate-precedence`, "headless parity"). Same skill code runs identically; only the availability of the fallback changes.
|
|
236
249
|
|
|
237
250
|
## Per-page sharing prerequisite
|
|
238
251
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: lisa-posthog-access
|
|
3
|
-
description: "Vendor-neutral access layer for PostHog. PostHog skills and observability rules MUST delegate through this skill rather than calling PostHog MCP tools or REST directly.
|
|
3
|
+
description: "Vendor-neutral access layer for PostHog. PostHog skills and observability rules MUST delegate through this skill rather than calling PostHog MCP tools or REST directly. Per the credential-substrate-precedence contract, resolves POSTHOG_PERSONAL_API_KEY bearer auth first when present and identity-matched to the configured project, then falls back to the PostHog MCP."
|
|
4
4
|
allowed-tools: ["Bash", "Read", "Skill"]
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -22,13 +22,21 @@ Return parsed JSON in a `<result>` block.
|
|
|
22
22
|
|
|
23
23
|
## Substrate Selection
|
|
24
24
|
|
|
25
|
-
Probe in order
|
|
25
|
+
Probe in order — the ordering is the shared `credential-substrate-precedence`
|
|
26
|
+
contract, not a PostHog-local choice. The first tier that is ready **and**
|
|
27
|
+
identity-matches the configured project is used; a substrate authenticated against
|
|
28
|
+
a different project is skipped, never used.
|
|
26
29
|
|
|
27
|
-
1.
|
|
28
|
-
|
|
30
|
+
1. **Tier 1 — configured-provider substrate: `POSTHOG_PERSONAL_API_KEY`** bearer
|
|
31
|
+
token against the configured PostHog host, resolved through
|
|
32
|
+
`lisa-secrets-access`.
|
|
33
|
+
2. **Tier 2 — interactive MCP fallback: PostHog MCP**, if available and
|
|
34
|
+
authenticated. Used when tier 1 is genuinely unavailable: no
|
|
35
|
+
`POSTHOG_PERSONAL_API_KEY`, no REST adapter for the operation, or a PostHog
|
|
36
|
+
outage.
|
|
29
37
|
|
|
30
|
-
PostHog documents personal API keys and bearer authentication
|
|
31
|
-
tier uses:
|
|
38
|
+
PostHog documents personal API keys and bearer authentication, and the same key
|
|
39
|
+
works interactively and headlessly — which is why it leads. The REST tier uses:
|
|
32
40
|
|
|
33
41
|
```bash
|
|
34
42
|
POSTHOG_HOST=${POSTHOG_HOST:-https://app.posthog.com}
|
|
@@ -54,7 +62,9 @@ Error: no PostHog access substrate available. Authenticate the PostHog MCP or se
|
|
|
54
62
|
|
|
55
63
|
## Invariants
|
|
56
64
|
|
|
57
|
-
-
|
|
65
|
+
- Tier order is `credential-substrate-precedence`: `POSTHOG_PERSONAL_API_KEY`
|
|
66
|
+
first, the PostHog MCP as a preserved first-class fallback. Identity-match
|
|
67
|
+
against the configured project is mandatory on every tier.
|
|
58
68
|
- `POSTHOG_HOST` defaults to PostHog Cloud but can point at a self-hosted
|
|
59
69
|
deployment.
|
|
60
70
|
- Consumer skills do not embed PostHog REST paths.
|
|
@@ -10,6 +10,14 @@ Single chokepoint for reading credentials. Caller skills MUST go through this
|
|
|
10
10
|
|
|
11
11
|
The rule this exists to enforce: **a secret lives in exactly one store.** Every local cache is a copy that will eventually drift from its source, and a drifted copy is indistinguishable from a valid one until something fails in production.
|
|
12
12
|
|
|
13
|
+
This skill is also the chokepoint that feeds **tier 1** of the shared
|
|
14
|
+
`credential-substrate-precedence` contract: every `*-access` skill resolves its
|
|
15
|
+
configured-provider token or CLI credential here, ahead of any interactive MCP. That
|
|
16
|
+
is what makes "provider-first" actionable rather than aspirational — including the
|
|
17
|
+
`tool:` note line below, which declares which CLI a given credential is expected to
|
|
18
|
+
drive. This skill decides *where a credential comes from*; it never decides substrate
|
|
19
|
+
ordering, which is settled once in that contract.
|
|
20
|
+
|
|
13
21
|
## Two axes, not one
|
|
14
22
|
|
|
15
23
|
A **provider** is where secrets live. A **surface** is where the running code lives, and it determines how secrets reach that code. These are independent: the same Bitwarden project serves a laptop, a CI runner, and a remote agent container, but each obtains its values differently.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: lisa-sentry-access
|
|
3
|
-
description: "Vendor-neutral access layer for Sentry. Sentry-oriented skills MUST delegate through this skill rather than calling Sentry MCP tools, sentry-cli, or REST directly.
|
|
3
|
+
description: "Vendor-neutral access layer for Sentry. Sentry-oriented skills MUST delegate through this skill rather than calling Sentry MCP tools, sentry-cli, or REST directly. Per the credential-substrate-precedence contract, resolves SENTRY_AUTH_TOKEN (REST, or sentry-cli authenticated from the same token) first when present and identity-matched to the configured org/project, then falls back to the Sentry MCP."
|
|
4
4
|
allowed-tools: ["Bash", "Read", "Skill"]
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -22,13 +22,21 @@ Return parsed JSON in a `<result>` block.
|
|
|
22
22
|
|
|
23
23
|
## Substrate Selection
|
|
24
24
|
|
|
25
|
-
Probe in order
|
|
25
|
+
Probe in order — the ordering is the shared `credential-substrate-precedence`
|
|
26
|
+
contract, not a Sentry-local choice. The first tier that is ready **and**
|
|
27
|
+
identity-matches the configured org/project is used; a substrate authenticated
|
|
28
|
+
against a different org is skipped, never used.
|
|
26
29
|
|
|
27
|
-
1.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
1. **Tier 1 — configured-provider substrate: `SENTRY_AUTH_TOKEN`** bearer token
|
|
31
|
+
against Sentry REST, resolved through `lisa-secrets-access`.
|
|
32
|
+
2. **Tier 1a — `sentry-cli`**, if installed and authenticated to the requested
|
|
33
|
+
org/project from that same token (the CLI arm of the provider substrate).
|
|
34
|
+
3. **Tier 2 — interactive MCP fallback: Sentry MCP**, if available and
|
|
35
|
+
authenticated. Used when tier 1 is genuinely unavailable: no
|
|
36
|
+
`SENTRY_AUTH_TOKEN`, no REST/CLI adapter for the operation, or a Sentry outage.
|
|
30
37
|
|
|
31
|
-
Sentry documents API auth tokens for REST API calls
|
|
38
|
+
Sentry documents API auth tokens for REST API calls, and the same token works
|
|
39
|
+
interactively and headlessly — which is why it leads. The REST tier uses:
|
|
32
40
|
|
|
33
41
|
```bash
|
|
34
42
|
sentry_api() {
|
|
@@ -50,7 +58,9 @@ Error: no Sentry access substrate available. Authenticate Sentry MCP/CLI or set
|
|
|
50
58
|
|
|
51
59
|
## Invariants
|
|
52
60
|
|
|
53
|
-
-
|
|
61
|
+
- Tier order is `credential-substrate-precedence`: `SENTRY_AUTH_TOKEN` first, the
|
|
62
|
+
Sentry MCP as a preserved first-class fallback. Identity-match against the
|
|
63
|
+
configured org/project is mandatory on every tier.
|
|
54
64
|
- Org/project come from `.sentryclirc`, `.lisa.config.json`, or explicit
|
|
55
65
|
operation args; never infer by searching all accessible orgs.
|
|
56
66
|
- Consumer skills do not embed Sentry REST paths.
|
|
@@ -15,7 +15,13 @@ this skill owns the tool selection.
|
|
|
15
15
|
There is exactly one substrate: the **official SonarQube MCP server**, provided by
|
|
16
16
|
the `sonarqube` plugin and launched by the `sonar` CLI (`sonar run mcp`). It
|
|
17
17
|
authenticates headlessly from environment variables — no browser, no OS keychain —
|
|
18
|
-
so it is the same substrate on a developer machine and in a headless cloud routine
|
|
18
|
+
so it is the same substrate on a developer machine and in a headless cloud routine.
|
|
19
|
+
|
|
20
|
+
That makes this skill conformant with `credential-substrate-precedence` as a
|
|
21
|
+
single-substrate access layer: this MCP **is** the configured-provider substrate
|
|
22
|
+
(it is token-authenticated, not browser-OAuth), so there is no interactive tier to
|
|
23
|
+
demote and no second REST tier to add. Identity-match against the configured org
|
|
24
|
+
remains mandatory, as on every substrate.
|
|
19
25
|
|
|
20
26
|
- `SONARQUBE_CLI_TOKEN` — required (Sonar user/analysis token).
|
|
21
27
|
- `SONARQUBE_CLI_ORG` — required for SonarQube Cloud.
|