@atollhq/skill-claude 0.4.6 → 0.4.7

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/README.md CHANGED
@@ -12,14 +12,14 @@ npx @atollhq/skill-claude --profile agent-a --key sk_atoll_... --org your-org-id
12
12
  ATOLL_API_KEY=sk_atoll_... ATOLL_ORG_ID=your-org-id npx @atollhq/skill-claude
13
13
  ```
14
14
 
15
- Optional defaults: `--profile`, `--project`, `--team`, and `--base-url` write `ATOLL_PROFILE`, `ATOLL_PROJECT`, `ATOLL_TEAM`, and `ATOLL_BASE_URL` for the agent. Use `--no-project`, `--no-team`, or `--no-base-url` to clear previously saved defaults. When `--profile` is set, the installer also creates or updates that profile in `~/.atoll/config.json`.
15
+ Optional defaults: `--project`, `--team`, and `--base-url` are stored with the selected mode. Use `--no-project`, `--no-team`, or `--no-base-url` to clear previously saved defaults. Pass `--profile` to store credentials and defaults only in that named Atoll CLI profile; Claude settings keep only `ATOLL_PROFILE` so direct CLI commands can use the profile safely. Omit `--profile` to use env-var mode, which writes `ATOLL_ENV_MODE=1` with the credential settings.
16
16
 
17
- Get an API key from **Settings > Members > Add Agent** (or **Create API Key** for integrations) in the Atoll app.
17
+ Get an agent API key from **Agents** in the Atoll app. Integration keys are still managed from **Settings > Members**.
18
18
 
19
19
  This does three things:
20
20
 
21
21
  1. Copies the skill into `~/.claude/skills/atoll-api/`
22
- 2. Writes Atoll env vars into `~/.claude/settings.json` under `env`
22
+ 2. Stores only `ATOLL_PROFILE` in `~/.claude/settings.json` when profile mode is used
23
23
  3. Creates or updates the named Atoll CLI profile when `--profile` is provided
24
24
 
25
25
  Restart Claude Code and the `atoll-api` skill is available.
package/bin/install.mjs CHANGED
@@ -41,7 +41,7 @@ Usage: npx @atollhq/skill-claude [--profile <name>] --key <api-key> --org <org-i
41
41
  or: ATOLL_API_KEY=<api-key> ATOLL_ORG_ID=<org-id> npx @atollhq/skill-claude
42
42
 
43
43
  Options:
44
- --profile Atoll CLI profile name to create/update. Defaults to ATOLL_PROFILE.
44
+ --profile Atoll CLI profile name to create/update.
45
45
  --key Atoll API key (sk_atoll_...). Defaults to ATOLL_API_KEY.
46
46
  --org Organization ID. Defaults to ATOLL_ORG_ID.
47
47
  --project Default project ID. Defaults to ATOLL_PROJECT.
@@ -58,7 +58,6 @@ the ability to manage tasks, goals, KPIs, and initiatives.
58
58
  }
59
59
 
60
60
  const args = parseArgs(process.argv)
61
- args.profile ??= process.env.ATOLL_PROFILE
62
61
  args.key ??= process.env.ATOLL_API_KEY
63
62
  args.org ??= process.env.ATOLL_ORG_ID
64
63
  if (args.clearProject) delete args.project
@@ -128,30 +127,46 @@ mkdirSync(join(skillDest, 'references'), { recursive: true })
128
127
  cpSync(skillSrc, skillDest, { recursive: true })
129
128
  console.log(`Installed skill to ${skillDest}`)
130
129
 
131
- // 2. Write credentials to ~/.claude/settings.json env vars
130
+ // 2. Configure ~/.claude/settings.json. Profile mode stores credentials only
131
+ // in the Atoll CLI profile and keeps Claude's host env limited to profile
132
+ // selection, removing stale credential/default env from earlier installs.
132
133
  const settingsPath = join(homedir(), '.claude', 'settings.json')
133
134
  let settings = readJson(settingsPath)
134
135
 
135
136
  if (!settings.env) settings.env = {}
136
- if (args.profile) settings.env.ATOLL_PROFILE = args.profile
137
- settings.env.ATOLL_API_KEY = args.key
138
- settings.env.ATOLL_ORG_ID = args.org
139
- if (args.project) settings.env.ATOLL_PROJECT = args.project
140
- else delete settings.env.ATOLL_PROJECT
141
- if (args.team) settings.env.ATOLL_TEAM = args.team
142
- else delete settings.env.ATOLL_TEAM
143
- if (args.baseUrl) settings.env.ATOLL_BASE_URL = args.baseUrl
144
- else delete settings.env.ATOLL_BASE_URL
137
+ if (args.profile) {
138
+ settings.env.ATOLL_PROFILE = args.profile
139
+ delete settings.env.ATOLL_API_KEY
140
+ delete settings.env.ATOLL_ORG_ID
141
+ delete settings.env.ATOLL_PROJECT
142
+ delete settings.env.ATOLL_TEAM
143
+ delete settings.env.ATOLL_BASE_URL
144
+ delete settings.env.ATOLL_ENV_MODE
145
+ } else {
146
+ delete settings.env.ATOLL_PROFILE
147
+ settings.env.ATOLL_API_KEY = args.key
148
+ settings.env.ATOLL_ORG_ID = args.org
149
+ if (args.project) settings.env.ATOLL_PROJECT = args.project
150
+ else delete settings.env.ATOLL_PROJECT
151
+ if (args.team) settings.env.ATOLL_TEAM = args.team
152
+ else delete settings.env.ATOLL_TEAM
153
+ if (args.baseUrl) settings.env.ATOLL_BASE_URL = args.baseUrl
154
+ else delete settings.env.ATOLL_BASE_URL
155
+ settings.env.ATOLL_ENV_MODE = '1'
156
+ }
145
157
 
146
158
  writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n')
147
159
  writeAtollProfile()
148
160
 
149
161
  const configuredVars = []
150
- if (args.profile) configuredVars.push('ATOLL_PROFILE')
151
- configuredVars.push('ATOLL_API_KEY', 'ATOLL_ORG_ID')
152
- if (args.project) configuredVars.push('ATOLL_PROJECT')
153
- if (args.team) configuredVars.push('ATOLL_TEAM')
154
- if (args.baseUrl) configuredVars.push('ATOLL_BASE_URL')
162
+ if (args.profile) {
163
+ configuredVars.push('ATOLL_PROFILE')
164
+ } else {
165
+ configuredVars.push('ATOLL_API_KEY', 'ATOLL_ORG_ID', 'ATOLL_ENV_MODE')
166
+ if (args.project) configuredVars.push('ATOLL_PROJECT')
167
+ if (args.team) configuredVars.push('ATOLL_TEAM')
168
+ if (args.baseUrl) configuredVars.push('ATOLL_BASE_URL')
169
+ }
155
170
  console.log(`Configured ${configuredVars.join(', ')} in ${settingsPath}`)
156
171
 
157
172
  console.log(`\nDone! Start Claude Code and the atoll-api skill will be available.`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atollhq/skill-claude",
3
- "version": "0.4.6",
3
+ "version": "0.4.7",
4
4
  "description": "Install the Atoll project management skill for Claude Code",
5
5
  "bin": {
6
6
  "skill-claude": "bin/install.mjs"
package/skill/SKILL.md CHANGED
@@ -26,7 +26,7 @@ Agents are org members with the same API, same permissions, same ability to crea
26
26
 
27
27
  All requests require: `Authorization: Bearer sk_atoll_<key>`
28
28
 
29
- API keys are generated in **Settings > Members > Add Agent** (for agents) or **Settings > Members > Create API Key** (for integrations). Each key is scoped to one org. Store both values as env vars:
29
+ API keys are generated in **Agents** (for agents) or **Settings > Members > Create API Key** (for integrations). Each key is scoped to one org. Store both values as env vars:
30
30
 
31
31
  ```bash
32
32
  export ATOLL_API_KEY="sk_atoll_..."
@@ -94,6 +94,8 @@ atoll --profile agent-b issue list
94
94
 
95
95
  Profiles can store default org ID, project, team, and base URL values. For named profiles, always persist `--org-id` or pass `--org-id` per command. Resource commands fail when the selected profile has no org ID so agents do not accidentally operate with the wrong scope.
96
96
 
97
+ Env vars remain supported for CI, containers, and one-off runtime usage, but persistent developer/agent machines should prefer profiles. When a profile is selected, ambient `ATOLL_*` env vars do not silently override profile context; conflicting env values fail before network calls. Pass `--profile`, use repo-local `.atoll/context.json`, or opt into env mode with `--env-mode` / `ATOLL_ENV_MODE=1`.
98
+
97
99
  `atoll issue list` and `atoll issue create` apply the selected default team unless a command-level `--team` override is passed.
98
100
 
99
101
  Common commands:
@@ -173,6 +175,7 @@ CLI JSON conventions:
173
175
  - Use `--json` for machine-readable output.
174
176
  - List commands return `{ resource, items, total, limit, offset, nextOffset, truncated, hint }`.
175
177
  - Project-scoped `atoll issue list --json` includes `project_context`; `atoll issue get/view --json` includes `status_column` plus `project_context` when available.
178
+ - For initiative execution context via API, `GET /api/orgs/{id}/initiatives/{initiativeId}/issues?details=1` returns accessible task details from linked projects, direct issue links, and linked milestones.
176
179
  - Diagnostics and errors go to stderr.
177
180
  - Interactive CLI update notices also go to stderr and are suppressed for JSON/non-TTY/CI/completion flows.
178
181
  - `atoll agent-context` returns a versioned command/flag manifest, available profile context, and structured `cli.update_available` metadata.
@@ -315,6 +318,8 @@ The primary pattern for autonomous agents. Prefer `atoll heartbeat --json` when
315
318
  - **Project context**: relevant board columns, including optional descriptions that explain stage criteria for agents
316
319
  - **Signals** sorted by severity — the agent's prioritized to-do list
317
320
 
321
+ Heartbeat is org-scoped, but project-bound payload details are filtered by the caller's project access. Owners/admins receive full org context; members/guests only receive project-bound strategy, work health, assigned work, milestone signals, and board context for accessible projects. Non-guest members can also see unprojected org-level strategy. Shared initiatives can appear with counts and signals based only on accessible work.
322
+
318
323
  Signal types: `kpi_off_pace`, `kpi_stale`, `issue_stale`, `issue_blocked`, `milestone_overdue`, `initiative_stalled`, `webhook_failing`. Severity: `info`, `warning`, `critical`.
319
324
 
320
325
  Useful CLI forms:
@@ -364,6 +369,8 @@ atoll initiative kpi link "Content pipeline" paying_customers --impact "+30 cust
364
369
  atoll kpi snapshot add paying_customers --value 42 --initiative "Content pipeline" --note "End-of-week Stripe check"
365
370
  ```
366
371
 
372
+ Project-scoped agent profiles apply their default project to `atoll initiative list` and `atoll initiative create`. Use `--project <id-or-slug>` to override that project, or `--org-wide` to intentionally suppress the default project. API callers can pass `project_id` or `projectId` on create, and `?project_id=...` on list; guest/project-scoped callers must use a project they can access, and create requires edit/admin project access.
373
+
367
374
  Every KPI snapshot can be attributed to an initiative or issue, building a record of *what actually moved the numbers*.
368
375
 
369
376
  ### Audit and improve the strategy
@@ -392,6 +399,17 @@ This is the structural-health lens (is the strategy well-formed?), complementary
392
399
 
393
400
  `POST /api/orgs/{id}/issues/bulk` with `{ "issues": [{...}, ...] }` (max 50).
394
401
 
402
+ ### Outbound webhooks
403
+
404
+ `POST /api/webhooks` creates outbound webhooks. Receiver URLs must be HTTPS DNS hostnames; Atoll rejects IP literals, `localhost`, `.local` hosts, URL credentials, and fragments at creation. Delivery also resolves DNS and refuses private, loopback, link-local, documentation, multicast, and other non-public addresses; redirects are not followed.
405
+
406
+ Webhook creation returns a raw `whsec_...` secret once. Delivery requests include:
407
+
408
+ - `X-Atoll-Signature`: `sha256=` plus an HMAC-SHA256 over the raw body, keyed by the SHA-256 hex digest of the raw secret.
409
+ - `X-Atoll-Delivery-Id`: stable delivery id for receiver-side deduplication.
410
+
411
+ Delivery rows expose `delivery_id`, `status`, and `next_retry_at`. Network failures and 5xx responses retry quickly in-process, then persist `status: retry_pending` with `next_retry_at`; an internal drain retries due deliveries every 15 minutes.
412
+
395
413
  ### Billing and plan limits
396
414
 
397
415
  Owners/admins can read billing state with `GET /api/orgs/{id}/billing` and start Stripe checkout with `POST /api/orgs/{id}/billing/checkout` using `{ "plan": "starter" }` or `{ "plan": "team" }`.
@@ -413,7 +431,7 @@ Full endpoint tables and field schemas:
413
431
  | Tasks | POST `.../issues` | GET `.../issues` | PATCH `.../issues/{id}` | DELETE `.../issues/{id}` † |
414
432
  | Goals | POST `.../goals` | GET `.../goals` | PATCH `.../goals/{id}` | DELETE `.../goals/{id}` |
415
433
  | KPIs | POST `.../kpis` | GET `.../kpis` | PATCH `.../kpis/{id}` | DELETE `.../kpis/{id}` |
416
- | Initiatives | POST `.../initiatives` | GET `.../initiatives` | PATCH `.../initiatives/{id}` | DELETE `.../initiatives/{id}` |
434
+ | Initiatives | POST `.../initiatives` (`project_id`/`projectId` optional; required for guests) | GET `.../initiatives` (`project_id` optional; required for guests) | PATCH `.../initiatives/{id}` | DELETE `.../initiatives/{id}` |
417
435
  | Milestones | POST `.../milestones` | GET `.../milestones` | PATCH `.../milestones/{id}` | DELETE `.../milestones/{id}` |
418
436
  | Comments | POST `.../comments` | GET `.../comments` | PATCH `.../comments/{id}` | DELETE `.../comments/{id}` |
419
437
  | Subtasks | POST `.../subtasks` | GET `.../subtasks` | PATCH `.../subtasks/{id}` | DELETE `.../subtasks/{id}` |
@@ -114,7 +114,7 @@ Plan limits are enforced when creating projects, human members, agents/integrati
114
114
  | POST | `/api/orgs/{id}/issues/{issueId}/initiatives` | Link task to initiative (`{ initiative_id }`) |
115
115
  | DELETE | `/api/orgs/{id}/issues/{issueId}/initiatives/{initiativeId}` | Unlink task from initiative |
116
116
 
117
- Issue-centric initiative links follow task project permissions: reading links requires access to the task's project; linking and unlinking requires a non-guest member with non-view access.
117
+ Issue-centric initiative links follow task project permissions: reading links requires access to the task's project; linking and unlinking require edit/admin access to that project. Guest callers may link only to initiatives already linked to the same accessible project.
118
118
 
119
119
  **List filters** (query params):
120
120
  - `status` -- `backlog`, `todo`, `in_progress`, `done`, `cancelled`
@@ -221,8 +221,8 @@ Roles: `owner`, `admin`, `member`, `guest`.
221
221
 
222
222
  | Method | Endpoint | Description |
223
223
  |--------|----------|-------------|
224
- | GET | `/api/orgs/{id}/initiatives` | List (optional `?goal_id=...&status=...&owner_id=...`) |
225
- | POST | `/api/orgs/{id}/initiatives` | Create initiative |
224
+ | GET | `/api/orgs/{id}/initiatives` | List (optional `?goal_id=...&status=...&owner_id=...&project_id=...`; guests require `project_id`) |
225
+ | POST | `/api/orgs/{id}/initiatives` | Create initiative (`project_id`/`projectId` optional; guests require editable project access) |
226
226
  | GET | `/api/orgs/{id}/initiatives/{initiativeId}` | Get initiative |
227
227
  | PATCH | `/api/orgs/{id}/initiatives/{initiativeId}` | Update initiative |
228
228
  | DELETE | `/api/orgs/{id}/initiatives/{initiativeId}` | Delete initiative (admin/owner only) |
@@ -238,7 +238,7 @@ Create accepts `title` or legacy `name`, plus camelCase aliases `goalId`, `owner
238
238
  | GET | `.../initiatives/{id}/kpi-impacts` | List KPI impact links |
239
239
  | POST | `.../initiatives/{id}/kpi-impacts` | Add (`{ kpi_id, expected_impact? }`) |
240
240
  | DELETE | `.../initiatives/{id}/kpi-impacts/{impactId}` | Remove link |
241
- | GET | `.../initiatives/{id}/issues` | List linked issues |
241
+ | GET | `.../initiatives/{id}/issues` | List linked issue links; add `?details=1` for accessible task details from linked projects, direct issue links, and linked milestones |
242
242
  | POST | `.../initiatives/{id}/issues` | Link issue (`{ issue_id }`) |
243
243
  | DELETE | `.../initiatives/{id}/issues/{issueId}` | Unlink issue |
244
244
  | GET | `.../initiatives/{id}/milestones` | List linked milestones |
@@ -251,7 +251,7 @@ Create accepts `title` or legacy `name`, plus camelCase aliases `goalId`, `owner
251
251
  |--------|----------|-------------|
252
252
  | GET | `/api/orgs/{id}/strategy/audit` | Audit the strategy chain for structural gaps + health issues, each with a suggested fix |
253
253
 
254
- Returns findings only (not the full graph). Use it for a high-level review — orphaned initiatives/KPIs (no goal), goals with no KPI or no initiative, KPIs missing targets/stale/off-pace, initiatives missing impact/execution or stalled, blocked/overdue work — then remediate with the goal/KPI/initiative write endpoints above. Forbidden for guests. CLI: `atoll strategy audit [--severity critical|warning|info] [--json]`.
254
+ Returns findings only (not the full graph). Use it for a high-level review — orphaned initiatives/KPIs (no goal), goals with no KPI or no initiative, dangling initiative execution links, KPIs missing targets/stale/off-pace, initiatives missing impact/execution or stalled, blocked/overdue work — then remediate with the goal/KPI/initiative write endpoints above. Forbidden for guests. CLI: `atoll strategy audit [--severity critical|warning|info] [--json]`.
255
255
 
256
256
  ## Heartbeat
257
257
 
@@ -259,7 +259,7 @@ Returns findings only (not the full graph). Use it for a high-level review — o
259
259
  |--------|----------|-------------|
260
260
  | GET | `/api/orgs/{id}/heartbeat` | Get heartbeat context for the authenticated agent |
261
261
 
262
- Returns computed briefing with goal status, KPI pace/trend, initiative progress, assigned work, and signals.
262
+ Returns computed briefing with goal status, KPI pace/trend, initiative progress, assigned work, and signals. The endpoint is org-scoped, but project-bound payload details are filtered by the caller's project access; non-guest members can also see unprojected org-level strategy, and shared initiatives can appear with counts and signals based only on accessible work.
263
263
 
264
264
  Signal types: `kpi_off_pace`, `kpi_stale`, `issue_stale`, `issue_blocked`, `milestone_overdue`, `initiative_stalled`, `webhook_failing`. Severity: `info`, `warning`, `critical`.
265
265
 
@@ -413,7 +413,7 @@ Trigger events: `issue.created`, `issue.status_changed`, `issue.assigned`, `issu
413
413
  | POST | `/api/webhooks/{id}/redeliver/{deliveryId}` | Redeliver a past payload |
414
414
  | POST | `/api/webhooks/{id}/test` | Send ping test event |
415
415
 
416
- URL must be HTTPS. Returns webhook record plus `secret` for HMAC verification.
416
+ URL must be an HTTPS DNS hostname. IP literals, `localhost`, and `.local` hosts are rejected at creation; delivery refuses non-public DNS results and does not follow redirects. Returns webhook record plus `secret` for HMAC verification. Store the secret immediately; it is shown only once. Delivery requests include `X-Atoll-Signature: sha256=<hmac>`, where the HMAC-SHA256 key is the SHA-256 hex digest of the webhook secret and the message is the exact raw request body. Delivery requests also include `X-Atoll-Delivery-Id` so receivers can dedupe retries. Atoll retries network failures and 5xx responses after 5s and 30s, then records `status: retry_pending` with `next_retry_at`; an internal cron drains due retries every 15 minutes.
417
417
 
418
418
  ## Notifications
419
419
 
@@ -428,13 +428,16 @@ URL must be HTTPS. Returns webhook record plus `secret` for HMAC verification.
428
428
  | Method | Endpoint | Description |
429
429
  |--------|----------|-------------|
430
430
  | GET | `/api/orgs/{id}/agents` | List agents (owner/admin) |
431
- | POST | `/api/orgs/{id}/agents` | Create agent (`{ name, role?, setupScoped? }`) |
432
- | DELETE | `/api/orgs/{id}/agents/{agentId}` | Revoke agent |
433
- | GET | `/api/orgs/{id}/agents/{agentId}/keys` | List API keys |
434
- | POST | `/api/orgs/{id}/agents/{agentId}/keys` | Generate new key |
435
- | DELETE | `/api/orgs/{id}/agents/{agentId}/keys/{keyId}` | Revoke key |
436
- | POST | `/api/orgs/{id}/agents/{agentId}/rotate` | Rotate all keys |
437
- | POST | `/api/orgs/{id}/agents/{agentId}/install-snippets` | Get install snippets (`{ key, profileName?, projectId?, teamId?, baseUrl? }`) |
431
+ | GET | `/api/orgs/{id}/agents/manageable` | List agents the current human can manage |
432
+ | POST | `/api/orgs/{id}/agents` | Create org, project-scoped, or personal agent |
433
+ | DELETE | `/api/orgs/{id}/agents/{agentId}` | Revoke manageable agent |
434
+ | PATCH | `/api/orgs/{id}/agents/{agentId}/projects` | Replace project access for a manageable non-personal agent |
435
+ | POST | `/api/orgs/{id}/projects/{projectId}/agents` | Grant selected manageable agents access to a project |
436
+ | GET | `/api/orgs/{id}/agents/{agentId}/keys` | List API keys for a manageable agent |
437
+ | POST | `/api/orgs/{id}/agents/{agentId}/keys` | Generate new key for a manageable agent |
438
+ | DELETE | `/api/orgs/{id}/agents/{agentId}/keys/{keyId}` | Revoke key for a manageable agent |
439
+ | POST | `/api/orgs/{id}/agents/{agentId}/rotate` | Rotate all keys for a manageable agent |
440
+ | POST | `/api/orgs/{id}/agents/{agentId}/install-snippets` | Get install snippets for a manageable agent (`{ key, profileName?, projectId?, teamId?, baseUrl? }`) |
438
441
 
439
442
  Install snippets returns config for `claude-code`, `codex`, `gemini`, `openclaw` (agent prompt), `openclaw-manual`, `hermes` (agent prompt), and `hermes-manual`. The server resolves the org slug and validates optional project/team IDs before generating snippets.
440
443
 
@@ -174,10 +174,13 @@ V1 syncs are `GET` only, `https` only, JSON only, exact-host allowlisted, no red
174
174
  "goal_id": "goal-uuid",
175
175
  "owner_id": "member-uuid",
176
176
  "status": "active",
177
- "target_date": "2026-05-15"
177
+ "target_date": "2026-05-15",
178
+ "project_id": "project-uuid"
178
179
  }
179
180
  ```
180
181
 
182
+ Create accepts `projectId` as a camelCase alias for `project_id`. Guest/project-scoped callers must pass a project they can edit when creating initiatives.
183
+
181
184
  For portfolio-style initiatives (grouping projects):
182
185
  ```json
183
186
  {
@@ -276,7 +279,7 @@ Add/remove projects with `{ "project_id": "uuid" }`.
276
279
  }
277
280
  ```
278
281
 
279
- URL must be HTTPS. Response includes `secret` for HMAC signature verification.
282
+ URL must be an HTTPS DNS hostname. IP literals, `localhost`, and `.local` hosts are rejected at creation; delivery refuses non-public DNS results and does not follow redirects. Response includes `secret` for HMAC signature verification. Store it immediately; it is shown only once. Delivery requests include `X-Atoll-Signature: sha256=<hmac>`, where the HMAC-SHA256 key is the SHA-256 hex digest of the webhook secret and the message is the exact raw request body. Delivery requests also include `X-Atoll-Delivery-Id` for receiver-side deduplication. Delivery history includes retry `status` and `next_retry_at`.
280
283
 
281
284
  ## Setup Proposal Fields
282
285
 
@@ -345,6 +348,8 @@ Proposal JSON currently supports at most one item in each collection: `projects`
345
348
  }
346
349
  ```
347
350
 
351
+ Heartbeat is org-scoped, but project-bound goals, KPIs, initiatives, issue health, milestone signals, assigned work, and `project_context` are filtered by the caller's project access. Non-guest members can also see unprojected org-level strategy. Shared initiatives can appear with counts and signals based only on accessible work.
352
+
348
353
  ## Strategy Audit Response
349
354
 
350
355
  `GET /api/orgs/{id}/strategy/audit` returns findings (sorted critical → warning → info), each with a concrete `suggested_fix`, plus summary counts.
@@ -368,7 +373,7 @@ Proposal JSON currently supports at most one item in each collection: `projects`
368
373
 
369
374
  Each finding carries whichever entity ids apply: `goal_id`, `kpi_id`, `initiative_id`, `issue_id`, `milestone_id`, `project_id`. Finding `type` values:
370
375
 
371
- - Structural: `initiative_orphaned`, `kpi_orphaned`, `goal_missing_kpi`, `goal_missing_initiative`
376
+ - Structural: `initiative_orphaned`, `kpi_orphaned`, `goal_missing_kpi`, `goal_missing_initiative`, `dangling_initiative_project`, `dangling_initiative_issue`, `dangling_initiative_milestone`
372
377
  - KPI health: `kpi_unrecorded`, `kpi_missing_target`, `kpi_stale`, `kpi_off_pace`
373
378
  - Initiative health: `initiative_missing_impact`, `initiative_missing_execution`, `initiative_stalled`
374
379
  - Execution: `issue_blocked`, `issue_overdue`, `milestone_overdue`