@agent-native/core 0.203.2-nightly-20261007213109 → 0.204.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 (121) hide show
  1. package/dist/a2a/handlers.js +57 -1
  2. package/dist/a2a/server.js +37 -1
  3. package/dist/action.d.ts +1 -0
  4. package/dist/action.js +26 -3
  5. package/dist/agent/contain-principal.d.ts +9 -0
  6. package/dist/agent/contain-principal.js +53 -0
  7. package/dist/agent/production-agent.js +32 -2
  8. package/dist/agent/run-manager.js +14 -2
  9. package/dist/agent/run-store.js +66 -12
  10. package/dist/audit/actions/export-audit-events.d.ts +1 -1
  11. package/dist/audit/actions/export-audit-events.js +2 -2
  12. package/dist/audit/actions/export-audit-ocsf.d.ts +11 -0
  13. package/dist/audit/actions/export-audit-ocsf.js +183 -0
  14. package/dist/audit/actions/list-audit-events.d.ts +1 -1
  15. package/dist/audit/actions/list-audit-events.js +3 -3
  16. package/dist/audit/config.d.ts +1 -1
  17. package/dist/audit/config.js +5 -1
  18. package/dist/audit/index.d.ts +1 -0
  19. package/dist/audit/index.js +1 -0
  20. package/dist/audit/ocsf.d.ts +70 -0
  21. package/dist/audit/ocsf.js +228 -0
  22. package/dist/audit/record.js +1 -1
  23. package/dist/audit/store.js +9 -2
  24. package/dist/audit/types.d.ts +12 -1
  25. package/dist/cli/agent.js +2 -11
  26. package/dist/cli/connect.js +7 -2
  27. package/dist/client/use-db-sync.d.ts +13 -0
  28. package/dist/client/use-db-sync.js +22 -1
  29. package/dist/coding-tools/run-code.js +22 -1
  30. package/dist/coding-tools/sandbox/local-child-process-adapter.js +20 -0
  31. package/dist/collab/client.js +36 -3
  32. package/dist/collab/routes.d.ts +9 -4
  33. package/dist/collab/routes.js +15 -1
  34. package/dist/collab/struct-routes.d.ts +1 -1
  35. package/dist/deploy/build.js +15 -8
  36. package/dist/deploy/workspace-deploy.js +88 -13
  37. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  38. package/dist/file-upload/builder.js +18 -4
  39. package/dist/framework-tools.js +3 -0
  40. package/dist/identity/rekey.js +6 -0
  41. package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
  42. package/dist/localization/core-messages/supplemental/ar-SA.js +15 -0
  43. package/dist/localization/core-messages/supplemental/de-DE.js +15 -0
  44. package/dist/localization/core-messages/supplemental/en-US.js +15 -0
  45. package/dist/localization/core-messages/supplemental/es-ES.js +15 -0
  46. package/dist/localization/core-messages/supplemental/fr-FR.js +15 -0
  47. package/dist/localization/core-messages/supplemental/hi-IN.js +15 -0
  48. package/dist/localization/core-messages/supplemental/ja-JP.js +15 -0
  49. package/dist/localization/core-messages/supplemental/ko-KR.js +15 -0
  50. package/dist/localization/core-messages/supplemental/pt-BR.js +15 -0
  51. package/dist/localization/core-messages/supplemental/zh-CN.js +15 -0
  52. package/dist/localization/core-messages/supplemental/zh-TW.js +15 -0
  53. package/dist/localization/mcp-settings-messages.d.ts +15 -0
  54. package/dist/mcp/actions/create-org-service-token.d.ts +8 -0
  55. package/dist/mcp/actions/create-org-service-token.js +93 -4
  56. package/dist/mcp/actions/list-org-service-tokens.d.ts +2 -0
  57. package/dist/mcp/actions/list-org-service-tokens.js +36 -8
  58. package/dist/mcp/actions/service-principal-input.d.ts +48 -0
  59. package/dist/mcp/actions/service-principal-input.js +102 -0
  60. package/dist/mcp/actions/set-service-principal-lifecycle.d.ts +15 -0
  61. package/dist/mcp/actions/set-service-principal-lifecycle.js +150 -0
  62. package/dist/mcp/actions/set-service-principal-policy.d.ts +14 -0
  63. package/dist/mcp/actions/set-service-principal-policy.js +105 -0
  64. package/dist/mcp/build-server.js +117 -13
  65. package/dist/mcp/connect-route.js +116 -4
  66. package/dist/mcp/connect-store.d.ts +7 -0
  67. package/dist/mcp/connect-store.js +21 -14
  68. package/dist/mcp-client/oauth-routes.js +2 -1
  69. package/dist/notifications/routes.d.ts +3 -3
  70. package/dist/observability/routes.d.ts +7 -7
  71. package/dist/org/index.d.ts +2 -2
  72. package/dist/org/service-principal-guard.d.ts +64 -0
  73. package/dist/org/service-principal-guard.js +140 -0
  74. package/dist/org/service-principal-policy.d.ts +104 -0
  75. package/dist/org/service-principal-policy.js +272 -0
  76. package/dist/provider-api/actions/custom-provider-registration.d.ts +5 -5
  77. package/dist/provider-api/actions/provider-api.d.ts +13 -13
  78. package/dist/scripts/dev/index.js +2 -8
  79. package/dist/scripts/parse-args.d.ts +7 -0
  80. package/dist/scripts/parse-args.js +76 -0
  81. package/dist/scripts/utils.d.ts +1 -1
  82. package/dist/scripts/utils.js +1 -1
  83. package/dist/secrets/routes.d.ts +11 -11
  84. package/dist/server/action-discovery.js +16 -42
  85. package/dist/server/agent-chat/script-entries.js +3 -8
  86. package/dist/server/agent-chat-plugin.js +8 -42
  87. package/dist/server/agent-engine-default-model-route.d.ts +2 -2
  88. package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
  89. package/dist/server/auth.js +5 -2
  90. package/dist/server/core-routes-plugin.js +25 -6
  91. package/dist/server/google-oauth.js +8 -4
  92. package/dist/server/identity-sso.js +2 -1
  93. package/dist/server/index.d.ts +1 -0
  94. package/dist/server/index.js +1 -0
  95. package/dist/server/poll.d.ts +9 -0
  96. package/dist/server/poll.js +86 -7
  97. package/dist/server/query-echo-safe-redirect.d.ts +10 -0
  98. package/dist/server/query-echo-safe-redirect.js +102 -0
  99. package/dist/server/release-schema.js +4 -0
  100. package/dist/server/workspace-provider-oauth.js +2 -1
  101. package/dist/templates/chat/.agents/skills/actions/references/action-fields.md +1 -0
  102. package/dist/templates/default/.agents/skills/actions/references/action-fields.md +1 -0
  103. package/dist/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +24 -25
  104. package/dist/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +33 -0
  105. package/dist/templates/headless/.agents/skills/actions/references/action-fields.md +1 -0
  106. package/dist/templates/workspace-core/.agents/skills/actions/references/action-fields.md +1 -0
  107. package/dist/triggers/actions/manage-automation.d.ts +7 -7
  108. package/dist/vite/client.js +1 -0
  109. package/docs/content/audit-log.mdx +15 -0
  110. package/docs/content/external-agents-catalog.mdx +19 -0
  111. package/docs/content/http-api.mdx +21 -2
  112. package/docs/content/pr-visual-recap.mdx +3 -1
  113. package/docs/content/template-clips-embed.mdx +1 -1
  114. package/docs/content/turn-into-app.mdx +118 -0
  115. package/package.json +4 -4
  116. package/src/templates/chat/.agents/skills/actions/references/action-fields.md +1 -0
  117. package/src/templates/default/.agents/skills/actions/references/action-fields.md +1 -0
  118. package/src/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +24 -25
  119. package/src/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +33 -0
  120. package/src/templates/headless/.agents/skills/actions/references/action-fields.md +1 -0
  121. package/src/templates/workspace-core/.agents/skills/actions/references/action-fields.md +1 -0
@@ -21,8 +21,10 @@ Four phases, in order. Phase 0 comes before any investigation, not after.
21
21
  2. **Fix** what the evidence actually proves, at the owning boundary.
22
22
  3. **Reply**, under a hard question budget, then recap.
23
23
 
24
- Output is fixes; reply only when informative. Two fixes and three messages
25
- beats thirty replies.
24
+ Output is fixes. Reply in-thread for every workflow reaction added this run;
25
+ continuing work gets a concrete progress reply, and resolved work gets a final
26
+ reply after verification. Follow
27
+ [`slack-reaction-replies.md`](references/slack-reaction-replies.md).
26
28
 
27
29
  ## Slack channels
28
30
 
@@ -46,9 +48,10 @@ work, post one status reply with task link and remaining scope.
46
48
 
47
49
  ## Phase 0: claim what you are taking
48
50
 
49
- For an eligible item you intend to take, add `👀` before investigation and
50
- never remove it. Do not resume items excluded by the Slack ownership gate. Post
51
- **In progress** only when work continues beyond this run.
51
+ Resolve known duplicate clusters before claiming. For an eligible item, add
52
+ `👀` before investigation and never remove it. Do not resume items excluded by
53
+ the Slack ownership gate. Post **In progress** only when work continues beyond
54
+ this run.
52
55
 
53
56
  **Defects are in scope: fix them or ask for the one detail needed to fix them.**
54
57
  Investigate first; ask what they saw or did in plain language. Gather request
@@ -62,13 +65,16 @@ doesn't close a separate defect. Tie each reaction to the scope it marks.
62
65
 
63
66
  ### Reaction gate
64
67
 
65
- `👀` is claim history; `✅` requires **Fixed** after all Phase 2 bars. `🎫`
66
- marks accepted work another owner must do; use it only with an existing ticket
67
- naming that owner and exact action, linked in the ledger and reply. No `🎫` for
68
- fixed scope, routine rollout, optional live checks, subjective/out-of-scope, or
69
- unapproved work. Pair `✅` + `🎫` only for distinct scopes; name the fixed
70
- behavior and ticket action. **Shipped**/**Live verified** alone don't earn
71
- `✅`. Never remove reactions.
68
+ `👀` is claim history; `✅` requires **Fixed** after Phase 2 bars. Check each
69
+ reported symptom separately. For partial fixes, pair `✅` for verified scope
70
+ with `🎫` for any distinct unfinished scope that needs a human to take
71
+ ownership or act, whether or not a ticket exists. Link a verified ticket and
72
+ name its owner/action; without one, keep the handoff explicit in the reply and
73
+ ledger and say that an owner or ticket still needs to be assigned. Do not
74
+ create or promise a ticket without authorization. No `🎫` for fixed scope,
75
+ routine rollout, optional checks, subjective/out-of-scope, or unapproved work.
76
+ **Shipped**/**Live verified** alone don't earn `✅`. Never remove reactions.
77
+ Follow the reply reference before marking an item replied.
72
78
 
73
79
  For every concrete objective defect, attempt to reproduce it before recording
74
80
  an evidence limit or asking the reporter for more information. A screenshot,
@@ -89,7 +95,8 @@ means you found neither a fix nor a useful question; state why in the thread.
89
95
  For eligible items, use one disposition per row; record it in the recap and, if
90
96
  unstated, in the thread or linked work. Do not inspect gated items for status.
91
97
  For clusters, post one owner status with each source permalink and
92
- **Clustered**; reply in a non-owner only for a distinct question or update.
98
+ **Clustered**; do not mark duplicate non-owners separately. Give a distinct
99
+ question or update its own reaction and reply.
93
100
 
94
101
  - **Terminal (keep this workflow's eye):** **Fixed**, **Shipped**, **Live
95
102
  verified**, **Open - no question**, **Resolved elsewhere**, **Skipped**,
@@ -455,16 +462,8 @@ Share only new or useful information.
455
462
 
456
463
  ### After a PR merges
457
464
 
458
- After a Slack-fix PR merges, reply only in threads claimed this run.
459
-
460
- For beta app fixes, check the merge-triggered publisher run before giving the
461
- normal few-hours ETA. This checks release-job status, not beta behavior. If it
462
- succeeds, say what changed and that it'll be on beta in the next few hours.
463
- Keep test environment, verification, publisher results, and rollout details out
464
- of the reply; record evidence in the recap or PR. If the run is missing or
465
- failed, omit the ETA and state what's done, the exact remaining action and owner,
466
- and an existing ticket when available. Apply the Reaction gate before `🎫`.
467
- For packages, state availability without verification details.
465
+ Follow [`slack-reaction-replies.md`](references/slack-reaction-replies.md) for
466
+ post-merge beta ETAs and package replies.
468
467
 
469
468
  For a fixed behavior with a ticketed handoff, state the done behavior and
470
469
  remaining action/owner/ticket separately; don't imply the fixed scope is open.
@@ -561,9 +560,9 @@ Questions asked: N/3 · Dropped at 4 days: N
561
560
  Repeats of a prior Fixed claim: N (each with its earlier thread and failed fix)
562
561
  Upvoted items in scope: N (built: N)
563
562
 
564
- | Tracker/source or workflow run | Reporter/owner | Status | Repro or failed step | Pre/post/recovery | Run/SHA/target/version/artifact/runtime proof | Locales | Handoff (action/owner/ticket) | Replied | Reactions |
563
+ | Tracker/source or workflow run | Reporter/owner | Status | Repro or failed step | Pre/post/recovery | Run/SHA/target/version/artifact/runtime proof | Locales | Handoff (action/owner/ticket) | Reply proof | Reactions |
565
564
  | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
566
- | 18 / [thread](...) or [workflow run](...) | ... | <disposition> | steps; expected/actual or failed job/step | before/after/recovery run | source/tests/build/run/SHA/target/version/URL | updated/N/A/pending | none or action/owner/[ticket](...) | yes/no | 👀 claim; ✅ Fixed; 🎫 ticketed only |
565
+ | 18 / [thread](...) or [workflow run](...) | ... | <disposition> | steps; expected/actual or failed job/step | before/after/recovery run | source/tests/build/run/SHA/target/version/URL | updated/N/A/pending | none or action/owner/[ticket](...) | [reply](...) or blocker | 👀 claim; ✅ Fixed; 🎫 human handoff needed |
567
566
 
568
567
  Sibling sweep: <fingerprint> - N hits, M fixed, K triaged
569
568
  Tracker: <sheet/export and bounded range> - N rows enumerated, N ledgers complete
@@ -0,0 +1,33 @@
1
+ # Slack Reaction Replies
2
+
3
+ For every Slack item this run independently marks with a workflow reaction,
4
+ send one concise reply under the same parent with its disposition. A same-run
5
+ fix gets its final reply after verification; continuing work gets a concrete
6
+ progress reply. An earlier generic “taking a look” does not satisfy this
7
+ update.
8
+
9
+ Preserve cluster handling: identify known duplicates before claiming them;
10
+ represent duplicate non-owner threads by permalink and **Clustered** in one
11
+ owner-thread status. Do not react or reply in a non-owner thread unless a
12
+ distinct question or update needs its own disposition; if it does, react and
13
+ reply there as a separate item. If a duplicate is discovered only after this
14
+ run added `👀`, keep it and post a concise **Clustered** reply with the owner
15
+ permalink; never remove the reaction.
16
+
17
+ Compare each reported symptom with the evidence. State what is fixed and what
18
+ is not. Use `✅` only for verified fixed scope. Use `🎫` for each distinct
19
+ unfinished scope that needs a human to take ownership or act, whether or not a
20
+ ticket exists. Link a verified ticket and name its owner/action; if no ticket
21
+ or owner is assigned, state the exact handoff and that gap in the reply and
22
+ ledger. Do not create or promise a ticket without authorization. Never imply a
23
+ partial fix resolved the whole report.
24
+
25
+ For beta app fixes, check the matching merge-triggered publisher. While it is
26
+ queued or running, say what changed and that it should be on beta within about
27
+ 24 hours. If it succeeds, report beta publication as complete; that does not
28
+ prove independent runtime behavior. If the run is missing or fails, omit the
29
+ ETA and state the next action and owner. Keep verification and rollout details
30
+ in the recap. For packages, state availability without verification details.
31
+
32
+ Read back the reaction and reply as the invoking identity before recording the
33
+ item as replied.
@@ -16,6 +16,7 @@ Rules:
16
16
  - To override the inference on an unusual action (e.g. a `POST` that only reads), pass `readOnly: true` on the action definition.
17
17
  - To let a mutating action run concurrently with other same-turn tool calls, pass `parallelSafe: true`. Only do this when the action is internally concurrency-safe and order-independent (for example, it uses an app-level lock or idempotent upsert semantics). Mutating actions remain serialized by default.
18
18
  - If a successful call hands control to the user — it renders a question, an intake form, or anything else the turn must wait on — pass `endsTurn: true` and make it the first or only call in the turn. It stops subsequent model steps and later calls, but cannot undo calls emitted before it. Without it a completion guard sees a turn that legitimately paused as one that failed to finish. `endsTurn: true` also hides the action from MCP, WebMCP, and A2A callers by default (`isActionExposedToExternalAgents` in core `action.ts`) — an external host has no in-app chat to receive the answer on. Pass `mcpTool: true` alongside it only when an external caller can complete the same operation without that answer.
19
+ - MCP hosts read tool annotations to decide how carefully to treat a call. Every MCP catalog advertises declared `mcpAnnotations` (`readOnlyHint`, `destructiveHint`, `openWorldHint`, optional `idempotentHint`); otherwise `/mcp` derives only `readOnlyHint` and an approval-based `destructiveHint`. Declare them on any external write that removes, overwrites, or replaces user content — recoverable Trash included — and mark idempotency-key-guarded writes `idempotentHint: true`.
19
20
 
20
21
  Agents do NOT need to call `refresh-screen` after a normal action — it's already handled. `refresh-screen` is only needed when the agent mutates data via a path the framework can't see (e.g. writing to an external system the app mirrors) or when the agent wants to pass a `scope` hint for narrower invalidation.
21
22
 
@@ -16,6 +16,7 @@ Rules:
16
16
  - To override the inference on an unusual action (e.g. a `POST` that only reads), pass `readOnly: true` on the action definition.
17
17
  - To let a mutating action run concurrently with other same-turn tool calls, pass `parallelSafe: true`. Only do this when the action is internally concurrency-safe and order-independent (for example, it uses an app-level lock or idempotent upsert semantics). Mutating actions remain serialized by default.
18
18
  - If a successful call hands control to the user — it renders a question, an intake form, or anything else the turn must wait on — pass `endsTurn: true` and make it the first or only call in the turn. It stops subsequent model steps and later calls, but cannot undo calls emitted before it. Without it a completion guard sees a turn that legitimately paused as one that failed to finish. `endsTurn: true` also hides the action from MCP, WebMCP, and A2A callers by default (`isActionExposedToExternalAgents` in core `action.ts`) — an external host has no in-app chat to receive the answer on. Pass `mcpTool: true` alongside it only when an external caller can complete the same operation without that answer.
19
+ - MCP hosts read tool annotations to decide how carefully to treat a call. Every MCP catalog advertises declared `mcpAnnotations` (`readOnlyHint`, `destructiveHint`, `openWorldHint`, optional `idempotentHint`); otherwise `/mcp` derives only `readOnlyHint` and an approval-based `destructiveHint`. Declare them on any external write that removes, overwrites, or replaces user content — recoverable Trash included — and mark idempotency-key-guarded writes `idempotentHint: true`.
19
20
 
20
21
  Agents do NOT need to call `refresh-screen` after a normal action — it's already handled. `refresh-screen` is only needed when the agent mutates data via a path the framework can't see (e.g. writing to an external system the app mirrors) or when the agent wants to pass a `scope` hint for narrower invalidation.
21
22
 
@@ -13,6 +13,8 @@ declare const _default: import("../../action.js").ActionDefinition<{
13
13
  executionEngine?: string;
14
14
  executionCwd?: string;
15
15
  }, {
16
+ deleted?: undefined;
17
+ enabled?: undefined;
16
18
  created: boolean;
17
19
  name: string;
18
20
  scope: "organization" | "personal";
@@ -23,33 +25,31 @@ declare const _default: import("../../action.js").ActionDefinition<{
23
25
  reasoningEffort: "auto" | "high" | "low" | "max" | "medium" | "minimal" | "none" | "xhigh";
24
26
  webhookPath: string;
25
27
  nextRun: string;
26
- deleted?: undefined;
27
- enabled?: undefined;
28
28
  executionHostId?: undefined;
29
29
  executionEngine?: undefined;
30
30
  executionCwd?: undefined;
31
31
  } | {
32
- scope?: undefined;
32
+ enabled?: undefined;
33
+ nextRun?: undefined;
33
34
  created?: undefined;
35
+ scope?: undefined;
34
36
  triggerType?: undefined;
35
37
  event?: undefined;
36
38
  deleted: boolean;
37
39
  name: string;
38
- enabled?: undefined;
39
40
  schedule?: undefined;
40
41
  timezone?: undefined;
41
42
  reasoningEffort?: undefined;
42
43
  executionHostId?: undefined;
43
44
  executionEngine?: undefined;
44
45
  executionCwd?: undefined;
45
- nextRun?: undefined;
46
46
  webhookPath?: undefined;
47
47
  } | {
48
- scope?: undefined;
48
+ deleted?: undefined;
49
49
  created?: undefined;
50
+ scope?: undefined;
50
51
  triggerType?: undefined;
51
52
  event?: undefined;
52
- deleted?: undefined;
53
53
  name: string;
54
54
  enabled: boolean;
55
55
  schedule: string;
@@ -1159,6 +1159,7 @@ function getAgentKitOptimizeDeps(cwd) {
1159
1159
  "@agent-native/toolkit/app/agentkit/react/components",
1160
1160
  "@agent-native/toolkit/app/agentkit/react/context",
1161
1161
  "@agent-native/toolkit/app/agentkit/react/root",
1162
+ "@agent-native/toolkit/app/chat",
1162
1163
  "@agent-native/toolkit/app/chat/agentkit-chat/index",
1163
1164
  ]
1164
1165
  : []),
@@ -169,6 +169,21 @@ const { data } = useActionQuery("list-audit-events", {
169
169
 
170
170
  The agent can call the same action — ask it "what did you change on this recording?" and it answers from the trail.
171
171
 
172
+ ## Export to a SIEM (OCSF) {#ocsf}
173
+
174
+ **`export-audit-ocsf`** returns the organization's trail as [OCSF](https://schema.ocsf.io) 1.9.0 API Activity events (`class_uid` 6003), oldest first, for a SIEM to pull on a schedule. Owners and admins only, and it reads the same `org` and `admins` events as the organization audit log, never anyone's private rows.
175
+
176
+ | Input | Meaning |
177
+ | -------- | ------------------------------------------------------------ |
178
+ | `since` | Only events at or after this ISO 8601 timestamp or epoch ms. |
179
+ | `until` | Only events before this ISO 8601 timestamp or epoch ms. |
180
+ | `cursor` | The `nextCursor` from the previous page. |
181
+ | `limit` | Events per page, up to 500 (default 100). |
182
+
183
+ It returns `{ events, hasMore, nextCursor }`. Store every returned `nextCursor` and send it back on the next pull, including when `events` is empty; empty pages advance the ready watermark. The cursor replays a five-minute overlap to catch late commits, so deduplicate across pulls by `metadata.uid`. The newest five seconds are held back so a late-written row is never skipped.
184
+
185
+ Each event maps `status` to `status_id` (`success` is Success; `error` and `denied` are Failure, with `denied` also `action_id` Denied and a higher `severity_id`), the actor to `actor.user` (a service principal is type Service), the action to `api.operation`, and the target to `resources`. Run, task, parent-task, thread, turn, and app ids have no OCSF field, so they sit under `unmapped`. Call arguments are never exported.
186
+
172
187
  ## Privacy & retention {#privacy}
173
188
 
174
189
  - **Redaction** — before any inputs are stored, credential-shaped keys and values (tokens, secrets, passwords, bearer strings) are stripped and oversized payloads truncated. The audit log never becomes a secondary store of secrets. Keep `summary` text free of sensitive data too.
@@ -236,6 +236,25 @@ from the request origin, so deployments should not hard-code a production
236
236
 
237
237
  Once an agent is connected, it sees the [compact catalog](#catalog-tiers) by default: template-declared app actions plus the builtin cross-app verbs (`list_apps`, `open_app`, `ask_app`, and the app-only embed helper). Use `ask_app` to route a natural-language task through an app agent (the same cross-app entry point [A2A](/docs/a2a-protocol) uses).
238
238
 
239
+ ### Tool annotations {#tool-annotations}
240
+
241
+ Every catalog advertises an action's declared `mcpAnnotations`, including the
242
+ optional `idempotentHint`. Declare them wherever the derived hints would
243
+ misstate the effect: moving an item to recoverable Trash still removes it from
244
+ view, so it is `destructiveHint: true`, and a call guarded by an idempotency
245
+ key is `idempotentHint: true`. Without a declaration, `/mcp` derives
246
+ `readOnlyHint` from `readOnly` and `destructiveHint` from `needsApproval` or
247
+ `publicAgent.isConsequential`.
248
+
249
+ ```ts
250
+ mcpAnnotations: {
251
+ readOnlyHint: false,
252
+ destructiveHint: true,
253
+ idempotentHint: true,
254
+ openWorldHint: false,
255
+ },
256
+ ```
257
+
239
258
  ### MCP Apps compatibility {#mcp-apps-compatibility}
240
259
 
241
260
  Agent-Native apps also speak the official MCP Apps extension. When any action
@@ -60,7 +60,7 @@ The command still authenticates you in the browser first — minting requires or
60
60
  | Who can mint | Any signed-in user | Org owner/admin only |
61
61
  | Best for | Your own scripts, local dev | CI, bots, scheduled integrations |
62
62
 
63
- Both default to a 365-day lifetime and accept `--ttl-days` between 1 and 365. If a machine cannot open a browser, the app's connect page at `https://<app>/mcp/connect` is the fallback: sign in there, copy the token, and pass it with `connect <url> --token <token>` or paste it directly into your secret store.
63
+ Personal tokens default to 365 days in the CLI device flow; to choose a lifetime from 1 to 365 days, use the app's Connect page at `https://<app>/mcp/connect`. Org service tokens also default to 365 days and accept `--ttl-days` values between 1 and 3650. If a machine cannot open a browser, the app's Connect page is the fallback: sign in there, copy the token, and pass it with `connect <url> --token <token>` or paste it directly into your secret store.
64
64
 
65
65
  For a self-hosted, single-tenant app where per-person identity does not matter, the static `ACCESS_TOKEN` / `ACCESS_TOKENS` environment variables are the simplest option — see [Authentication — Static MCP Bearer Tokens](/docs/authentication#access-tokens).
66
66
 
@@ -223,7 +223,7 @@ The action ran exactly as it would have from chat or the UI — same validation,
223
223
 
224
224
  Tokens are JWTs signed by the app and bound to that app's audience. A token minted for one app cannot call another.
225
225
 
226
- - **Lifetime** — 365 days by default, `--ttl-days` between 1 and 365. Shorter is better for anything that runs unattended.
226
+ - **Lifetime** — 365 days by default, `--ttl-days` between 1 and 3650. Shorter is better for anything that runs unattended.
227
227
  - **Rotation** — mint the new token, deploy it to the caller, confirm traffic is flowing, then revoke the old one. There is no in-place rotation; the two tokens are independent and can overlap.
228
228
  - **Revocation** — every token carries a `jti`, and revocation is a `jti` check on every request, so a revoked token stops working immediately. Use the `list-org-service-tokens` action to see names, who minted them, and last-used timestamps, then `revoke-org-service-token` with the id. Both are owner/admin-gated for writes; any org member can list.
229
229
  - **Recovery** — the token value is never stored, only its `jti`. If you lose it, mint a new one and revoke the old.
@@ -242,6 +242,25 @@ Because `list-org-service-tokens` reports `lastUsedAt`, it doubles as a cleanup
242
242
 
243
243
  </Callout>
244
244
 
245
+ ## Service principals {#service-principals}
246
+
247
+ Each org service token is a **service principal**, and a governance record says who answers for it and what it may do. Owners and admins set it when minting (`create-org-service-token` takes optional `ownerEmail`, `team`, `riskTier`, `purpose`, and `allowedActions`) and change it later with `set-service-principal-policy`. `list-org-service-tokens` returns each principal's record in `principals`.
248
+
249
+ | Field | Meaning |
250
+ | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
251
+ | `ownerEmail` | The accountable human. Defaults to the admin who minted the token. |
252
+ | `team`, `purpose` | Who to page and what the principal is for. |
253
+ | `riskTier` | `low`, `medium`, or `high`. |
254
+ | `lifecycle` | `active`, `suspended`, or `retired`. |
255
+ | `allowedActions` | `null` is unrestricted. A list is a deny-by-default grant of exact action names or a trailing `*` prefix pattern; an empty list grants nothing. |
256
+
257
+ - **Grant** — `tools/list` shows only the granted actions, and any call outside the grant is refused, whether it arrives over MCP, an HTTP action route, or an agent run the principal started with `ask-agent` or `ask_app`. Agent built-ins such as `tool-search` and `view-screen` are covered too, so name them in the grant if the principal needs them. The grant narrows what the `member` role already allows; it never adds access.
258
+ - **Suspend and resume** — `set-service-principal-lifecycle` with `suspended` refuses the principal on every request, refuses new agent runs for it, and aborts its in-flight runs. `active` resumes it. The MCP connect page (`/mcp/connect`) has the same suspend and resume controls. `retired` also revokes its tokens and cannot be undone.
259
+ - **Ungoverned** — a service token minted before governance has no record. It stays active and unrestricted until an admin sets a policy.
260
+ - **Fails closed** — if the policy cannot be read, the request is refused with a `503`, never admitted.
261
+
262
+ These actions are owner/admin-only and are not callable by the agent, so neither an agent nor a service token can loosen its own grant.
263
+
245
264
  ## When it 403s {#troubleshooting}
246
265
 
247
266
  A `401` means the token was not accepted at all. A `403` means the token _was_ accepted and the request then failed an access check — a different problem with a different fix.
@@ -249,7 +249,7 @@ survives any individual leaving, the recaps it publishes are org-visible, and
249
249
  any org owner or admin can list or revoke it. Mint one (org owner/admin only):
250
250
 
251
251
  ```bash
252
- npx @agent-native/core@latest connect https://plan.agent-native.com --service-token pr-recap
252
+ npx @agent-native/core@latest connect https://plan.agent-native.com --service-token pr-recap --ttl-days 3650
253
253
  ```
254
254
 
255
255
  The command authenticates you in the browser, then prints the service token
@@ -257,6 +257,8 @@ exactly once — store it as the `PLAN_RECAP_TOKEN` secret. Manage it later with
257
257
  the `list-org-service-tokens` and `revoke-org-service-token` actions on the
258
258
  Plans app.
259
259
 
260
+ That gives CI a 10-year lifetime while keeping the token revocable at any time.
261
+
260
262
  **Solo: a personal token still works.** Mint it with `npx @agent-native/core@latest connect`
261
263
  against your Plans app. For the hosted app, this also writes a local
262
264
  publish-token file that `npx @agent-native/recap-cli@latest recap setup` can read:
@@ -43,7 +43,7 @@ npx @agent-native/core@latest connect https://clips.agent-native.com \
43
43
 
44
44
  Copy the printed token directly into your product's server-side secret manager. The token is organization-owned, so it continues working if the person who minted it leaves the organization or revokes their personal tokens. It is never written to a local client configuration. Use a different service name for each integration so one caller can be revoked without breaking another.
45
45
 
46
- `--ttl-days` accepts 1 to 365 days and defaults to 365. The token acts as an organization member. It cannot create or revoke other service tokens or change organization settings.
46
+ `--ttl-days` accepts 1 to 3650 days and defaults to 365. The token acts as an organization member. It cannot create or revoke other service tokens or change organization settings.
47
47
 
48
48
  ## Request an intake URL
49
49
 
@@ -0,0 +1,118 @@
1
+ ---
2
+ title: "Turn Into App"
3
+ description: "The /turn-into-app skill turns a thread, skill, spreadsheet, or Claude or ChatGPT project into a runnable Agent-Native app that opens on a populated, domain-shaped surface."
4
+ ---
5
+
6
+ # Turn Into App
7
+
8
+ `/turn-into-app` is a skill that turns a workflow you have already proven into an Agent-Native app. The source can be a thread, a skill, a spreadsheet, or a Claude or ChatGPT project. The app opens on your world, populated with sample data shaped like the source, with the in-app agent working behind controls on the objects it acts on. A form under a step bar is the result the skill is built to avoid.
9
+
10
+ ## Install {#install}
11
+
12
+ ```bash
13
+ npx @agent-native/skills@latest add --skill turn-into-app
14
+ ```
15
+
16
+ The same skill ships with the core CLI:
17
+
18
+ ```bash
19
+ npx @agent-native/core@latest skills add turn-into-app
20
+ ```
21
+
22
+ Inside a project that already has the Agent-Native CLI, `agent-native skills add turn-into-app` does the same thing. See the [Skills Guide](/docs/skills-guide) for how skills are installed and loaded.
23
+
24
+ ## Invoke it {#invoke}
25
+
26
+ | Command | Source |
27
+ | ---------------------------------- | ----------------------------------------------------------- |
28
+ | `/turn-into-app` | The visible project context, otherwise the current thread |
29
+ | `/turn-into-app /some-skill` | The named skill, packaged immediately, even at thread start |
30
+ | `/turn-into-app ./forecast.xlsx` | A spreadsheet, turned into a workbench |
31
+ | `/turn-into-app ./project-export/` | A Claude or ChatGPT project export |
32
+
33
+ Without an argument the skill builds the latest successful, repeatable job and names it. A worked example, such as one account or one deal, is evidence for the job, not its schema. If no job can be identified, it says what is missing instead of asking a generic "what app do you want?" question.
34
+
35
+ ## Where it runs {#hosts}
36
+
37
+ | Host | What happens |
38
+ | ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
39
+ | Claude Code, Codex, Cursor, other local coding agents | Builds, runs, and reviews the app in your checkout. |
40
+ | Claude or ChatGPT on the web | Writes a bounded brief and hands it to Builder through the Dispatch connector. It does not run anything itself. |
41
+
42
+ A host counts as local when it has a real working directory, a terminal, and a workspace. Having a Builder or Dispatch connector does not make a host a browser host.
43
+
44
+ On a browser host the skill requires the Agent-Native Dispatch MCP connector (`https://dispatch.agent-native.com/mcp`, OAuth) and calls its `start-workspace-app-creation` action. The host cannot run or inspect the app, so the report stays pending or unverified until the branch Dispatch returns is built. The skill reports what Dispatch actually returned and never invents a branch URL. For a ChatGPT Project, or any host that only exposes MCP, put the skill instructions in the Project instructions or knowledge files.
45
+
46
+ ## Sources {#sources}
47
+
48
+ - **Threads.** The last concrete, repeatable job is the product.
49
+ - **Skills.** The skill's steps, human decision points, and read-only rules become the app. If the skill keeps plan files, those files stay the source of truth through Local File Mode and the app is local-only.
50
+ - **Spreadsheets.** Every worksheet is inventoried first. Structure decides which cells are inputs and outputs; color is only a weak hint. The workbook is never copied into a prompt, SQL, or application state. A Google Sheets link is only a source when it is readable through an authenticated Sheets or Drive connection; otherwise the skill asks for an export.
51
+ - **Claude and ChatGPT projects.** Visible instructions, knowledge files, and supplied past runs are the source. The connector does not read hidden project chats, private URLs, or credentials, and there is no shared-link importer. Attach an export when the context is not visible.
52
+
53
+ ## What you get {#what-you-get}
54
+
55
+ - **A shape chosen from the source.** The skill picks one of 13 archetypes, such as a triage queue, pipeline board, live workbench, schedule grid, review diff, or monitor. The archetype is the object the user looks at, not the procedure they follow, so source steps become states on objects (Draft, Checked, Approved) rather than a stepper or wizard.
56
+ - **A named visual direction.** One of six directions (`ledger`, `control-room`, `paper-desk`, `studio-canvas`, `tidepool`, `signal-board`) with light and dark tokens, picked from the archetype and the source's domain. An existing brand or your explicit direction wins.
57
+ - **A populated first screen.** `/` lands on a named domain route with synthetic sample data, labeled as sample, shaped like the source. An empty state, a hero, the procedure's first step, or a form is a defect unless the source itself is form-shaped.
58
+ - **Agent moments on the objects.** Each AI-labeled control hands a bounded prompt to the agent sidebar. The object shows a working state right away, and the result lands back on it with Accept, Edit, and Retry. Follow-ups stay in the same thread.
59
+ - **A real Agent-Native app.** Deterministic work is `defineAction` files that both the UI and the agent call. The app uses the standard Use Builder.io or Custom keys setup, writes the current view and selection to application state, and has `AGENTS.md` teaching the in-app agent about the new app.
60
+
61
+ The skill saves its source brief as `docs/brief.md` and appends the surface design to `DESIGN.md`.
62
+
63
+ ## How a run works {#how-it-works}
64
+
65
+ 1. **Brief.** It reads the whole source and writes a short brief: the job, at most three agent moments, invariants, data hazards, source of truth, and assumptions.
66
+ 2. **Design.** It decides the archetype, direction, shell, first viewport and sample data, and agent moments, and posts the brief once as a checkpoint. It continues without waiting.
67
+ 3. **Scaffold.** It creates the real app with the chat template and never overwrites an existing app.
68
+
69
+ ```bash
70
+ npx --yes @agent-native/core@latest create <slug> --standalone --template chat
71
+ ```
72
+
73
+ Inside an existing Agent-Native workspace it runs `pnpm exec agent-native add-app <slug> --template chat` from the workspace root instead.
74
+
75
+ 4. **Build.** Actions, the domain surface, sample data, agent moments, and agent instructions are built together.
76
+ 5. **Review.** It runs the app and reviews screenshots. See below.
77
+ 6. **Verify.** It runs `pnpm typecheck`, `pnpm agent-native:doctor`, and `pnpm build`, then writes the final report.
78
+
79
+ The skill aims for about 45 minutes. If 35 minutes have passed when the review starts, it runs one pass and lists the open criteria.
80
+
81
+ ## The screenshot review loop {#review-loop}
82
+
83
+ Before handoff the skill starts the dev server and screenshots `/` at desktop and phone sizes in light and dark, plus the main agent control after a click. It scores the shots against a 12-row rubric covering first-viewport impact, domain fit, hierarchy, chrome discipline, sample data, typography, color, polish, agent and UI together, 390px layout, dark mode, and accessibility.
84
+
85
+ Some findings fail the review whatever the average score: a stepper or wizard shell, `/` not opening on the domain route, an empty or form-only first viewport, scaffold default tokens, clipped text, raw markdown, sideways scroll at 390px, or a console error. It fixes every finding in one batch, resets whatever the click changed, and shoots again. The default is two passes, with a third only if the second still misses the bar and time allows. The bar is a mean of 4.0 or higher with no criterion below 3 and no automatic fail.
86
+
87
+ Screenshots are saved in the app's `.tmp/ui-review/out/`, and the final report lists their paths and scores. A result landing in the UI can only be exercised with a connected AI provider; without one, the report says the result was not exercised.
88
+
89
+ ## Run and deploy {#run-deploy}
90
+
91
+ For local review the skill sets `AUTH_DISABLED=1` in the app's ignored `.env`, because a fresh screenshot browser is not signed in. This is for loopback development only. It is removed before the final `pnpm build`, and it must never be committed or deployed. Without it, opening the app shows a "Continue as local dev" card.
92
+
93
+ To run the app yourself:
94
+
95
+ ```bash
96
+ pnpm dev
97
+ ```
98
+
99
+ To use your own model provider locally, put a key such as `ANTHROPIC_API_KEY` in the same `.env`. Otherwise the agent sidebar shows the shared Use Builder.io or Custom keys setup.
100
+
101
+ The skill deploys only when you ask or the provider is already configured. A healthy local build still prints a "production configuration errors" block with exit code 0. That is the deploy checklist: a stable `BETTER_AUTH_SECRET` and a persistent `DATABASE_URL`, plus `A2A_SECRET` for an app inside a workspace. The skill reports these as the pending step and does not create placeholder values. See [Deploy an app](/docs/deploy-an-app) and [Deploy a workspace](/docs/workspace-deployment).
102
+
103
+ The final report labels evidence separately: locally running, locally verified, build-ready, deployed, and live-verified are different states, and the report never lets one stand in for another.
104
+
105
+ ## Known limits {#limits}
106
+
107
+ - **Editing `.env` while the dev server runs.** An in-place reload after an `.env` change loops on the local database lock and serves 500s. Stop the server, edit `.env`, and start it again.
108
+ - **Ambiguous spreadsheets.** If the candidate workflows or the input and output mapping stay materially ambiguous after a bounded review, the skill asks one compact question with its recommended interpretation. In a headless run it prints the question, records the assumption in the brief, and keeps building.
109
+ - **Spreadsheets on a browser host.** A workbook cannot be attached; the host passes a bounded CSV rendering as text, or a URL that is intentionally public, and the handoff says what was left out. A link to a private sheet is not a source; the skill asks for an authenticated Sheets or Drive connection or an export, and never uses a public export URL to get around access controls.
110
+ - **Cell colors.** A text preview cannot prove colors, so on a local host the skill reads both formulas and cached values, and treats color as a weak hint.
111
+ - **Local File Mode apps.** An app whose source of truth is the host's files is local-only and is not deployed.
112
+ - **Browser-host handoffs.** The result is unverified until Dispatch returns a built branch. The skill cannot run or screenshot the app there.
113
+
114
+ ## What's next {#whats-next}
115
+
116
+ - [Skills Guide](/docs/skills-guide) for how skills are installed and loaded
117
+ - [Dispatch](/docs/dispatch) for the connector used from Claude and ChatGPT on the web
118
+ - [Deploy an app](/docs/deploy-an-app) for taking the generated app to production
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.203.2-nightly-20261007213109",
3
+ "version": "0.204.0",
4
4
  "description": "The agentic application framework for building autonomous agents with intuitive UIs",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -682,8 +682,8 @@
682
682
  "yaml": "^2.9.0",
683
683
  "yjs": "^13.6.33",
684
684
  "zod": "^4.3.6",
685
- "@agent-native/agentkit": "^0.203.2-nightly-20261007213109",
686
- "@agent-native/recap-cli": "^0.5.65"
685
+ "@agent-native/agentkit": "^0.204.0",
686
+ "@agent-native/recap-cli": "^0.5.66"
687
687
  },
688
688
  "devDependencies": {
689
689
  "@ai-sdk/anthropic": "^3.0.71",
@@ -753,7 +753,7 @@
753
753
  "xlsx": "^0.18.5"
754
754
  },
755
755
  "peerDependencies": {
756
- "@agent-native/core-corpus": "0.203.2-nightly-20261007213109",
756
+ "@agent-native/core-corpus": ">=0.196.0",
757
757
  "@ai-sdk/anthropic": ">=3",
758
758
  "@ai-sdk/cohere": ">=3",
759
759
  "@ai-sdk/google": ">=3",
@@ -16,6 +16,7 @@ Rules:
16
16
  - To override the inference on an unusual action (e.g. a `POST` that only reads), pass `readOnly: true` on the action definition.
17
17
  - To let a mutating action run concurrently with other same-turn tool calls, pass `parallelSafe: true`. Only do this when the action is internally concurrency-safe and order-independent (for example, it uses an app-level lock or idempotent upsert semantics). Mutating actions remain serialized by default.
18
18
  - If a successful call hands control to the user — it renders a question, an intake form, or anything else the turn must wait on — pass `endsTurn: true` and make it the first or only call in the turn. It stops subsequent model steps and later calls, but cannot undo calls emitted before it. Without it a completion guard sees a turn that legitimately paused as one that failed to finish. `endsTurn: true` also hides the action from MCP, WebMCP, and A2A callers by default (`isActionExposedToExternalAgents` in core `action.ts`) — an external host has no in-app chat to receive the answer on. Pass `mcpTool: true` alongside it only when an external caller can complete the same operation without that answer.
19
+ - MCP hosts read tool annotations to decide how carefully to treat a call. Every MCP catalog advertises declared `mcpAnnotations` (`readOnlyHint`, `destructiveHint`, `openWorldHint`, optional `idempotentHint`); otherwise `/mcp` derives only `readOnlyHint` and an approval-based `destructiveHint`. Declare them on any external write that removes, overwrites, or replaces user content — recoverable Trash included — and mark idempotency-key-guarded writes `idempotentHint: true`.
19
20
 
20
21
  Agents do NOT need to call `refresh-screen` after a normal action — it's already handled. `refresh-screen` is only needed when the agent mutates data via a path the framework can't see (e.g. writing to an external system the app mirrors) or when the agent wants to pass a `scope` hint for narrower invalidation.
21
22
 
@@ -16,6 +16,7 @@ Rules:
16
16
  - To override the inference on an unusual action (e.g. a `POST` that only reads), pass `readOnly: true` on the action definition.
17
17
  - To let a mutating action run concurrently with other same-turn tool calls, pass `parallelSafe: true`. Only do this when the action is internally concurrency-safe and order-independent (for example, it uses an app-level lock or idempotent upsert semantics). Mutating actions remain serialized by default.
18
18
  - If a successful call hands control to the user — it renders a question, an intake form, or anything else the turn must wait on — pass `endsTurn: true` and make it the first or only call in the turn. It stops subsequent model steps and later calls, but cannot undo calls emitted before it. Without it a completion guard sees a turn that legitimately paused as one that failed to finish. `endsTurn: true` also hides the action from MCP, WebMCP, and A2A callers by default (`isActionExposedToExternalAgents` in core `action.ts`) — an external host has no in-app chat to receive the answer on. Pass `mcpTool: true` alongside it only when an external caller can complete the same operation without that answer.
19
+ - MCP hosts read tool annotations to decide how carefully to treat a call. Every MCP catalog advertises declared `mcpAnnotations` (`readOnlyHint`, `destructiveHint`, `openWorldHint`, optional `idempotentHint`); otherwise `/mcp` derives only `readOnlyHint` and an approval-based `destructiveHint`. Declare them on any external write that removes, overwrites, or replaces user content — recoverable Trash included — and mark idempotency-key-guarded writes `idempotentHint: true`.
19
20
 
20
21
  Agents do NOT need to call `refresh-screen` after a normal action — it's already handled. `refresh-screen` is only needed when the agent mutates data via a path the framework can't see (e.g. writing to an external system the app mirrors) or when the agent wants to pass a `scope` hint for narrower invalidation.
21
22
 
@@ -21,8 +21,10 @@ Four phases, in order. Phase 0 comes before any investigation, not after.
21
21
  2. **Fix** what the evidence actually proves, at the owning boundary.
22
22
  3. **Reply**, under a hard question budget, then recap.
23
23
 
24
- Output is fixes; reply only when informative. Two fixes and three messages
25
- beats thirty replies.
24
+ Output is fixes. Reply in-thread for every workflow reaction added this run;
25
+ continuing work gets a concrete progress reply, and resolved work gets a final
26
+ reply after verification. Follow
27
+ [`slack-reaction-replies.md`](references/slack-reaction-replies.md).
26
28
 
27
29
  ## Slack channels
28
30
 
@@ -46,9 +48,10 @@ work, post one status reply with task link and remaining scope.
46
48
 
47
49
  ## Phase 0: claim what you are taking
48
50
 
49
- For an eligible item you intend to take, add `👀` before investigation and
50
- never remove it. Do not resume items excluded by the Slack ownership gate. Post
51
- **In progress** only when work continues beyond this run.
51
+ Resolve known duplicate clusters before claiming. For an eligible item, add
52
+ `👀` before investigation and never remove it. Do not resume items excluded by
53
+ the Slack ownership gate. Post **In progress** only when work continues beyond
54
+ this run.
52
55
 
53
56
  **Defects are in scope: fix them or ask for the one detail needed to fix them.**
54
57
  Investigate first; ask what they saw or did in plain language. Gather request
@@ -62,13 +65,16 @@ doesn't close a separate defect. Tie each reaction to the scope it marks.
62
65
 
63
66
  ### Reaction gate
64
67
 
65
- `👀` is claim history; `✅` requires **Fixed** after all Phase 2 bars. `🎫`
66
- marks accepted work another owner must do; use it only with an existing ticket
67
- naming that owner and exact action, linked in the ledger and reply. No `🎫` for
68
- fixed scope, routine rollout, optional live checks, subjective/out-of-scope, or
69
- unapproved work. Pair `✅` + `🎫` only for distinct scopes; name the fixed
70
- behavior and ticket action. **Shipped**/**Live verified** alone don't earn
71
- `✅`. Never remove reactions.
68
+ `👀` is claim history; `✅` requires **Fixed** after Phase 2 bars. Check each
69
+ reported symptom separately. For partial fixes, pair `✅` for verified scope
70
+ with `🎫` for any distinct unfinished scope that needs a human to take
71
+ ownership or act, whether or not a ticket exists. Link a verified ticket and
72
+ name its owner/action; without one, keep the handoff explicit in the reply and
73
+ ledger and say that an owner or ticket still needs to be assigned. Do not
74
+ create or promise a ticket without authorization. No `🎫` for fixed scope,
75
+ routine rollout, optional checks, subjective/out-of-scope, or unapproved work.
76
+ **Shipped**/**Live verified** alone don't earn `✅`. Never remove reactions.
77
+ Follow the reply reference before marking an item replied.
72
78
 
73
79
  For every concrete objective defect, attempt to reproduce it before recording
74
80
  an evidence limit or asking the reporter for more information. A screenshot,
@@ -89,7 +95,8 @@ means you found neither a fix nor a useful question; state why in the thread.
89
95
  For eligible items, use one disposition per row; record it in the recap and, if
90
96
  unstated, in the thread or linked work. Do not inspect gated items for status.
91
97
  For clusters, post one owner status with each source permalink and
92
- **Clustered**; reply in a non-owner only for a distinct question or update.
98
+ **Clustered**; do not mark duplicate non-owners separately. Give a distinct
99
+ question or update its own reaction and reply.
93
100
 
94
101
  - **Terminal (keep this workflow's eye):** **Fixed**, **Shipped**, **Live
95
102
  verified**, **Open - no question**, **Resolved elsewhere**, **Skipped**,
@@ -455,16 +462,8 @@ Share only new or useful information.
455
462
 
456
463
  ### After a PR merges
457
464
 
458
- After a Slack-fix PR merges, reply only in threads claimed this run.
459
-
460
- For beta app fixes, check the merge-triggered publisher run before giving the
461
- normal few-hours ETA. This checks release-job status, not beta behavior. If it
462
- succeeds, say what changed and that it'll be on beta in the next few hours.
463
- Keep test environment, verification, publisher results, and rollout details out
464
- of the reply; record evidence in the recap or PR. If the run is missing or
465
- failed, omit the ETA and state what's done, the exact remaining action and owner,
466
- and an existing ticket when available. Apply the Reaction gate before `🎫`.
467
- For packages, state availability without verification details.
465
+ Follow [`slack-reaction-replies.md`](references/slack-reaction-replies.md) for
466
+ post-merge beta ETAs and package replies.
468
467
 
469
468
  For a fixed behavior with a ticketed handoff, state the done behavior and
470
469
  remaining action/owner/ticket separately; don't imply the fixed scope is open.
@@ -561,9 +560,9 @@ Questions asked: N/3 · Dropped at 4 days: N
561
560
  Repeats of a prior Fixed claim: N (each with its earlier thread and failed fix)
562
561
  Upvoted items in scope: N (built: N)
563
562
 
564
- | Tracker/source or workflow run | Reporter/owner | Status | Repro or failed step | Pre/post/recovery | Run/SHA/target/version/artifact/runtime proof | Locales | Handoff (action/owner/ticket) | Replied | Reactions |
563
+ | Tracker/source or workflow run | Reporter/owner | Status | Repro or failed step | Pre/post/recovery | Run/SHA/target/version/artifact/runtime proof | Locales | Handoff (action/owner/ticket) | Reply proof | Reactions |
565
564
  | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
566
- | 18 / [thread](...) or [workflow run](...) | ... | <disposition> | steps; expected/actual or failed job/step | before/after/recovery run | source/tests/build/run/SHA/target/version/URL | updated/N/A/pending | none or action/owner/[ticket](...) | yes/no | 👀 claim; ✅ Fixed; 🎫 ticketed only |
565
+ | 18 / [thread](...) or [workflow run](...) | ... | <disposition> | steps; expected/actual or failed job/step | before/after/recovery run | source/tests/build/run/SHA/target/version/URL | updated/N/A/pending | none or action/owner/[ticket](...) | [reply](...) or blocker | 👀 claim; ✅ Fixed; 🎫 human handoff needed |
567
566
 
568
567
  Sibling sweep: <fingerprint> - N hits, M fixed, K triaged
569
568
  Tracker: <sheet/export and bounded range> - N rows enumerated, N ledgers complete