@atollhq/skill-codex 0.4.9 → 0.4.11
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 +15 -5
- package/bin/install.mjs +145 -23
- package/package.json +1 -1
- package/skill/SKILL.md +29 -12
- package/skill/references/api-endpoints.md +33 -6
- package/skill/references/api-fields.md +89 -7
package/README.md
CHANGED
|
@@ -14,15 +14,25 @@ ATOLL_API_KEY=sk_atoll_... ATOLL_ORG_ID=your-org-id npx @atollhq/skill-codex@lat
|
|
|
14
14
|
|
|
15
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. The installer does not write global `ATOLL_*` exports to `~/.zshrc` or `~/.bashrc` in profile mode. Use `atoll --profile agent-a ...` for profile-scoped commands, or omit `--profile` to use env-var mode.
|
|
16
16
|
|
|
17
|
+
For multi-org setups, keep global Codex instructions neutral and bind profiles per repo:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npx @atollhq/skill-codex@latest --profile agent-a --key sk_atoll_... --org your-org-id \
|
|
21
|
+
--write-project-instructions --project-dir /path/to/repo --instruction-files agents,claude
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`--write-project-instructions` writes a small managed block to repo-local instruction files. It requires `--profile`, defaults to the current directory, writes `AGENTS.md` by default, and can also write `CLAUDE.md` with `--instruction-files agents,claude`. If the target file already has unmanaged Atoll profile guidance, pass `--force-project-instructions` after reviewing the replacement.
|
|
25
|
+
|
|
17
26
|
Get an agent API key from **Agents** in the Atoll app. Integration keys are still managed from **Settings > Members**.
|
|
18
27
|
|
|
19
|
-
This does
|
|
28
|
+
This does six things:
|
|
20
29
|
|
|
21
30
|
1. Installs the `atoll-api` skill to `~/.codex/skills/atoll-api/`
|
|
22
|
-
2. Appends (or updates)
|
|
31
|
+
2. Appends (or updates) a neutral `# Atoll Integration` section in `~/.codex/AGENTS.md`
|
|
23
32
|
3. Copies API reference files to `~/.codex/atoll-references/`
|
|
24
33
|
4. Creates or updates the named Atoll CLI profile when `--profile` is provided
|
|
25
34
|
5. Appends Atoll env var exports to your shell profile (`~/.zshrc` or `~/.bashrc`) only when no profile is provided
|
|
35
|
+
6. Optionally writes repo-local profile guidance when `--write-project-instructions` is provided
|
|
26
36
|
|
|
27
37
|
For profile mode, Codex has the Atoll skill immediately and terminal commands can use `atoll --profile agent-a ...`. For env-var mode, the installer writes `ATOLL_ENV_MODE=1` with the credential exports; open a fresh shell or `source` your profile.
|
|
28
38
|
|
|
@@ -48,9 +58,9 @@ For terminal-first work, see [`@atollhq/cli`](https://www.npmjs.com/package/@ato
|
|
|
48
58
|
npm install -g @atollhq/cli
|
|
49
59
|
atoll auth login --profile agent-a --key sk_atoll_... --org-id org-uuid --project project-id --team team-id
|
|
50
60
|
atoll auth login --profile agent-a --key sk_atoll_... --org-id org-uuid --project project-id --no-team --no-base-url
|
|
51
|
-
atoll heartbeat
|
|
52
|
-
atoll issue list --json
|
|
53
|
-
atoll agent-context
|
|
61
|
+
atoll --profile agent-a heartbeat
|
|
62
|
+
atoll --profile agent-a issue list --json
|
|
63
|
+
atoll --profile agent-a agent-context
|
|
54
64
|
```
|
|
55
65
|
|
|
56
66
|
For multiple agents or orgs, use CLI auth profiles:
|
package/bin/install.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { cpSync, existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync } from 'node:fs'
|
|
4
|
-
import { join, dirname } from 'node:path'
|
|
3
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync, statSync, renameSync, rmSync } from 'node:fs'
|
|
4
|
+
import { join, dirname, resolve, basename } from 'node:path'
|
|
5
5
|
import { homedir } from 'node:os'
|
|
6
6
|
import { fileURLToPath } from 'node:url'
|
|
7
7
|
|
|
@@ -20,6 +20,10 @@ function parseArgs(argv) {
|
|
|
20
20
|
else if (argv[i] === '--no-team') args.clearTeam = true
|
|
21
21
|
else if (argv[i] === '--base-url' && argv[i + 1]) args.baseUrl = argv[++i]
|
|
22
22
|
else if (argv[i] === '--no-base-url') args.clearBaseUrl = true
|
|
23
|
+
else if (argv[i] === '--write-project-instructions') args.writeProjectInstructions = true
|
|
24
|
+
else if (argv[i] === '--project-dir' && argv[i + 1]) args.projectDir = argv[++i]
|
|
25
|
+
else if (argv[i] === '--instruction-files' && argv[i + 1]) args.instructionFiles = argv[++i]
|
|
26
|
+
else if (argv[i] === '--force-project-instructions') args.forceProjectInstructions = true
|
|
23
27
|
else if (argv[i] === '--help' || argv[i] === '-h') args.help = true
|
|
24
28
|
}
|
|
25
29
|
return args
|
|
@@ -40,12 +44,21 @@ Options:
|
|
|
40
44
|
--no-team Clear any saved default team.
|
|
41
45
|
--base-url Atoll base URL. Defaults to ATOLL_BASE_URL.
|
|
42
46
|
--no-base-url Clear any saved custom base URL.
|
|
47
|
+
--write-project-instructions
|
|
48
|
+
Write a small repo-local Atoll profile block. Requires --profile.
|
|
49
|
+
--project-dir
|
|
50
|
+
Directory for repo-local instructions. Defaults to the current directory.
|
|
51
|
+
--instruction-files
|
|
52
|
+
Comma-separated files to update: agents, claude, or agents,claude. Defaults to agents.
|
|
53
|
+
--force-project-instructions
|
|
54
|
+
Replace existing unmanaged Atoll profile guidance in repo-local instruction files.
|
|
43
55
|
--help Show this help message
|
|
44
56
|
|
|
45
57
|
Installs the Atoll integration for Codex CLI:
|
|
46
58
|
- Installs the atoll-api skill to ~/.codex/skills/atoll-api/
|
|
47
|
-
- Writes AGENTS.md
|
|
59
|
+
- Writes neutral AGENTS.md API guidance to ~/.codex/
|
|
48
60
|
- Creates/updates an Atoll CLI profile when --profile is provided
|
|
61
|
+
- Optionally writes repo-local AGENTS.md/CLAUDE.md profile guidance with --write-project-instructions
|
|
49
62
|
- Otherwise writes Atoll env vars to your shell profile for env-var mode
|
|
50
63
|
`)
|
|
51
64
|
}
|
|
@@ -64,6 +77,32 @@ if (args.help) { printUsage(); process.exit(0) }
|
|
|
64
77
|
|
|
65
78
|
console.log(`Running @atollhq/skill-codex installer v${packageJson.version}`)
|
|
66
79
|
|
|
80
|
+
const instructionFileChoices = (args.instructionFiles ?? 'agents')
|
|
81
|
+
.split(',')
|
|
82
|
+
.map((entry) => entry.trim().toLowerCase())
|
|
83
|
+
.filter(Boolean)
|
|
84
|
+
const allowedInstructionFiles = new Set(['agents', 'claude'])
|
|
85
|
+
if (instructionFileChoices.some((entry) => !allowedInstructionFiles.has(entry))) {
|
|
86
|
+
console.error('Error: --instruction-files must be agents, claude, or agents,claude')
|
|
87
|
+
process.exit(1)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const projectInstructionFiles = [...new Set(instructionFileChoices)]
|
|
91
|
+
if (args.writeProjectInstructions && projectInstructionFiles.length === 0) {
|
|
92
|
+
console.error('Error: --instruction-files must include agents, claude, or agents,claude')
|
|
93
|
+
process.exit(1)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (args.writeProjectInstructions && !args.profile) {
|
|
97
|
+
console.error('Error: --write-project-instructions requires --profile so the repo-local guidance can name a profile')
|
|
98
|
+
process.exit(1)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (args.writeProjectInstructions && !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(args.profile)) {
|
|
102
|
+
console.error('Error: --write-project-instructions requires a profile name that starts with a letter or number and contains only letters, numbers, dots, underscores, or hyphens')
|
|
103
|
+
process.exit(1)
|
|
104
|
+
}
|
|
105
|
+
|
|
67
106
|
if (!args.key || !args.org) {
|
|
68
107
|
console.error('Error: provide --key and --org, or set ATOLL_API_KEY and ATOLL_ORG_ID\n')
|
|
69
108
|
printUsage()
|
|
@@ -104,7 +143,6 @@ function writeAtollProfile() {
|
|
|
104
143
|
else delete profile.defaultTeam
|
|
105
144
|
if (args.baseUrl) profile.baseUrl = args.baseUrl
|
|
106
145
|
else delete profile.baseUrl
|
|
107
|
-
config.activeProfile = args.profile
|
|
108
146
|
|
|
109
147
|
mkdirSync(atollDir, { recursive: true })
|
|
110
148
|
writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n')
|
|
@@ -112,6 +150,107 @@ function writeAtollProfile() {
|
|
|
112
150
|
return configPath
|
|
113
151
|
}
|
|
114
152
|
|
|
153
|
+
const projectInstructionsStart = '<!-- ATOLL:PROJECT-INSTRUCTIONS:START -->'
|
|
154
|
+
const projectInstructionsEnd = '<!-- ATOLL:PROJECT-INSTRUCTIONS:END -->'
|
|
155
|
+
const projectInstructionsPattern = new RegExp(`${projectInstructionsStart}[\\s\\S]*?${projectInstructionsEnd}`)
|
|
156
|
+
|
|
157
|
+
function projectInstructionBlock(profileName) {
|
|
158
|
+
return `${projectInstructionsStart}
|
|
159
|
+
## Atoll
|
|
160
|
+
|
|
161
|
+
For this workspace, use Atoll CLI profile \`${profileName}\`:
|
|
162
|
+
|
|
163
|
+
\`\`\`bash
|
|
164
|
+
atoll --profile ${profileName} ...
|
|
165
|
+
\`\`\`
|
|
166
|
+
${projectInstructionsEnd}`
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function removeUnmanagedAtollProfileGuidance(content) {
|
|
170
|
+
return content
|
|
171
|
+
.split('\n')
|
|
172
|
+
.filter((line) => !/atoll\s+--profile\s+\S+/i.test(line) && !/Atoll CLI profile/i.test(line))
|
|
173
|
+
.join('\n')
|
|
174
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
175
|
+
.trimEnd()
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function buildProjectInstructionUpdate(targetPath, block) {
|
|
179
|
+
const existing = existsSync(targetPath) ? readFileSync(targetPath, 'utf-8') : ''
|
|
180
|
+
|
|
181
|
+
if (projectInstructionsPattern.test(existing)) {
|
|
182
|
+
return {
|
|
183
|
+
targetPath,
|
|
184
|
+
content: existing.replace(projectInstructionsPattern, block),
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (/atoll\s+--profile\s+\S+/i.test(existing) || /Atoll CLI profile/i.test(existing)) {
|
|
189
|
+
if (!args.forceProjectInstructions) {
|
|
190
|
+
console.error(`Error: ${targetPath} already contains unmanaged Atoll profile guidance. Re-run with --force-project-instructions to replace it.`)
|
|
191
|
+
process.exit(1)
|
|
192
|
+
}
|
|
193
|
+
const cleaned = removeUnmanagedAtollProfileGuidance(existing)
|
|
194
|
+
return {
|
|
195
|
+
targetPath,
|
|
196
|
+
content: cleaned ? `${cleaned}\n\n${block}\n` : `${block}\n`,
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
targetPath,
|
|
202
|
+
content: existing.trimEnd() ? `${existing.trimEnd()}\n\n${block}\n` : `${block}\n`,
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function writeProjectInstructionUpdates(updates) {
|
|
207
|
+
const prepared = []
|
|
208
|
+
|
|
209
|
+
try {
|
|
210
|
+
for (const [index, { targetPath, content }] of updates.entries()) {
|
|
211
|
+
const tmpPath = join(dirname(targetPath), `.${basename(targetPath)}.atoll-${process.pid}-${Date.now()}-${index}.tmp`)
|
|
212
|
+
const normalized = content.endsWith('\n') ? content : `${content}\n`
|
|
213
|
+
writeFileSync(tmpPath, normalized, { flag: 'wx' })
|
|
214
|
+
prepared.push({ targetPath, tmpPath })
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
for (const { targetPath, tmpPath } of prepared) {
|
|
218
|
+
renameSync(tmpPath, targetPath)
|
|
219
|
+
console.log(`Wrote Atoll project instructions to ${targetPath}`)
|
|
220
|
+
}
|
|
221
|
+
} catch (error) {
|
|
222
|
+
for (const { tmpPath } of prepared) {
|
|
223
|
+
if (existsSync(tmpPath)) rmSync(tmpPath, { force: true })
|
|
224
|
+
}
|
|
225
|
+
console.error(`Error: failed to write Atoll project instructions: ${error.message}`)
|
|
226
|
+
process.exit(1)
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function writeProjectInstructions() {
|
|
231
|
+
if (!args.writeProjectInstructions) return
|
|
232
|
+
|
|
233
|
+
const projectDir = resolve(args.projectDir ?? process.cwd())
|
|
234
|
+
if (!existsSync(projectDir) || !statSync(projectDir).isDirectory()) {
|
|
235
|
+
console.error(`Error: --project-dir must be an existing directory: ${projectDir}`)
|
|
236
|
+
process.exit(1)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const block = projectInstructionBlock(args.profile)
|
|
240
|
+
const fileNames = {
|
|
241
|
+
agents: 'AGENTS.md',
|
|
242
|
+
claude: 'CLAUDE.md',
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const updates = []
|
|
246
|
+
for (const choice of projectInstructionFiles) {
|
|
247
|
+
const targetPath = join(projectDir, fileNames[choice])
|
|
248
|
+
updates.push(buildProjectInstructionUpdate(targetPath, block))
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
writeProjectInstructionUpdates(updates)
|
|
252
|
+
}
|
|
253
|
+
|
|
115
254
|
// 1. Install the Codex skill to ~/.codex/skills/atoll-api/
|
|
116
255
|
const codexDir = join(homedir(), '.codex')
|
|
117
256
|
mkdirSync(codexDir, { recursive: true })
|
|
@@ -126,27 +265,9 @@ console.log(`Installed Atoll skill to ${skillDest}`)
|
|
|
126
265
|
const skillMd = readFileSync(join(skillDir, 'SKILL.md'), 'utf-8')
|
|
127
266
|
// Strip YAML frontmatter for AGENTS.md
|
|
128
267
|
const body = skillMd.replace(/^---[\s\S]*?---\n*/, '')
|
|
129
|
-
const profileModeInstructions = args.profile ? `## Profile-scoped CLI configuration
|
|
130
|
-
|
|
131
|
-
This Codex install is scoped to Atoll CLI profile \`${args.profile}\`. No global \`ATOLL_API_KEY\`, \`ATOLL_ORG_ID\`, \`ATOLL_PROJECT\`, \`ATOLL_TEAM\`, or \`ATOLL_BASE_URL\` shell exports were written. Use \`atoll --profile ${args.profile} ...\` for direct Atoll CLI commands. Direct API snippets below use \`ATOLL_API_KEY\` / \`ATOLL_ORG_ID\` only when you intentionally choose env-var mode.
|
|
132
|
-
|
|
133
|
-
` : ''
|
|
134
|
-
const optionalCredentialLines = [
|
|
135
|
-
args.profile ? `- Atoll CLI profile: ${args.profile}` : null,
|
|
136
|
-
args.project ? `- Default project ID: ${args.project}` : null,
|
|
137
|
-
args.team ? `- Default team: ${args.team}` : null,
|
|
138
|
-
args.baseUrl ? `- Base URL: ${args.baseUrl}` : null,
|
|
139
|
-
].filter(Boolean).join('\n')
|
|
140
|
-
|
|
141
268
|
const agentsMd = `# Atoll Integration
|
|
142
269
|
|
|
143
|
-
${profileModeInstructions}
|
|
144
270
|
${body}
|
|
145
|
-
|
|
146
|
-
## Credentials
|
|
147
|
-
|
|
148
|
-
- API Key: ${args.profile ? `stored in Atoll CLI profile \`${args.profile}\`` : 'set as ATOLL_API_KEY environment variable'}
|
|
149
|
-
- Org ID: ${args.org}${optionalCredentialLines ? `\n${optionalCredentialLines}` : ''}
|
|
150
271
|
`
|
|
151
272
|
|
|
152
273
|
const agentsPath = join(codexDir, 'AGENTS.md')
|
|
@@ -193,10 +314,11 @@ if (args.profile) {
|
|
|
193
314
|
console.log(`Removed stale Atoll shell exports from ${profilePath}`)
|
|
194
315
|
}
|
|
195
316
|
writeAtollProfile()
|
|
317
|
+
writeProjectInstructions()
|
|
196
318
|
console.log('No Atoll shell exports were written.')
|
|
197
319
|
console.log(`Run profile-scoped commands with: atoll --profile ${args.profile} ...`)
|
|
198
320
|
console.log(`Verify the profile with: atoll --profile ${args.profile} agent-context --json`)
|
|
199
|
-
console.log(`\nDone! Codex has the Atoll skill installed, and the Atoll CLI profile "${args.profile}" is
|
|
321
|
+
console.log(`\nDone! Codex has the Atoll skill installed, and the Atoll CLI profile "${args.profile}" is configured.`)
|
|
200
322
|
process.exit(0)
|
|
201
323
|
}
|
|
202
324
|
|
package/package.json
CHANGED
package/skill/SKILL.md
CHANGED
|
@@ -123,6 +123,7 @@ atoll issue bulk-create --file ./issues.json --continue-on-error
|
|
|
123
123
|
|
|
124
124
|
# Update a task
|
|
125
125
|
atoll issue update ATOLL-42 --status in_progress
|
|
126
|
+
atoll issue update ATOLL-42 --status in_progress --comment-body "Starting this because the activation KPI is off pace."
|
|
126
127
|
atoll issue upsert ATOLL-42 --status in_progress
|
|
127
128
|
atoll issue bulk-update --file ./updates.json --dry-run
|
|
128
129
|
|
|
@@ -161,6 +162,9 @@ atoll kpi create --name paying_customers --goal "Reach 100 paying customers by Q
|
|
|
161
162
|
atoll kpi create --name mvp_tasks_done --goal "Launch MVP" --internal-task-completion
|
|
162
163
|
atoll initiative create --title "Content pipeline" --goal "Reach 100 paying customers by Q2" --status active
|
|
163
164
|
atoll initiative kpi link "Content pipeline" paying_customers --impact "+30 customers/mo"
|
|
165
|
+
atoll initiative target create "Content pipeline" --title "Publish 10 comparison posts" --mode progress --target 10 --current 0 --unit count --unit-label posts
|
|
166
|
+
atoll initiative target create "Retailer coverage" --title "Get 5 retailers live by July 5" --mode gate --target 5 --current 0 --unit count --unit-label retailers --target-date 2026-07-05 --due-soon-days 7
|
|
167
|
+
atoll initiative target issue link "Retailer coverage" "Get 5 retailers live by July 5" ATOLL-42
|
|
164
168
|
atoll kpi snapshot add paying_customers --value 42 --initiative "Content pipeline" --note "End-of-week Stripe check"
|
|
165
169
|
|
|
166
170
|
# Audit the strategy chain for gaps (orphaned initiatives, goals with no KPI, etc.)
|
|
@@ -179,14 +183,14 @@ CLI JSON conventions:
|
|
|
179
183
|
- Diagnostics and errors go to stderr.
|
|
180
184
|
- Interactive CLI update notices also go to stderr and are suppressed for JSON/non-TTY/CI/completion flows.
|
|
181
185
|
- `atoll agent-context` returns a versioned command/flag manifest, available profile context, and structured `cli.update_available` metadata.
|
|
182
|
-
- `atoll heartbeat --json` includes the same structured `cli` update metadata for agents.
|
|
186
|
+
- `atoll heartbeat --json` includes the same structured `cli` update metadata for agents, plus `attention_items`, `attention_summary`, and `recommended_action` when Atoll can propose one concrete strategy-backed next action. `atoll heartbeat --signals-only --json` preserves filtered `signals`, `attention_items`, `attention_summary`, and `recommended_action` for short polling. Handle direct attention items first, then call each handled item's `ack_endpoint`. Follow `recommended_action.usage_guidance`: prefer `suggested_write.operation` when it still matches the board, preserve KPI/initiative/initiative_target/why-now/expected-impact/first-step/success-criteria evidence, and avoid copying deferred busywork into issue or comment payloads. If a `start_work` recommendation uses `issue.update` with a body, update the issue status and preserve that body as an issue comment; `PATCH /issues/{issueId}` accepts `comment_body` for this same-request progress note.
|
|
183
187
|
- `atoll plan validate/apply` consumes `schemaVersion: "atoll.plan.v1"` files with `milestones`, `issues`, `dependencies`, `initiativeLinks`, and `milestoneLinks`; local `key` values can be referenced by `milestoneKey`, `issueKey`, `dependsOn`, `blockedBy`, or `blocks`.
|
|
184
188
|
|
|
185
189
|
## KPI HTTP Sync Drafts
|
|
186
190
|
|
|
187
191
|
When a human asks you to help automate a KPI from a third-party API, use this Atoll skill. If the current agent environment does not have the `atoll-api` skill installed, tell the user to install it before continuing or use the Atoll CLI/MCP tools directly if they are available.
|
|
188
192
|
|
|
189
|
-
Agents may create draft syncs and validate proposed configs only after a human admin has allowlisted the exact destination host in Atoll. Human admins must review the draft in
|
|
193
|
+
Agents may create draft syncs and validate proposed configs only after a human admin has allowlisted the exact destination host in Atoll. Human admins must create or review the draft in Settings > Integrations > KPI syncs, edit supported request/extraction fields and secrets through structured UI, dry-run, publish, disable, or run-now with snapshot writing.
|
|
190
194
|
|
|
191
195
|
```bash
|
|
192
196
|
atoll kpi sync validate <kpi-id> \
|
|
@@ -313,14 +317,18 @@ The primary pattern for autonomous agents. Prefer `atoll heartbeat --json` when
|
|
|
313
317
|
|
|
314
318
|
- **Goal status** with days remaining
|
|
315
319
|
- **KPI pace**: `pace_needed` vs `pace_actual`, trend (`accelerating`/`decelerating`/`flat`), staleness
|
|
316
|
-
- **Initiative progress**: total/completed/stalled/blocked issue counts, expected KPI impacts
|
|
320
|
+
- **Initiative progress**: total/completed/stalled/blocked issue counts, expected KPI impacts, and initiative targets
|
|
317
321
|
- **Assigned work** for this agent
|
|
318
322
|
- **Project context**: relevant board columns, including optional descriptions that explain stage criteria for agents
|
|
319
323
|
- **Signals** sorted by severity — the agent's prioritized to-do list
|
|
324
|
+
- **Attention items**: direct current-member notifications such as mentions, assignments, assignee comments, and creator-visible status changes, with an `ack_endpoint` to call after handling
|
|
325
|
+
- **Recommended action**: one deterministic strategy-backed next action when Atoll has enough evidence (`create_work`, `start_work`, `escalate_blocker`, or `refresh_metric`), including why-now, expected impact, first step, success criteria, quality warnings, and any suggested write.
|
|
320
326
|
|
|
321
327
|
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
328
|
|
|
323
|
-
Signal types: `kpi_off_pace`, `kpi_stale`, `issue_stale`, `issue_blocked`, `milestone_overdue`, `initiative_stalled`, `webhook_failing`. Severity: `info`, `warning`, `critical`.
|
|
329
|
+
Signal types: `kpi_off_pace`, `kpi_stale`, `issue_stale`, `issue_blocked`, `milestone_overdue`, `initiative_stalled`, `initiative_target_due_soon`, `initiative_target_overdue`, `initiative_target_blocked`, `webhook_failing`. Severity: `info`, `warning`, `critical`.
|
|
330
|
+
|
|
331
|
+
Targets under initiatives are commitments, not business KPIs. KPIs measure business outcomes such as MRR, traffic, paying customers, or onboarding success. Use progress targets for initiative outputs such as "publish 10 comparison posts." Use gate targets for launch prerequisites such as "get 5 retailers live by July 5." Gate targets emit stateful due/blocked messages and should not be converted into fractional KPI pace such as "0.07 retailers/day."
|
|
324
332
|
|
|
325
333
|
Useful CLI forms:
|
|
326
334
|
|
|
@@ -333,10 +341,12 @@ atoll heartbeat --json
|
|
|
333
341
|
|
|
334
342
|
**The agent loop:**
|
|
335
343
|
1. Call heartbeat
|
|
336
|
-
2.
|
|
337
|
-
3.
|
|
338
|
-
4.
|
|
339
|
-
5.
|
|
344
|
+
2. Handle direct `attention_items` that need a reply, task update, or blocker follow-up
|
|
345
|
+
3. Call each handled item's `ack_endpoint`
|
|
346
|
+
4. Read remaining signals (highest severity first)
|
|
347
|
+
5. Reason about highest-leverage action given direct attention, gate targets, KPI pace, and initiative state
|
|
348
|
+
6. Execute (unblock issues, update KPIs, create work, report progress)
|
|
349
|
+
7. Repeat
|
|
340
350
|
|
|
341
351
|
## Other Common Workflows
|
|
342
352
|
|
|
@@ -345,7 +355,7 @@ atoll heartbeat --json
|
|
|
345
355
|
```bash
|
|
346
356
|
atoll heartbeat --signals-only # orient first
|
|
347
357
|
atoll issue list --status todo --assignee self --json # find assigned work
|
|
348
|
-
atoll issue update ATOLL-42 --status in_progress
|
|
358
|
+
atoll issue update ATOLL-42 --status in_progress --comment-body "Starting because the linked KPI is off pace." # start work with durable context
|
|
349
359
|
atoll comment add ATOLL-42 --body "Progress update…" # report progress
|
|
350
360
|
atoll issue update ATOLL-42 --status done # complete
|
|
351
361
|
```
|
|
@@ -357,7 +367,8 @@ atoll issue update ATOLL-42 --status done # complete
|
|
|
357
367
|
3. `POST /api/orgs/{id}/kpis/{kpiId}/snapshots` -- record measurement (auto-updates `current_value`)
|
|
358
368
|
4. `POST /api/orgs/{id}/initiatives` -- create initiative linked to goal
|
|
359
369
|
5. `POST /api/orgs/{id}/initiatives/{id}/kpi-impacts` -- declare expected KPI impact
|
|
360
|
-
6.
|
|
370
|
+
6. `POST /api/orgs/{id}/initiatives/{id}/targets` -- create progress or gate targets for initiative commitments
|
|
371
|
+
7. Link issues and milestones to the initiative and to specific targets when the work exists to satisfy that target
|
|
361
372
|
|
|
362
373
|
CLI equivalent:
|
|
363
374
|
|
|
@@ -366,6 +377,8 @@ atoll goal create --title "Reach 100 paying customers by Q2" --target-date 2026-
|
|
|
366
377
|
atoll kpi create --name paying_customers --goal "Reach 100 paying customers by Q2" --unit count --target 100 --current 34
|
|
367
378
|
atoll initiative create --title "Content pipeline" --goal "Reach 100 paying customers by Q2" --status active
|
|
368
379
|
atoll initiative kpi link "Content pipeline" paying_customers --impact "+30 customers/mo"
|
|
380
|
+
atoll initiative target create "Content pipeline" --title "Publish 10 comparison posts" --mode progress --target 10 --current 0 --unit count --unit-label posts
|
|
381
|
+
atoll initiative target create "Retailer coverage" --title "Get 5 retailers live by July 5" --mode gate --target 5 --current 0 --unit count --unit-label retailers --target-date 2026-07-05 --due-soon-days 7
|
|
369
382
|
atoll kpi snapshot add paying_customers --value 42 --initiative "Content pipeline" --note "End-of-week Stripe check"
|
|
370
383
|
```
|
|
371
384
|
|
|
@@ -412,7 +425,7 @@ Delivery rows expose `delivery_id`, `status`, and `next_retry_at`. Network failu
|
|
|
412
425
|
|
|
413
426
|
### Billing and plan limits
|
|
414
427
|
|
|
415
|
-
Owners/admins can read billing state with `GET /api/orgs/{id}/billing` and start Stripe
|
|
428
|
+
Owners/admins can read billing state with `GET /api/orgs/{id}/billing` and start a self-serve Stripe billing flow with `POST /api/orgs/{id}/billing/checkout` using `{ "plan": "starter" }`, `{ "plan": "team" }`, or `{ "plan": "pro" }`. Owner/admin read requests sync Stripe first and return `502` with `Stripe billing sync failed` if that sync cannot complete, rather than serving stale local billing state. New subscribers use Checkout; existing active, trialing, or past-due subscribers use a Billing Portal update confirmation.
|
|
416
429
|
|
|
417
430
|
Creation endpoints can return `402` with `code: "PLAN_LIMIT_REACHED"` when an org reaches limits for humans, agents/integrations, active projects, or active issues.
|
|
418
431
|
|
|
@@ -440,6 +453,10 @@ Initiative create accepts `title` or legacy `name`, plus camelCase aliases `goal
|
|
|
440
453
|
|
|
441
454
|
All endpoints are under `/api/orgs/{orgId}/...`.
|
|
442
455
|
|
|
456
|
+
Issue comments inherit issue project permissions: listing comments requires access to the issue's project, comment writes (add, edit, delete) require write access to that project, edit/delete still require comment authorship, and guests cannot access comments on unprojected issues.
|
|
457
|
+
|
|
458
|
+
Comment bodies accept Markdown/plain text or existing rich-text HTML. Atoll stores and returns comment bodies as sanitized HTML. If sanitization leaves no visible text or safe media, the request returns `400` with `body is required` for direct comments or `comment_body is required` for issue updates with `comment_body`.
|
|
459
|
+
|
|
443
460
|
† `DELETE /issues/{id}` requires `owner` or `admin` role — any caller without that role (including member-role agents) gets `403`. If you just need to remove a task, use `POST /api/orgs/{orgId}/issues/{issueId}/archive` (soft delete, no role gate); reverse with `DELETE` on the same path (unarchive). In the CLI, prefer `atoll issue archive <id>`. Permanent `atoll issue delete <id>` requires `--force` and supports `--dry-run`.
|
|
444
461
|
|
|
445
462
|
### Quick enum reference
|
|
@@ -488,7 +505,7 @@ atoll feedback resend fb_123
|
|
|
488
505
|
## Notes
|
|
489
506
|
|
|
490
507
|
- Request bodies accept camelCase; responses use snake_case
|
|
491
|
-
- Descriptions and
|
|
508
|
+
- Descriptions support Markdown; comment bodies accept Markdown/plain text or rich-text HTML and are stored as sanitized HTML
|
|
492
509
|
- All timestamps are ISO 8601 UTC
|
|
493
510
|
- Board statuses are customizable per project -- query `/board-columns` for available values and optional column descriptions
|
|
494
511
|
- API changes appear in real-time on the web board
|
|
@@ -89,12 +89,12 @@ Access levels: `view`, `edit`, `admin` (default: `view`).
|
|
|
89
89
|
|
|
90
90
|
## Billing
|
|
91
91
|
|
|
92
|
-
Org billing is managed through Stripe. Owners/admins can
|
|
92
|
+
Org billing is managed through Stripe. Owners/admins can start self-serve billing flows and create billing portal sessions.
|
|
93
93
|
|
|
94
94
|
| Method | Endpoint | Description |
|
|
95
95
|
|--------|----------|-------------|
|
|
96
|
-
| GET | `/api/orgs/{id}/billing` | Get plan, status, usage, limits, and subscription summary |
|
|
97
|
-
| POST | `/api/orgs/{id}/billing/checkout` |
|
|
96
|
+
| GET | `/api/orgs/{id}/billing` | Get plan, status, usage, limits, and subscription summary; owner/admin read requests sync Stripe first and return `502` if that sync fails |
|
|
97
|
+
| POST | `/api/orgs/{id}/billing/checkout` | Start Stripe billing flow (`{ plan: "starter" \| "team" \| "pro" }`); new subscribers use Checkout and existing active/trialing/past-due subscribers use Billing Portal update confirmation |
|
|
98
98
|
| POST | `/api/orgs/{id}/billing/portal` | Create Stripe Billing Portal Session |
|
|
99
99
|
|
|
100
100
|
Plan limits are enforced when creating projects, human members, agents/integrations, and active tasks. Limit errors return `402` with `code: "PLAN_LIMIT_REACHED"`.
|
|
@@ -106,7 +106,7 @@ Plan limits are enforced when creating projects, human members, agents/integrati
|
|
|
106
106
|
| GET | `/api/orgs/{id}/issues` | List tasks (see filters below) |
|
|
107
107
|
| POST | `/api/orgs/{id}/issues` | Create task |
|
|
108
108
|
| GET | `/api/orgs/{id}/issues/{issueId}` | Get task detail |
|
|
109
|
-
| PATCH | `/api/orgs/{id}/issues/{issueId}` | Update task |
|
|
109
|
+
| PATCH | `/api/orgs/{id}/issues/{issueId}` | Update task; optional `comment_body` also adds a task comment in the same request |
|
|
110
110
|
| DELETE | `/api/orgs/{id}/issues/{issueId}` | Delete task (admin/owner only) |
|
|
111
111
|
| POST | `/api/orgs/{id}/issues/bulk` | Bulk create tasks (up to 50) |
|
|
112
112
|
| GET | `/api/orgs/{id}/issues/search?q=...` | Search tasks by title |
|
|
@@ -120,7 +120,8 @@ Issue-centric initiative links follow task project permissions: reading links re
|
|
|
120
120
|
- `status` -- `backlog`, `todo`, `in_progress`, `done`, `cancelled`
|
|
121
121
|
- `priority` -- `0` (urgent), `1` (high), `2` (medium), `3` (low)
|
|
122
122
|
- `projectId`, `assigneeId`, `teamId`, `milestoneId`
|
|
123
|
-
- `q` -- search title and description (case-insensitive)
|
|
123
|
+
- `q` -- full issue lists search title and description (case-insensitive)
|
|
124
|
+
- Compact views (`view=board` or `view=list`) also support `assignee` (member ID or `unassigned`, including multi-assignee links), `initiativeId`, `scope` (`mine` or `blocked`), and `q` over title plus issue number
|
|
124
125
|
- `includeArchived` -- `true` to include archived tasks
|
|
125
126
|
- `orderBy` -- `created_at` (default), `updated_at`, `priority`, `due_date`, `title`, `status`
|
|
126
127
|
- `orderDir` -- `asc` or `desc` (default)
|
|
@@ -149,6 +150,10 @@ Add with `{ "blockedByIssueId": "uuid" }` or `{ "blockingIssueId": "uuid" }`. Ci
|
|
|
149
150
|
| PATCH | `/api/orgs/{id}/issues/{issueId}/comments/{commentId}` | Edit comment |
|
|
150
151
|
| DELETE | `/api/orgs/{id}/issues/{issueId}/comments/{commentId}` | Delete comment |
|
|
151
152
|
|
|
153
|
+
Issue comments inherit issue project permissions: listing comments requires access to the issue's project, comment writes (add, edit, delete) require write access to that project, edit/delete still require comment authorship, and guests cannot access comments on unprojected issues.
|
|
154
|
+
|
|
155
|
+
Comment bodies accept Markdown/plain text or existing rich-text HTML. Atoll stores and returns comment bodies as sanitized HTML. If sanitization leaves no visible text or safe media, the request returns `400` with `body is required` for direct comments or `comment_body is required` for issue updates with `comment_body`.
|
|
156
|
+
|
|
152
157
|
## Subtasks
|
|
153
158
|
|
|
154
159
|
| Method | Endpoint | Description |
|
|
@@ -203,6 +208,7 @@ Roles: `owner`, `admin`, `member`, `guest`.
|
|
|
203
208
|
| POST | `/api/orgs/{id}/kpis/{kpiId}/snapshots` | Record a snapshot |
|
|
204
209
|
| GET | `/api/orgs/{id}/kpi-http-sync-policy` | List exact-host KPI HTTP sync allowlist policy |
|
|
205
210
|
| POST | `/api/orgs/{id}/kpi-http-sync-policy` | Add an allowed exact host (human admin only) |
|
|
211
|
+
| GET | `/api/orgs/{id}/kpi-http-syncs` | List org-wide KPI HTTP sync review rows for Settings; admins get config/secret metadata, members get redacted status rows |
|
|
206
212
|
| GET | `/api/orgs/{id}/kpis/{kpiId}/http-syncs` | List KPI HTTP syncs |
|
|
207
213
|
| POST | `/api/orgs/{id}/kpis/{kpiId}/http-syncs` | Create a draft KPI HTTP sync |
|
|
208
214
|
| PUT | `/api/orgs/{id}/kpis/{kpiId}/http-syncs` | Validate a proposed KPI HTTP sync config without storing or running it |
|
|
@@ -244,6 +250,19 @@ Create accepts `title` or legacy `name`, plus camelCase aliases `goalId`, `owner
|
|
|
244
250
|
| GET | `.../initiatives/{id}/milestones` | List linked milestones |
|
|
245
251
|
| POST | `.../initiatives/{id}/milestones` | Link milestone (`{ milestone_id }`) |
|
|
246
252
|
| DELETE | `.../initiatives/{id}/milestones/{milestoneId}` | Unlink milestone |
|
|
253
|
+
| GET | `.../initiatives/{id}/targets` | List initiative targets |
|
|
254
|
+
| POST | `.../initiatives/{id}/targets` | Create target (`{ title, mode?, current_value?, target_value?, unit?, unit_label?, target_date?, due_soon_days? }`) |
|
|
255
|
+
| GET | `.../initiatives/{id}/targets/{targetId}` | Get target |
|
|
256
|
+
| PATCH | `.../initiatives/{id}/targets/{targetId}` | Update target |
|
|
257
|
+
| DELETE | `.../initiatives/{id}/targets/{targetId}` | Delete target |
|
|
258
|
+
| GET | `.../initiatives/{id}/targets/{targetId}/issues` | List target issue links |
|
|
259
|
+
| POST | `.../initiatives/{id}/targets/{targetId}/issues` | Link issue to target (`{ issue_id }`) |
|
|
260
|
+
| DELETE | `.../initiatives/{id}/targets/{targetId}/issues/{issueId}` | Unlink issue from target |
|
|
261
|
+
| GET | `.../initiatives/{id}/targets/{targetId}/milestones` | List target milestone links |
|
|
262
|
+
| POST | `.../initiatives/{id}/targets/{targetId}/milestones` | Link milestone to target (`{ milestone_id }`) |
|
|
263
|
+
| DELETE | `.../initiatives/{id}/targets/{targetId}/milestones/{milestoneId}` | Unlink milestone from target |
|
|
264
|
+
|
|
265
|
+
Targets are initiative-level commitments. Use `mode: "progress"` for normal output tracking and `mode: "gate"` for launch blockers or prerequisites where KPI pace language would be misleading. Targets do not create KPI snapshots.
|
|
247
266
|
|
|
248
267
|
## Strategy
|
|
249
268
|
|
|
@@ -259,7 +278,7 @@ Returns findings only (not the full graph). Use it for a high-level review — o
|
|
|
259
278
|
|--------|----------|-------------|
|
|
260
279
|
| GET | `/api/orgs/{id}/heartbeat` | Get heartbeat context for the authenticated agent |
|
|
261
280
|
|
|
262
|
-
Returns computed briefing with goal status, KPI pace/trend, initiative progress, assigned work, and
|
|
281
|
+
Returns computed briefing with goal status, KPI pace/trend, initiative progress, assigned work, direct `attention_items`, `attention_summary`, signals, and a deterministic `recommended_action` when Atoll can propose one concrete strategy-backed next action. 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
282
|
|
|
264
283
|
Signal types: `kpi_off_pace`, `kpi_stale`, `issue_stale`, `issue_blocked`, `milestone_overdue`, `initiative_stalled`, `webhook_failing`. Severity: `info`, `warning`, `critical`.
|
|
265
284
|
|
|
@@ -271,6 +290,8 @@ atoll heartbeat --signals-only
|
|
|
271
290
|
atoll heartbeat --severity critical
|
|
272
291
|
```
|
|
273
292
|
|
|
293
|
+
`atoll heartbeat --signals-only --json` returns filtered `signals`, direct `attention_items`, `attention_summary`, and `recommended_action` for polling agents.
|
|
294
|
+
|
|
274
295
|
## Activity
|
|
275
296
|
|
|
276
297
|
| Method | Endpoint | Description |
|
|
@@ -419,10 +440,16 @@ URL must be an HTTPS DNS hostname. IP literals, `localhost`, and `.local` hosts
|
|
|
419
440
|
|
|
420
441
|
| Method | Endpoint | Description |
|
|
421
442
|
|--------|----------|-------------|
|
|
443
|
+
| GET | `/api/orgs/{id}/notifications` | List unread actionable notifications for current org member |
|
|
444
|
+
| POST | `/api/orgs/{id}/notifications/{notificationId}/ack` | Acknowledge current-member notification |
|
|
445
|
+
| GET | `/api/orgs/{id}/notifications/preferences` | Read current-member notification preferences, including default-on mention notifications |
|
|
446
|
+
| POST | `/api/orgs/{id}/notifications/preferences` | Update current-member notification preferences, including mention opt-out and cleanup |
|
|
422
447
|
| GET | `/api/notifications` | List notifications (last 50, unread first) |
|
|
423
448
|
| POST | `/api/notifications/{id}/read` | Mark as read |
|
|
424
449
|
| POST | `/api/notifications/read-all` | Mark all as read |
|
|
425
450
|
|
|
451
|
+
Current-member notifications can include `mention.created`, `issue.assigned`, `comment.added`, and `issue.status_changed`. Notification preferences currently support mention opt-out for `mention.created`. Disabling in-app `mention.created` delivery also attempts to acknowledge that member's currently unread mention notifications; when cleanup succeeds, muted mentions leave both the bell and heartbeat `attention_items`.
|
|
452
|
+
|
|
426
453
|
## Agents
|
|
427
454
|
|
|
428
455
|
| Method | Endpoint | Description |
|
|
@@ -201,6 +201,27 @@ Use `title` for create/update requests; create also accepts legacy `name`. Atoll
|
|
|
201
201
|
|
|
202
202
|
Add/remove projects with `{ "project_id": "uuid" }`.
|
|
203
203
|
|
|
204
|
+
## Initiative Target Fields
|
|
205
|
+
|
|
206
|
+
Targets attach to initiatives and track commitments separately from business KPIs. Use `mode: "progress"` for initiative outputs and `mode: "gate"` for hard launch prerequisites. Gate target heartbeat signals use stateful copy such as `0/5 retailers complete`; agents must not convert them into fractional KPI pace.
|
|
207
|
+
|
|
208
|
+
```json
|
|
209
|
+
{
|
|
210
|
+
"title": "Get 5 retailers live by July 5",
|
|
211
|
+
"description": "Prerequisite before price comparison launch",
|
|
212
|
+
"mode": "gate",
|
|
213
|
+
"unit": "count",
|
|
214
|
+
"unit_label": "retailers",
|
|
215
|
+
"current_value": 0,
|
|
216
|
+
"target_value": 5,
|
|
217
|
+
"target_direction": "increase",
|
|
218
|
+
"target_date": "2026-07-05",
|
|
219
|
+
"due_soon_days": 7
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Target work links use `{ "issue_id": "issue-uuid" }` at `.../targets/{targetId}/issues` and `{ "milestone_id": "milestone-uuid" }` at `.../targets/{targetId}/milestones`. Target response rows include linked `issueIds` and `milestoneIds` when returned by the target list/get endpoints.
|
|
224
|
+
|
|
204
225
|
## Automation Rule Fields
|
|
205
226
|
|
|
206
227
|
```json
|
|
@@ -233,7 +254,7 @@ Add/remove projects with `{ "project_id": "uuid" }`.
|
|
|
233
254
|
|
|
234
255
|
## Board Context Response
|
|
235
256
|
|
|
236
|
-
`GET /api/orgs/{id}/projects/{projectId}/board-context` returns the strategy data used by the board
|
|
257
|
+
`GET /api/orgs/{id}/projects/{projectId}/board-context` returns the strategy data used by the board filter toolbar:
|
|
237
258
|
|
|
238
259
|
```json
|
|
239
260
|
{
|
|
@@ -333,7 +354,20 @@ Proposal JSON currently supports at most one item in each collection: `projects`
|
|
|
333
354
|
"total_issues": 8,
|
|
334
355
|
"completed_issues": 3,
|
|
335
356
|
"stalled_issues": 2,
|
|
336
|
-
"blocked_issues": 1
|
|
357
|
+
"blocked_issues": 1,
|
|
358
|
+
"project_ids": ["..."],
|
|
359
|
+
"linked_issues": [{
|
|
360
|
+
"id": "...",
|
|
361
|
+
"title": "Publish comparison page",
|
|
362
|
+
"status": "todo",
|
|
363
|
+
"priority": 1,
|
|
364
|
+
"assignee_id": "...",
|
|
365
|
+
"project_id": "...",
|
|
366
|
+
"milestone_id": null,
|
|
367
|
+
"number": 42,
|
|
368
|
+
"blocked": false,
|
|
369
|
+
"updated_at": "2026-03-28T12:00:00Z"
|
|
370
|
+
}]
|
|
337
371
|
}]
|
|
338
372
|
}],
|
|
339
373
|
"standalone_kpis": [...],
|
|
@@ -349,12 +383,59 @@ Proposal JSON currently supports at most one item in each collection: `projects`
|
|
|
349
383
|
}],
|
|
350
384
|
"signals": [
|
|
351
385
|
{ "type": "kpi_off_pace", "severity": "warning", "message": "..." }
|
|
352
|
-
]
|
|
386
|
+
],
|
|
387
|
+
"recommended_action": {
|
|
388
|
+
"id": "create_work:...",
|
|
389
|
+
"action_type": "create_work",
|
|
390
|
+
"title": "Create Content pipeline work for paying_customers",
|
|
391
|
+
"target_type": "initiative",
|
|
392
|
+
"target_id": "...",
|
|
393
|
+
"goal_id": "...",
|
|
394
|
+
"kpi_id": "...",
|
|
395
|
+
"initiative_id": "...",
|
|
396
|
+
"source_signal_ids": ["kpi_off_pace:..."],
|
|
397
|
+
"why_now": "paying_customers is off pace, and Content pipeline has no active linked issue.",
|
|
398
|
+
"expected_impact": "Create the missing execution path for the initiative expected to move paying_customers: +30 signups/mo.",
|
|
399
|
+
"evidence": ["KPI \"paying_customers\" is off pace..."],
|
|
400
|
+
"first_step": "Open Content pipeline and define the smallest task that can move paying_customers.",
|
|
401
|
+
"success_criteria": ["Create or update concrete follow-up actions tied to paying_customers."],
|
|
402
|
+
"suggested_write": {
|
|
403
|
+
"operation": "issue.create",
|
|
404
|
+
"title": "Create Content pipeline work for paying_customers",
|
|
405
|
+
"body": "<h2>Why now</h2>...",
|
|
406
|
+
"status": "todo",
|
|
407
|
+
"priority": 1,
|
|
408
|
+
"project_id": "...",
|
|
409
|
+
"initiative_id": "...",
|
|
410
|
+
"kpi_id": "...",
|
|
411
|
+
"initiative_target_id": "..."
|
|
412
|
+
},
|
|
413
|
+
"confidence": "high",
|
|
414
|
+
"caveats": [],
|
|
415
|
+
"quality_checks": [{ "id": "kpi_link", "status": "pass", "message": "Recommendation includes a KPI link." }],
|
|
416
|
+
"usage_guidance": {
|
|
417
|
+
"instructions": [
|
|
418
|
+
"Prefer suggested_write.operation when it matches the current board state and the recommendation is still current.",
|
|
419
|
+
"Preserve goal, KPI, initiative, initiative target, why-now, expected impact, first step, suggested_write, and success criteria evidence in any issue, status update, KPI refresh, or comment you create.",
|
|
420
|
+
"Do not copy deferred busywork, unrelated tasks, or caveat text into write payloads unless it is directly needed for the recommended action."
|
|
421
|
+
],
|
|
422
|
+
"preserve_fields": ["goal_id", "kpi_id", "initiative_id", "initiative_target_id", "why_now", "expected_impact", "first_step", "success_criteria", "suggested_write"],
|
|
423
|
+
"avoid_payload_sources": ["deferred_busywork", "unrelated_assigned_issues", "stale_recommendations_after_board_change"]
|
|
424
|
+
}
|
|
425
|
+
}
|
|
353
426
|
}
|
|
354
427
|
```
|
|
355
428
|
|
|
356
429
|
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.
|
|
357
430
|
|
|
431
|
+
Heartbeat also includes `attention_items` for direct current-member notifications such as mentions, assignments, assignee comments, and creator-visible status changes. Each attention item includes `id`, `source`, `event_type`, `severity`, `action_kind`, resource fields, `target_path`, `created_at`, and `ack_endpoint`; after handling the referenced item, call `ack_endpoint` so the notification is acknowledged and removed from later heartbeat attention results. `attention_summary` includes counts such as `mentions`, `assignments`, `blockers`, and `total_unread`.
|
|
432
|
+
|
|
433
|
+
Current-member notifications can use `event_type` values such as `mention.created`, `issue.assigned`, `comment.added`, and `issue.status_changed`. Notification preferences use `event_type` (currently `mention.created`), `channel` (currently `in_app`), and `enabled` for current-member mention opt-out. Setting `enabled: false` for in-app `mention.created` also attempts to acknowledge that member's currently unread mention notifications; when cleanup succeeds, they no longer appear in notification lists or heartbeat `attention_items`.
|
|
434
|
+
|
|
435
|
+
Agents should follow `recommended_action.usage_guidance`: prefer `suggested_write.operation` when it still matches the board, preserve KPI/initiative/initiative-target/why-now/expected-impact/first-step/success-criteria evidence in any write, and avoid copying deferred busywork or unrelated assigned tasks into issue or comment payloads. When `start_work` uses `suggested_write.operation: "issue.update"` with a body, apply the status update and preserve that body as an issue comment; `PATCH /issues/{issueId}` accepts `comment_body` for this same-request progress note.
|
|
436
|
+
|
|
437
|
+
`recommended_action` is a deterministic strategy-backed next action built from heartbeat context. MVP action types are `create_work`, `start_work`, `escalate_blocker`, and `refresh_metric`; suggested writes may prefill issue creation, issue status updates, blocker comments, or KPI refresh requests. Issue-create bodies are HTML for Atoll's rich-text issue description; blocker/comment and metric-refresh bodies are plain text.
|
|
438
|
+
|
|
358
439
|
## Strategy Audit Response
|
|
359
440
|
|
|
360
441
|
`GET /api/orgs/{id}/strategy/audit` returns findings (sorted critical → warning → info), each with a concrete `suggested_fix`, plus summary counts.
|
|
@@ -376,11 +457,11 @@ Heartbeat is org-scoped, but project-bound goals, KPIs, initiatives, issue healt
|
|
|
376
457
|
}
|
|
377
458
|
```
|
|
378
459
|
|
|
379
|
-
Each finding carries whichever entity ids apply: `goal_id`, `kpi_id`, `initiative_id`, `issue_id`, `milestone_id`, `project_id`. Finding `type` values:
|
|
460
|
+
Each finding carries whichever entity ids apply: `goal_id`, `kpi_id`, `initiative_id`, `initiative_target_id`, `issue_id`, `milestone_id`, `project_id`. Finding `type` values:
|
|
380
461
|
|
|
381
462
|
- Structural: `initiative_orphaned`, `kpi_orphaned`, `goal_missing_kpi`, `goal_missing_initiative`, `dangling_initiative_project`, `dangling_initiative_issue`, `dangling_initiative_milestone`
|
|
382
463
|
- KPI health: `kpi_unrecorded`, `kpi_missing_target`, `kpi_stale`, `kpi_off_pace`
|
|
383
|
-
- Initiative health: `initiative_missing_impact`, `initiative_missing_execution`, `initiative_stalled`
|
|
464
|
+
- Initiative health: `initiative_missing_impact`, `initiative_missing_execution`, `initiative_stalled`, `initiative_target_missing_execution`, `initiative_target_overdue`, `initiative_target_blocked`
|
|
384
465
|
- Execution: `issue_blocked`, `issue_overdue`, `milestone_overdue`
|
|
385
466
|
|
|
386
467
|
## Analytics Response
|
|
@@ -403,6 +484,7 @@ Each finding carries whichever entity ids apply: `goal_id`, `kpi_id`, `initiativ
|
|
|
403
484
|
| Task | `status` | `backlog`, `todo`, `in_progress`, `done`, `cancelled` (custom per project via board-columns) |
|
|
404
485
|
| Board column | `description` | Optional stage criteria or agent guidance |
|
|
405
486
|
| Task | `priority` | `0` (urgent), `1` (high), `2` (medium), `3` (low) |
|
|
487
|
+
| Task update request | `comment_body` | Optional Markdown/plain text or rich-text HTML comment body created with the issue update; stored and returned as sanitized HTML |
|
|
406
488
|
| Task | `recurrenceType` | `daily`, `weekly`, `monthly`, `yearly` |
|
|
407
489
|
| Goal | `status` | `active`, `achieved`, `missed`, `paused`, `cancelled` |
|
|
408
490
|
| KPI | `unit` | `count`, `percentage`, `currency`, `duration`, `ratio`, `custom` |
|
|
@@ -417,7 +499,7 @@ Each finding carries whichever entity ids apply: `goal_id`, `kpi_id`, `initiativ
|
|
|
417
499
|
| Member | `role` | `owner`, `admin`, `member`, `guest` |
|
|
418
500
|
| Project member | `accessLevel` | `view`, `edit`, `admin` |
|
|
419
501
|
| Automation | `trigger_event` | `issue.created`, `issue.status_changed`, `issue.assigned`, `issue.priority_changed` |
|
|
420
|
-
| Heartbeat signal | `type` | `kpi_off_pace`, `kpi_stale`, `issue_stale`, `issue_blocked`, `milestone_overdue`, `initiative_stalled`, `webhook_failing` |
|
|
502
|
+
| Heartbeat signal | `type` | `kpi_off_pace`, `kpi_stale`, `issue_stale`, `issue_blocked`, `milestone_overdue`, `initiative_stalled`, `initiative_target_due_soon`, `initiative_target_overdue`, `initiative_target_blocked`, `webhook_failing` |
|
|
421
503
|
| Heartbeat signal | `severity` | `info`, `warning`, `critical` |
|
|
422
504
|
| Custom view | `display_mode` | `board`, `list` |
|
|
423
505
|
|
|
@@ -430,7 +512,7 @@ REST list responses use resource-specific keys by default. Main list endpoints s
|
|
|
430
512
|
## Notes
|
|
431
513
|
|
|
432
514
|
- All timestamps are ISO 8601 in UTC
|
|
433
|
-
-
|
|
515
|
+
- Descriptions support Markdown; comment bodies accept Markdown/plain text or rich-text HTML and are stored as sanitized HTML
|
|
434
516
|
- Board columns (statuses) are customizable per project -- query `/board-columns` for available statuses and optional descriptions
|
|
435
517
|
- Default statuses for new projects: `backlog`, `todo`, `in_progress`, `done`
|
|
436
518
|
- `cancelled` is always valid but not shown on the board
|