@codyswann/lisa 3.0.0 → 3.1.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.
Files changed (116) hide show
  1. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  2. package/dist/core/upstream-evidence-manifest.js +22 -11
  3. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  4. package/expo/create-only/.github/workflows/nightly-e2e-report.yml +71 -0
  5. package/package.json +1 -1
  6. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  7. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  8. package/plugins/lisa/.codex-plugin/skills/lisa-atlassian-access/SKILL.md +75 -64
  9. package/plugins/lisa/.codex-plugin/skills/lisa-jam-access/SKILL.md +13 -5
  10. package/plugins/lisa/.codex-plugin/skills/lisa-linear-access/SKILL.md +30 -10
  11. package/plugins/lisa/.codex-plugin/skills/lisa-notion-access/SKILL.md +36 -23
  12. package/plugins/lisa/.codex-plugin/skills/lisa-posthog-access/SKILL.md +16 -6
  13. package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/SKILL.md +8 -0
  14. package/plugins/lisa/.codex-plugin/skills/lisa-sentry-access/SKILL.md +16 -6
  15. package/plugins/lisa/.codex-plugin/skills/lisa-sonarcloud-access/SKILL.md +7 -1
  16. package/plugins/lisa/rules/eager/integration-access-layer.md +7 -3
  17. package/plugins/lisa/rules/reference/credential-substrate-precedence.md +166 -0
  18. package/plugins/lisa/rules/reference/integration-access-layer.md +27 -15
  19. package/plugins/lisa/skills/lisa-atlassian-access/SKILL.md +76 -65
  20. package/plugins/lisa/skills/lisa-jam-access/SKILL.md +14 -6
  21. package/plugins/lisa/skills/lisa-linear-access/SKILL.md +31 -11
  22. package/plugins/lisa/skills/lisa-notion-access/SKILL.md +37 -24
  23. package/plugins/lisa/skills/lisa-posthog-access/SKILL.md +17 -7
  24. package/plugins/lisa/skills/lisa-secrets-access/SKILL.md +8 -0
  25. package/plugins/lisa/skills/lisa-sentry-access/SKILL.md +17 -7
  26. package/plugins/lisa/skills/lisa-sonarcloud-access/SKILL.md +7 -1
  27. package/plugins/lisa-agy/plugin.json +1 -1
  28. package/plugins/lisa-agy/skills/lisa-atlassian-access/SKILL.md +76 -65
  29. package/plugins/lisa-agy/skills/lisa-jam-access/SKILL.md +14 -6
  30. package/plugins/lisa-agy/skills/lisa-linear-access/SKILL.md +31 -11
  31. package/plugins/lisa-agy/skills/lisa-notion-access/SKILL.md +37 -24
  32. package/plugins/lisa-agy/skills/lisa-posthog-access/SKILL.md +17 -7
  33. package/plugins/lisa-agy/skills/lisa-secrets-access/SKILL.md +8 -0
  34. package/plugins/lisa-agy/skills/lisa-sentry-access/SKILL.md +17 -7
  35. package/plugins/lisa-agy/skills/lisa-sonarcloud-access/SKILL.md +7 -1
  36. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  37. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  38. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  39. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  40. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  41. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa-copilot/rules/eager/integration-access-layer.md +7 -3
  43. package/plugins/lisa-copilot/rules/reference/credential-substrate-precedence.md +166 -0
  44. package/plugins/lisa-copilot/rules/reference/integration-access-layer.md +27 -15
  45. package/plugins/lisa-copilot/skills/lisa-atlassian-access/SKILL.md +76 -65
  46. package/plugins/lisa-copilot/skills/lisa-jam-access/SKILL.md +14 -6
  47. package/plugins/lisa-copilot/skills/lisa-linear-access/SKILL.md +31 -11
  48. package/plugins/lisa-copilot/skills/lisa-notion-access/SKILL.md +37 -24
  49. package/plugins/lisa-copilot/skills/lisa-posthog-access/SKILL.md +17 -7
  50. package/plugins/lisa-copilot/skills/lisa-secrets-access/SKILL.md +8 -0
  51. package/plugins/lisa-copilot/skills/lisa-sentry-access/SKILL.md +17 -7
  52. package/plugins/lisa-copilot/skills/lisa-sonarcloud-access/SKILL.md +7 -1
  53. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-cursor/rules/credential-substrate-precedence-reference.mdc +171 -0
  55. package/plugins/lisa-cursor/rules/integration-access-layer-reference.mdc +27 -15
  56. package/plugins/lisa-cursor/rules/integration-access-layer.mdc +7 -3
  57. package/plugins/lisa-cursor/skills/lisa-atlassian-access/SKILL.md +76 -65
  58. package/plugins/lisa-cursor/skills/lisa-jam-access/SKILL.md +14 -6
  59. package/plugins/lisa-cursor/skills/lisa-linear-access/SKILL.md +31 -11
  60. package/plugins/lisa-cursor/skills/lisa-notion-access/SKILL.md +37 -24
  61. package/plugins/lisa-cursor/skills/lisa-posthog-access/SKILL.md +17 -7
  62. package/plugins/lisa-cursor/skills/lisa-secrets-access/SKILL.md +8 -0
  63. package/plugins/lisa-cursor/skills/lisa-sentry-access/SKILL.md +17 -7
  64. package/plugins/lisa-cursor/skills/lisa-sonarcloud-access/SKILL.md +7 -1
  65. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  66. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  67. package/plugins/lisa-expo-agy/plugin.json +1 -1
  68. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  71. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  72. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  73. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  75. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  76. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  77. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  78. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  79. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  80. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  81. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  82. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  83. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  84. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  85. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  86. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  87. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  88. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  89. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  90. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  91. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  92. package/plugins/lisa-rails-agy/plugin.json +1 -1
  93. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  94. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  95. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  96. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  97. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  98. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  99. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  100. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  101. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  102. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  103. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  104. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  105. package/plugins/src/base/rules/eager/integration-access-layer.md +7 -3
  106. package/plugins/src/base/rules/reference/credential-substrate-precedence.md +166 -0
  107. package/plugins/src/base/rules/reference/integration-access-layer.md +27 -15
  108. package/plugins/src/base/skills/lisa-atlassian-access/SKILL.md +76 -65
  109. package/plugins/src/base/skills/lisa-jam-access/SKILL.md +14 -6
  110. package/plugins/src/base/skills/lisa-linear-access/SKILL.md +31 -11
  111. package/plugins/src/base/skills/lisa-notion-access/SKILL.md +37 -24
  112. package/plugins/src/base/skills/lisa-posthog-access/SKILL.md +17 -7
  113. package/plugins/src/base/skills/lisa-secrets-access/SKILL.md +8 -0
  114. package/plugins/src/base/skills/lisa-sentry-access/SKILL.md +17 -7
  115. package/plugins/src/base/skills/lisa-sonarcloud-access/SKILL.md +7 -1
  116. package/typescript/copy-overwrite/scripts/check-nightly-e2e-health.mjs +631 -5
@@ -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. Resolves a substrate per operation in this order: (1) Notion MCP if authenticated and the configured prdDatabaseId is fetchable through it (identity-match), (2) curl + Bearer auth + internal-integration token. Verifies the active connection matches `.lisa.config.json` before every operation — substrates authenticated as a different Notion workspace are skipped, not used."
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: Notion MCP (identity-matched by fetching the configured PRD database)
47
- # Pseudo-code; actual call is the MCP tool invocation.
48
- # Try to fetch DB_ID through the MCP. Success MCP is authed to the right workspace.
49
- # 404 / object_not_found MCP is authed elsewhere (or unauthenticated). Skip.
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
- : ${substrate:=curl}
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
- Remediation paths (pick one):
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
- 1. Install the Notion MCP plugin (local scope — per-developer, gitignored).
146
- This is the simplest path for single-workspace developers.
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 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.
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). The ladder collapses to curl + `NOTION_API_TOKEN`. Same skill code runs identically; only the substrate changes.
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. Resolves PostHog MCP first when available, then falls back to POSTHOG_PERSONAL_API_KEY bearer auth."
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. PostHog MCP, if available and authenticated.
28
- 2. `POSTHOG_PERSONAL_API_KEY` bearer token against the configured PostHog host.
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. The headless REST
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
- - Fallback is gated on `POSTHOG_PERSONAL_API_KEY`.
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. Resolves Sentry MCP/CLI first when available, then falls back to SENTRY_AUTH_TOKEN + Sentry REST API."
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. Sentry MCP, if available and authenticated.
28
- 2. `sentry-cli`, if installed and authenticated to the requested org/project.
29
- 3. `SENTRY_AUTH_TOKEN` bearer token against Sentry REST.
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. The headless REST tier uses:
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
- - Fallback is gated on `SENTRY_AUTH_TOKEN`.
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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "AWS CDK-specific Lisa plugin.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -4,7 +4,11 @@ Skills and rules that use external integrations route through the matching
4
4
  `*-access` skill. Do not call vendor MCP tools or REST APIs directly from a
5
5
  consumer skill.
6
6
 
7
- Resolution order is MCP when available and authenticated, then documented
8
- token/REST substrate when the env var is set, then a loud error naming the env
9
- var. Full matrix and migration rules:
7
+ Resolution order is the configured-provider token/CLI substrate first when its
8
+ bootstrap credential is present and identity-matched, then the interactive MCP as
9
+ fallback, then a loud error naming the exact credential to set. Identity-match is
10
+ mandatory on every substrate — one authenticated as a different tenant is skipped,
11
+ never used. The ordering itself is settled in
12
+ [reference/credential-substrate-precedence.md](../reference/credential-substrate-precedence.md);
13
+ full matrix and migration rules:
10
14
  [reference/integration-access-layer.md](../reference/integration-access-layer.md).
@@ -0,0 +1,166 @@
1
+ # Credential-Substrate Precedence
2
+
3
+ **When more than one substrate can reach an external system, the configured credentials
4
+ provider's token/CLI path goes first and the interactive MCP is the fallback — and
5
+ identity-match verification is mandatory on every substrate, at every tier.**
6
+
7
+ This is **one shared, vendor-neutral contract cited by every `*-access` skill** (the
8
+ `leaf-only-lifecycle` / `repo-scope-split` precedent: one shared slug, never divergent
9
+ per-skill prose). An access skill states its per-vendor mechanics — which token, which
10
+ CLI, which identity anchor — and cites this rule for the ordering. It never restates,
11
+ narrows, or locally overrides the ordering.
12
+
13
+ Settled by the decision record `2026-08-12-credential-substrate-precedence` (D6). It is
14
+ a **settled decision** in the `settled-decisions` sense: re-arguing MCP-first inside a
15
+ skill is out of scope for that skill's work.
16
+
17
+ ## The ladder
18
+
19
+ ### Tier 1 — configured-provider substrate
20
+
21
+ The token or CLI path fed by `lisa-secrets-access`. Chosen **whenever its bootstrap
22
+ credential is available AND the resolved substrate identity-matches the configured
23
+ tenant/workspace/site.**
24
+
25
+ `lisa-secrets-access` is the single chokepoint that makes this tier actionable rather
26
+ than aspirational: it owns the one-store rule and the surface ladder, and its `tool:`
27
+ note line already declares which CLI a given credential is expected to drive. An access
28
+ skill resolves its credential through that chokepoint — never by reading an OS keychain
29
+ a second time.
30
+
31
+ Both halves of the gate are load-bearing. A present credential that identity-matches
32
+ nothing is **not** tier 1; it is a failed tier, and the ladder moves on.
33
+
34
+ ### Tier 2 — interactive MCP
35
+
36
+ Used when the tier 1 path is **genuinely unavailable**. The three genuine cases:
37
+
38
+ - **No bootstrap** — the provider credential is absent or the project has not adopted a
39
+ credentials provider at all.
40
+ - **No adapter for the operation** — the token/CLI substrate has no documented adapter
41
+ for the requested operation, and the MCP does (per-operation, not per-session: a skill
42
+ may resolve tier 1 for one operation and tier 2 for the next).
43
+ - **Provider outage** — the provider path is present but failing for reasons the caller
44
+ cannot fix in-session.
45
+
46
+ "The MCP happens to be authenticated" is not one of them. Neither is "tier 1 is slower."
47
+
48
+ ### Tier 3 — loud, actionable failure
49
+
50
+ When no substrate is both available and identity-matched, fail with a message naming the
51
+ exact credential to set and the exact remediation path. Never silently no-op, never
52
+ blind-retry a failed or absent substrate, and never fall through to a substrate that
53
+ failed identity-match.
54
+
55
+ ## Identity-match is mandatory on every substrate
56
+
57
+ Verification runs **in both directions** before any operation: the substrate must claim
58
+ the configured tenant, and the configured tenant must be one the substrate can actually
59
+ reach. A substrate authenticated as a **different** account is **skipped, never used**,
60
+ regardless of tier — including tier 1. A credential is not an identity claim; the
61
+ identity claim is what the provider says when asked.
62
+
63
+ | Vendor | Identity anchor | Probe |
64
+ |---|---|---|
65
+ | Atlassian | `atlassian.cloudId` / `atlassian.site` | `/rest/api/3/myself` email, acli `auth status` site, MCP accessible-resources contains the cloudId |
66
+ | Notion | `notion.workspaceId` (+ `prdDatabaseId` reachability) | `GET /v1/users/me` → `bot.workspace_name`/`workspace_id` |
67
+ | Linear | `linear.workspace` / `linear.teamKey` | `viewer`/`organization` on GraphQL; team list through the MCP |
68
+ | Sentry / PostHog / Jam / Sonar | configured org + project | the substrate's own whoami/org listing |
69
+
70
+ Skipping the check because "the user obviously meant this workspace" is forbidden. Silent
71
+ cross-tenant operations are precisely the hazard this contract exists to prevent.
72
+
73
+ ## Why provider-first (and why it overturns a working default)
74
+
75
+ MCP-first was defensible and is being overturned deliberately, not corrected as an
76
+ oversight. Three reasons outweigh it.
77
+
78
+ **Headless parity.** Cron runs, cloud sessions, CI, and subagent sessions have no
79
+ browser. Under MCP-first an interactive session and a headless session resolve through
80
+ *different* substrates and can therefore fail differently — and the failure surfaces only
81
+ in the environment nobody is watching. Provider-first makes the primary path the same one
82
+ everywhere, with MCP as the enhancement rather than the default.
83
+
84
+ **Tenant safety — the generalized Atlassian write rule.** Substrates differ in *where
85
+ their target comes from*:
86
+
87
+ - **Per-invocation-bound.** The target is part of the call. A cloudId-scoped REST URL
88
+ (`https://api.atlassian.com/ex/jira/<CLOUDID>/…`) or a workspace-scoped API token
89
+ carries its tenant in the request itself, so nothing outside the call can redirect it.
90
+ - **Ambient-bound.** The target comes from machine-global or session-global state a skill
91
+ does not own: acli's single active account, an MCP's browser OAuth session bound to
92
+ whatever account the human last used. Any other process — or the human — can change it
93
+ between the check and the call. That is a **TOCTOU** window, and a successful
94
+ pre-flight `auth status` does not close it.
95
+
96
+ Prefer the per-invocation-bound substrate. This is exactly why Atlassian JIRA *writes*
97
+ were already forced onto the cloudId-scoped curl adapter; the hazard is not specific to
98
+ Atlassian and not specific to writes. A misrouted write is loud and often reversible; a
99
+ **read through the wrong tenant silently returns wrong data**, which then propagates into
100
+ tickets, PRDs, and verification claims — harder to detect and harder to unwind. Reads get
101
+ the same ordering as writes.
102
+
103
+ **Determinism.** A token path either has its bootstrap or does not, and says so. An MCP's
104
+ readiness depends on session state a skill cannot inspect reliably — the same server
105
+ registers under different prefixes depending on install path, and its data tools register
106
+ only after OAuth completes.
107
+
108
+ ## MCP stays a first-class fallback
109
+
110
+ This is a **re-ordering, not a removal.** The strongest argument for MCP-first — that an
111
+ already-authenticated MCP is zero-setup and identity-verified — is preserved by keeping
112
+ MCP as a genuine, fully supported tier rather than deleting it:
113
+
114
+ - Every access skill keeps its MCP adapters in the dispatch table.
115
+ - An operation with no tier 1 adapter routes to MCP **as the normal path**, not as an
116
+ error.
117
+ - A project with no credentials provider is fully functional on MCP alone.
118
+ - MCP failure messages stay actionable (how to enable and authenticate the plugin).
119
+
120
+ Removing an MCP adapter is a separate decision requiring its own justification. Do not
121
+ treat this contract as license to delete one.
122
+
123
+ ## Guarded fallback for ambient-bound substrates
124
+
125
+ When the ladder does fall back to an ambient-bound substrate for a **mutating**
126
+ operation, the fallback is guarded — never the normal path:
127
+
128
+ 1. Switch to the configured profile and assert the active identity matches config
129
+ immediately before the write.
130
+ 2. Execute the write.
131
+ 3. Re-read the affected object(s) immediately afterward.
132
+ 4. Perform a **post-write tenant assertion** on the response — the tenant is proven from
133
+ the response (self URL host, cloudId in the path, or response metadata), not assumed
134
+ from the pre-flight check.
135
+ 5. On mismatch: stop, report a cross-tenant hazard, and best-effort **roll back** the
136
+ write when a safe reversal exists (delete the created object, remove the created
137
+ comment/link, revert a reversible field edit). Never continue as if it succeeded.
138
+
139
+ A successful pre-flight switch is not sufficient for tenant safety — another process can
140
+ mutate global state between the check and the write.
141
+
142
+ ## Consequences to expect
143
+
144
+ - **A stale or wrong token now fails identity-match instead of silently succeeding
145
+ through an authenticated MCP.** That is intended: it is the exact class of bug this
146
+ contract exists to surface. Fix the credential (`/lisa:setup:<vendor>`); do not
147
+ re-order the ladder to route around it.
148
+ - Headless and interactive sessions take the same primary path, so a credential problem
149
+ reproduces on a laptop instead of only at 3am in cron.
150
+ - The ordering is **not configurable per project**. A knob would let a project
151
+ reintroduce the headless divergence this contract removes. Revisit only with a concrete
152
+ need and a new decision record.
153
+
154
+ ## Adding or editing an access skill
155
+
156
+ 1. Cite this rule by name; do not restate the ordering.
157
+ 2. Document the tier 1 credential and the `lisa-secrets-access` resolution path.
158
+ 3. Document the identity anchor and its probe, and state that mismatch means skip.
159
+ 4. Keep MCP adapters and name the operations for which MCP is the only substrate.
160
+ 5. Make the terminal failure name the exact credential and remediation command.
161
+
162
+ Single-substrate access skills are conformant when their one substrate is
163
+ provider-credential-authenticated (`lisa-sonarcloud-access`: the official SonarQube MCP
164
+ authenticates headlessly from `SONARQUBE_CLI_TOKEN`, so it *is* the tier 1 substrate and
165
+ needs no separate REST tier). Reserve a multi-tier ladder for vendors whose MCP is
166
+ browser-OAuth or keychain-bound and therefore dead headless.
@@ -4,24 +4,35 @@ Every Lisa skill or rule that consumes an external integration MUST route throug
4
4
  the integration's `*-access` skill instead of calling that vendor's MCP tools or
5
5
  REST API directly.
6
6
 
7
- The access skill owns substrate resolution:
7
+ The access skill owns substrate resolution. **The ordering is not this rule's to
8
+ define** — it is the single shared contract in `credential-substrate-precedence`,
9
+ cited identically by every `*-access` skill:
8
10
 
9
- 1. MCP, when the tool is available and already authenticated to the configured
10
- workspace/account.
11
- 2. Token/REST substrate, only when the documented env var is present.
12
- 3. Loud failure naming the exact env var to set.
11
+ 1. **Configured-provider token/CLI substrate** the path fed by
12
+ `lisa-secrets-access` — when its bootstrap credential is present AND the
13
+ resolved substrate identity-matches the configured tenant/workspace/account.
14
+ 2. **Interactive MCP**, as a first-class fallback, when the provider path is
15
+ genuinely unavailable (no bootstrap, no adapter for the operation, provider
16
+ outage).
17
+ 3. Loud failure naming the exact credential to set.
13
18
 
14
- Do not blind-retry a failed or absent MCP. Fall back only after checking the
15
- documented env var for that vendor, and never silently no-op when neither tier is
16
- available.
19
+ Identity-match verification is mandatory on **every** substrate, in both
20
+ directions; one authenticated as a different tenant is skipped, never used. See the
21
+ `credential-substrate-precedence` rule for the rationale (headless parity, tenant
22
+ safety, determinism), the guarded-fallback protocol, and what "genuinely
23
+ unavailable" means.
24
+
25
+ Do not blind-retry a failed or absent substrate, and never silently no-op when no
26
+ tier is available.
17
27
 
18
28
  Some MCPs authenticate headlessly from an env token and need no separate REST
19
- tier — the MCP **is** the headless substrate on both developer machines and cloud
20
- routines. The official SonarQube MCP is one such case (`SONARQUBE_CLI_TOKEN`
29
+ tier — such an MCP **is** the configured-provider substrate on both developer
30
+ machines and cloud routines, so a single-substrate access skill is conformant. The
31
+ official SonarQube MCP is one such case (`SONARQUBE_CLI_TOKEN`
21
32
  [+ `SONARQUBE_CLI_ORG`/`SONARQUBE_CLI_SERVER`]): `lisa-sonarcloud-access` resolves it as a
22
- single substrate with no hand-rolled REST fallback. Reserve the two-tier
23
- MCP-then-REST shape for vendors whose MCP is browser-OAuth or keychain-bound and
24
- therefore dead headless.
33
+ single substrate with no hand-rolled REST fallback. Reserve the multi-tier ladder
34
+ for vendors whose MCP is browser-OAuth or keychain-bound and therefore dead
35
+ headless.
25
36
 
26
37
  ## Access Skills
27
38
 
@@ -60,7 +71,8 @@ When editing any skill listed in the matrix:
60
71
  delegate to the matching access skill.
61
72
  - Keep operation names coarse and vendor-native. Add new operation rows to the
62
73
  access skill instead of embedding REST details in the consumer.
63
- - Preserve existing MCP behavior as the first tier where Claude/Codex can expose
64
- the MCP, but make headless token fallback explicit and gated by the env var.
74
+ - Put the documented token/CLI substrate first and keep the MCP as an explicit,
75
+ fully supported fallback tier `credential-substrate-precedence`. Preserving
76
+ the MCP adapters is required; re-ordering them is not the same as removing them.
65
77
  - If a vendor has no documented token substrate, keep the MCP-only behavior and
66
78
  fail with a clear "no documented headless substrate" message.