@bastani/atomic 0.9.14-alpha.4 → 0.9.14-alpha.6
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/CHANGELOG.md +13 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +6 -0
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/src/extension/schemas.ts +5 -0
- package/dist/builtin/subagents/src/runs/shared/long-running-guard.ts +3 -1
- package/dist/builtin/subagents/src/runs/shared/progress-trend.ts +69 -0
- package/dist/builtin/subagents/src/runs/shared/subagent-control.ts +12 -1
- package/dist/builtin/subagents/src/shared/types-results.ts +2 -0
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +47 -1
- package/dist/builtin/workflows/README.md +6 -5
- package/dist/builtin/workflows/builtin/adversarial-verification-prompts.ts +13 -5
- package/dist/builtin/workflows/builtin/adversarial-verification-runner.ts +376 -89
- package/dist/builtin/workflows/builtin/adversarial-verification.d.ts +30 -6
- package/dist/builtin/workflows/builtin/adversarial-verification.ts +14 -9
- package/dist/builtin/workflows/builtin/generate-and-filter-prompts.ts +26 -3
- package/dist/builtin/workflows/builtin/generate-and-filter-runner.ts +18 -14
- package/dist/builtin/workflows/builtin/goal-artifacts.ts +9 -8
- package/dist/builtin/workflows/builtin/goal-convergence.ts +87 -0
- package/dist/builtin/workflows/builtin/goal-ledger.ts +4 -0
- package/dist/builtin/workflows/builtin/goal-prompts.ts +2 -0
- package/dist/builtin/workflows/builtin/goal-reducer.ts +6 -1
- package/dist/builtin/workflows/builtin/goal-reverify.ts +305 -0
- package/dist/builtin/workflows/builtin/goal-runner.ts +75 -10
- package/dist/builtin/workflows/builtin/goal-schemas.ts +7 -0
- package/dist/builtin/workflows/builtin/goal-types.ts +6 -0
- package/dist/builtin/workflows/builtin/loop-until-done-runner.ts +94 -6
- package/dist/builtin/workflows/builtin/loop-until-done.d.ts +8 -0
- package/dist/builtin/workflows/builtin/loop-until-done.ts +15 -0
- package/dist/builtin/workflows/builtin/progress-scoring.ts +230 -0
- package/dist/builtin/workflows/builtin/ralph-core.ts +11 -0
- package/dist/builtin/workflows/builtin/ralph-review-gate.ts +1 -0
- package/dist/builtin/workflows/builtin/ralph-reviewer-prompt.ts +2 -0
- package/dist/builtin/workflows/builtin/ralph-runner.ts +60 -10
- package/dist/builtin/workflows/builtin/selection-math.ts +156 -0
- package/dist/builtin/workflows/builtin/shared-prompts.ts +5 -0
- package/dist/builtin/workflows/builtin/tournament-prompts.ts +57 -75
- package/dist/builtin/workflows/builtin/tournament-runner.ts +384 -178
- package/dist/builtin/workflows/builtin/tournament.d.ts +46 -17
- package/dist/builtin/workflows/builtin/tournament.ts +66 -32
- package/dist/builtin/workflows/builtin/verification-criteria.ts +330 -0
- package/dist/builtin/workflows/builtin/verification-prompts.ts +206 -0
- package/dist/builtin/workflows/builtin/verification-usage.ts +44 -0
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/skills/create-spec/SKILL.md +90 -30
- package/dist/builtin/workflows/skills/show-me/LICENSE.txt +21 -0
- package/dist/builtin/workflows/skills/show-me/SKILL.md +143 -0
- package/dist/builtin/workflows/src/authoring/workflow.ts +8 -0
- package/dist/builtin/workflows/src/authoring.d.ts +1 -1
- package/dist/builtin/workflows/src/durable/completed-catalog.ts +5 -2
- package/dist/builtin/workflows/src/durable/dbos-envelope.ts +1 -1
- package/dist/builtin/workflows/src/durable/resume-eligibility.ts +5 -3
- package/dist/builtin/workflows/src/durable/run-timing.ts +41 -10
- package/dist/builtin/workflows/src/durable/tool-primitive.ts +24 -2
- package/dist/builtin/workflows/src/engine/options.ts +1 -0
- package/dist/builtin/workflows/src/engine/primitives/workflow.ts +12 -3
- package/dist/builtin/workflows/src/engine/run-budget.ts +308 -0
- package/dist/builtin/workflows/src/engine/run-returned-status.ts +8 -0
- package/dist/builtin/workflows/src/engine/run-tool-node-lifecycle.ts +6 -0
- package/dist/builtin/workflows/src/engine/run.ts +124 -2
- package/dist/builtin/workflows/src/engine/runtime.ts +9 -0
- package/dist/builtin/workflows/src/extension/config-file-loader.ts +6 -0
- package/dist/builtin/workflows/src/extension/config-loader.ts +24 -1
- package/dist/builtin/workflows/src/extension/dispatcher.ts +6 -5
- package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +2 -0
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +2975 -843
- package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +51 -4
- package/dist/builtin/workflows/src/extension/public-types.ts +3 -1
- package/dist/builtin/workflows/src/extension/runtime-durable-resume.ts +7 -1
- package/dist/builtin/workflows/src/extension/runtime.ts +22 -10
- package/dist/builtin/workflows/src/extension/workflow-module-loader.ts +5 -0
- package/dist/builtin/workflows/src/extension/workflow-prompts.ts +1 -0
- package/dist/builtin/workflows/src/extension/workflow-schema.ts +16 -0
- package/dist/builtin/workflows/src/extension/workflow-status-summary.ts +44 -1
- package/dist/builtin/workflows/src/extension/workflow-tool-content.ts +10 -1
- package/dist/builtin/workflows/src/extension/workflow-tool-control.ts +21 -9
- package/dist/builtin/workflows/src/runs/foreground/executor-continuation.ts +14 -0
- package/dist/builtin/workflows/src/runs/foreground/executor-lifecycle.ts +15 -4
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-call.ts +62 -5
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +4 -0
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-types.ts +2 -0
- package/dist/builtin/workflows/src/runs/foreground/executor-types.ts +3 -1
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +10 -1
- package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +1 -0
- package/dist/builtin/workflows/src/shared/authoring-contract-stage.ts +1 -0
- package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +7 -0
- package/dist/builtin/workflows/src/shared/authoring-contract-ui.ts +7 -0
- package/dist/builtin/workflows/src/shared/authoring-contract.d.ts +1 -0
- package/dist/builtin/workflows/src/shared/budget-meter.ts +34 -0
- package/dist/builtin/workflows/src/shared/budget.d.ts +67 -0
- package/dist/builtin/workflows/src/shared/budget.ts +127 -0
- package/dist/builtin/workflows/src/shared/persistence-restore-helpers.ts +92 -8
- package/dist/builtin/workflows/src/shared/persistence-restore.ts +11 -1
- package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +15 -3
- package/dist/builtin/workflows/src/shared/returned-run-status.ts +35 -2
- package/dist/builtin/workflows/src/shared/store-public-types.ts +4 -1
- package/dist/builtin/workflows/src/shared/store-run-methods.ts +8 -1
- package/dist/builtin/workflows/src/shared/store-stage-methods.ts +1 -0
- package/dist/builtin/workflows/src/shared/store-types.ts +24 -0
- package/dist/builtin/workflows/src/shared/types.ts +3 -0
- package/dist/builtin/workflows/src/shared/workflow-artifacts.ts +1 -0
- package/dist/builtin/workflows/src/shared/workflow-authoring-types.d.ts +3 -0
- package/dist/builtin/workflows/src/shared/workflow-authoring-types.ts +3 -0
- package/dist/core/atomic-guide-command.d.ts.map +1 -1
- package/dist/core/atomic-guide-command.js +1 -0
- package/dist/core/atomic-guide-command.js.map +1 -1
- package/dist/core/extensions/ui-types.d.ts +13 -3
- package/dist/core/extensions/ui-types.d.ts.map +1 -1
- package/dist/core/extensions/ui-types.js +15 -3
- package/dist/core/extensions/ui-types.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +33 -3
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/main-deferred-startup.d.ts.map +1 -1
- package/dist/main-deferred-startup.js +6 -2
- package/dist/main-deferred-startup.js.map +1 -1
- package/dist/modes/interactive/interactive-startup.js +4 -0
- package/dist/modes/interactive/interactive-startup.js.map +1 -1
- package/dist/modes/interactive/interactive-tui.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-tui.js +19 -1
- package/dist/modes/interactive/interactive-tui.js.map +1 -1
- package/dist/modes/interactive-engine/isolated-runtime.d.ts +7 -0
- package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
- package/dist/modes/interactive-engine/isolated-runtime.js +94 -37
- package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +1 -0
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +15 -2
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-input-scheduler.d.ts +3 -2
- package/dist/modes/rpc/rpc-input-scheduler.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-input-scheduler.js +5 -2
- package/dist/modes/rpc/rpc-input-scheduler.js.map +1 -1
- package/docs/extensions.md +1 -1
- package/docs/quickstart.md +1 -0
- package/docs/skills.md +4 -0
- package/docs/workflows.md +74 -10
- package/npm-shrinkwrap.json +29 -29
- package/package.json +2 -2
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: create-spec
|
|
3
3
|
description: "Create a detailed execution plan/spec/PRD for implementing features or refactors in a codebase, designed around the program's entrypoints, the doors that carry domain intent, by leveraging existing research in the codebase."
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
author: Atomic
|
|
7
|
+
method-source: https://github.com/dmmulroy/skills/blob/main/tech-spec/SKILL.md
|
|
4
8
|
---
|
|
5
9
|
|
|
6
10
|
You are tasked with creating a spec for implementing a new feature or system change in the codebase by leveraging existing research in the **$ARGUMENTS** path. If no research path is specified, use the entire `research/` directory. IMPORTANT: Research documents are located in the `research/` directory — do NOT look in the `specs/` directory for research. Follow the template below to produce a comprehensive specification as output in the `specs/` folder using the findings from RELEVANT research documents found in `research/`. The spec file MUST be named using the format `YYYY-MM-DD-topic.md` (e.g., `specs/2026-03-26-my-feature.md`), where the date is the current date and the topic is a kebab-case summary. Tip: It's good practice to use the `codebase-research-locator` and `codebase-research-analyzer` agents to help you find and analyze the research documents in the `research/` directory. It is also HIGHLY recommended to cite relevant research throughout the spec for additional context.
|
|
@@ -19,6 +23,28 @@ You are tasked with creating a spec for implementing a new feature or system cha
|
|
|
19
23
|
- When allowing breaking changes, document existing legacy behavior, compatibility shims, optional flags, and public APIs as current state, not as constraints future specs must preserve unless the user explicitly asks for preservation.
|
|
20
24
|
- When not allowing breaking changes, document public APIs, compatibility-sensitive surfaces, downstream callers, migration constraints, and behavior that future work must preserve.
|
|
21
25
|
|
|
26
|
+
### Choose a working path before drafting
|
|
27
|
+
|
|
28
|
+
First inspect the context already available: the conversation, the requested research path, the `research/` documents, local docs, and the codebase. Choose the path that matches what is actually known:
|
|
29
|
+
|
|
30
|
+
- **Path A — Convert context to spec:** use this when the available conversation, research, docs, or code contain enough background to describe the problem, constraints, affected code, and acceptance criteria.
|
|
31
|
+
- **Path B — Grill first:** use this when the user wants a spec but the problem, constraints, design direction, affected code, or acceptance criteria are not yet clear. Do not invent architectural decisions.
|
|
32
|
+
|
|
33
|
+
If the codebase can answer a question, inspect it instead of asking the user. For Path B, do not write a full spec yet: state what context is missing, then use the existing `ask_user_question` and contrastive-clarification rules below (one question at a time or a logical group, with a recommended answer and concrete trade-offs). Once the answers and repository evidence provide enough context, run Path A.
|
|
34
|
+
|
|
35
|
+
### Path A working method
|
|
36
|
+
|
|
37
|
+
When Path A is selected, work in this order and map the results into the numbered document headings below:
|
|
38
|
+
|
|
39
|
+
1. **Load standards and local context.** Inspect local vocabulary, module layout, domain concepts, errors, adapters, observability, runtime patterns, and test style. Check precedent before introducing a pattern, library, adapter, schema style, or test strategy; ground the findings in §2.1 and the door names.
|
|
40
|
+
2. **Extract the design problem.** Record current state, users and callers, pain point, goals, non-goals, constraints, invariants, affected systems, likely doors, operational concerns, risks, and open questions in §2, §3, and §9. Unknowns stay open questions.
|
|
41
|
+
3. **Explore materially different alternatives before locking the recommendation.** Compare interface shape, seam placement, ownership, call stack, runtime topology, and module boundaries—not just names. Record the comparison in §6 even though §6 appears after the recommended design in the document.
|
|
42
|
+
4. **Specify typed contracts.** Define the recommended doors, types, APIs, named failures, and refusals in §5.1–§5.3 while preserving the door rubric.
|
|
43
|
+
5. **Specify call stacks and data flow.** Put current and proposed paths, failure behavior, retry, cancellation, and idempotency where reachable into §5.4 using the visual formats below.
|
|
44
|
+
6. **Map files and modules.** List add/change/delete/test/config files and the responsibility each owns under §4 or §5.
|
|
45
|
+
7. **Plan vertical RGR TDD slices.** In §8, take each important public door or seam through a red behavior test, the smallest green implementation, and a refactor that preserves the behavior; do not write a horizontal all-tests-first plan.
|
|
46
|
+
8. **Produce the design-only spec.** Write `specs/YYYY-MM-DD-topic.md`; do not implement the change in this skill.
|
|
47
|
+
|
|
22
48
|
## Design philosophy: a spec is a theory of its doors
|
|
23
49
|
|
|
24
50
|
The entrypoints of a program, read together, are the program's **theory of its own purpose**. Everything inside the boundary is mechanism — the *how*. Only at the boundary does the code speak in terms of meaning — the *what* and the *why*. So the single most important thing this spec defines is not the mechanism inside the system, but the **set of doors** the system keeps: the functions, routes, and RPC methods through which untrusted input arrives and irreversible effects happen.
|
|
@@ -76,6 +102,30 @@ For each non-trivial entrypoint the spec introduces or changes, walk these in or
|
|
|
76
102
|
|
|
77
103
|
</EXTREMELY_IMPORTANT>
|
|
78
104
|
|
|
105
|
+
### Shape-first visual language
|
|
106
|
+
|
|
107
|
+
Specs stay in Markdown, but their visuals should use the smallest view that makes the key point clear. Skip a preamble, keep prose brief, and place each visual next to the short text it supports. Use one or several of these as needed; do not use all of them every time:
|
|
108
|
+
|
|
109
|
+
- Show logic or algorithms as indented `text` pseudocode, not prose alone.
|
|
110
|
+
- Show runtime control flow as an indented call tree.
|
|
111
|
+
- Show UI structure as a `tsx` component tree, including the state and module boundaries that matter.
|
|
112
|
+
- Show file responsibility or a broad refactor as a shallow `text` file tree.
|
|
113
|
+
- Show component interaction, control flow, or data flow with Mermaid.
|
|
114
|
+
- Use `diff` when the surrounding shape already exists and the point is what changes. Match the diff to the topic: component tree, file tree, call tree, or state/control flow.
|
|
115
|
+
- Show the whole block when most of it is new, omitted context would hide ownership or order, or the reader needs a copyable target shape.
|
|
116
|
+
|
|
117
|
+
For a visual UI, layout, state comparison, or concept too dense for Mermaid, allow one focused `show-me-{description}.html` artifact (diagram, infographic, or short slide deck). Match the product's colors, type, spacing, components, labels, and data; support desktop and mobile. Specs remain Markdown in `specs/`; HTML is an optional extra only when the page is the point. Open it with Atomic's `bash` tool and a portable opener:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
if [ "$(uname -s)" = "Darwin" ] && command -v open >/dev/null 2>&1; then
|
|
121
|
+
open "path/to/show-me-{description}.html"
|
|
122
|
+
elif [ "$(uname -s)" = "Linux" ] && command -v xdg-open >/dev/null 2>&1; then
|
|
123
|
+
xdg-open "path/to/show-me-{description}.html"
|
|
124
|
+
else
|
|
125
|
+
printf 'Open this file: %s\n' "path/to/show-me-{description}.html"
|
|
126
|
+
fi
|
|
127
|
+
```
|
|
128
|
+
|
|
79
129
|
# [Project Name] Technical Design Document / RFC
|
|
80
130
|
|
|
81
131
|
| Document Metadata | Details |
|
|
@@ -97,7 +147,7 @@ _Instruction: Why are we doing this? Why now? Link to the Product Requirement Do
|
|
|
97
147
|
|
|
98
148
|
### 2.1 Current State
|
|
99
149
|
|
|
100
|
-
_Instruction: Describe the existing architecture
|
|
150
|
+
_Instruction: Describe the existing architecture and be honest about the flaws — including which existing doors **leak** (named for tools, dishonest compression, scattered danger). Pick the smallest view that makes the current state clear: a shallow file tree for ownership, a call tree for runtime flow, a component tree for UI structure, or Mermaid for interaction/data flow. Place the visual next to the short explanation and do not force a diagram when prose is clearer._
|
|
101
151
|
|
|
102
152
|
- **Architecture:** Currently, Service A communicates with Service B via a shared SQL database.
|
|
103
153
|
- **Limitations:** This creates a tight coupling; when Service A locks the table, Service B times out.
|
|
@@ -130,37 +180,19 @@ _Instruction: Explicitly state what you are NOT doing. Remember: **intent lives
|
|
|
130
180
|
|
|
131
181
|
## 4. Proposed Solution (High-Level Design)
|
|
132
182
|
|
|
133
|
-
_Instruction: The "Big Picture."
|
|
183
|
+
_Instruction: The "Big Picture." Choose the smallest fitting view from the shape-first visual language above; use one or several only when each answers a different question. Types, trees, diffs, or Mermaid should define the shape, while brief prose explains why. Do not use a heavy styled diagram when a simpler view communicates the boundary._
|
|
134
184
|
|
|
135
185
|
### 4.1 System Architecture Diagram
|
|
136
186
|
|
|
137
|
-
_Instruction:
|
|
187
|
+
_Instruction: Show the system boundary and mark the **airlock** (the single edge where untrusted input becomes a trusted request). Use Mermaid for component interaction or data flow, a shallow file tree for module responsibility, a call tree for runtime control flow, or a component tree for UI structure. Show the whole block when most of it is new or omitted context would hide ownership or order._
|
|
138
188
|
|
|
139
189
|
```mermaid
|
|
140
|
-
%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#f8f9fa','primaryTextColor':'#2c3e50','primaryBorderColor':'#4a5568','lineColor':'#4a90e2','secondaryColor':'#ffffff','tertiaryColor':'#e9ecef','clusterBkg':'#ffffff','clusterBorder':'#cbd5e0'}}}%%
|
|
141
190
|
flowchart TB
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
User(("◉<br><b>User</b>")):::person
|
|
149
|
-
subgraph Boundary["◆ System Boundary — Airlock at the edge"]
|
|
150
|
-
direction TB
|
|
151
|
-
Gateway{{"<b>API Gateway</b><br><i>auth · validate · authorize</i><br>the one trust transition"}}:::core
|
|
152
|
-
API["<b>Core Service</b><br><i>trusts its own invariants</i>"]:::core
|
|
153
|
-
Worker(["<b>Worker</b><br><i>async</i>"]):::support
|
|
154
|
-
DB[("●<br><b>Primary DB</b>")]:::db
|
|
155
|
-
end
|
|
156
|
-
Ext{{"<b>Payment Provider</b>"}}:::external
|
|
157
|
-
|
|
158
|
-
User -->|"1. HTTPS (untrusted)"| Gateway
|
|
159
|
-
Gateway -->|"2. trusted request"| API
|
|
160
|
-
API -->|"3. persist (txn)"| DB
|
|
161
|
-
API -.->|"4. enqueue"| Worker
|
|
162
|
-
Worker -.->|"5. settle (irreversible)"| Ext
|
|
163
|
-
style Boundary fill:#fff,stroke:#cbd5e0,stroke-width:2px,stroke-dasharray:8 4
|
|
191
|
+
User((User)) -->|untrusted request| Gateway["Gateway<br/>auth · validate · authorize<br/>airlock"]
|
|
192
|
+
Gateway -->|trusted request| API["Core service<br/>trusts its invariants"]
|
|
193
|
+
API --> DB[(Primary DB)]
|
|
194
|
+
API -.-> Worker[Worker]
|
|
195
|
+
Worker -.-> Ext["External provider<br/>irreversible effect"]
|
|
164
196
|
```
|
|
165
197
|
|
|
166
198
|
### 4.2 Architectural Pattern
|
|
@@ -177,6 +209,13 @@ _Instruction: Name the pattern (e.g., "Event Sourcing", "BFF — Backend for Fro
|
|
|
177
209
|
| Event Bus | Decouples services | Kafka | Durable log, replay capability. |
|
|
178
210
|
| Projections DB | Read-optimized views | MongoDB | Flexible schema for diverse receipt formats. |
|
|
179
211
|
|
|
212
|
+
_Instruction: Map the files and modules that implement the design. List every add, change, delete, test, and configuration file, and state the responsibility each owns. Use a shallow file tree when layout is the key point; otherwise use this compact map._
|
|
213
|
+
|
|
214
|
+
| Path | Action | Owns |
|
|
215
|
+
| ---- | ------ | ---- |
|
|
216
|
+
| `src/feature/door.ts` | change | Public door and boundary contract |
|
|
217
|
+
| `test/feature/door.test.ts` | add | Vertical behavior slice through the door |
|
|
218
|
+
|
|
180
219
|
### 4.4 The Door Set at a Glance (Stranger-Across-Time View)
|
|
181
220
|
|
|
182
221
|
_Instruction: List the entrypoint **names alone** — no signatures, no bodies. A competent stranger should reconstruct the system's purpose from this list. If they cannot, intent has leaked into the mechanism; return to §5 and rename until they can. Mark every door that guards an irreversible effect with ⚠._
|
|
@@ -189,7 +228,7 @@ _Instruction: The "Meat" of the document. Sufficient detail for an engineer to s
|
|
|
189
228
|
|
|
190
229
|
### 5.1 The Doors (Entrypoint Contracts)
|
|
191
230
|
|
|
192
|
-
_Instruction: For each non-trivial entrypoint, give a typed signature (typed pseudocode is fine — read the types, not the syntax), the one-sentence guarantee (no "and"), the named failure set, and the refusals it enforces in the type system. Then record the rubric result. Make illegal states **unrepresentable**, not merely checked. Cite the `research/` doc that establishes each joint._
|
|
231
|
+
_Instruction: For each non-trivial entrypoint, give a typed signature (typed pseudocode is fine — read the types, not the syntax), the one-sentence guarantee (no "and"), the named failure set, and the refusals it enforces in the type system. Then record the rubric result. Make illegal states **unrepresentable**, not merely checked. Cite the `research/` doc that establishes each joint. Use a whole block when the door is mostly new or the reader needs a copyable target shape; use a topic-matched `diff` when an existing door is changing. Show the runtime path to and from the door as a call tree, and include UI/module boundaries as a component tree when they matter._
|
|
193
232
|
|
|
194
233
|
```
|
|
195
234
|
// — Money. Two doors, and there is no third way to move a cent. —
|
|
@@ -260,14 +299,35 @@ _Instruction: Provide ERDs or JSON schemas. Discuss normalization vs. denormaliz
|
|
|
260
299
|
|
|
261
300
|
### 5.4 Algorithms and State Management
|
|
262
301
|
|
|
263
|
-
_Instruction: Describe complex logic, state machines, or consistency models. Tie each state transition to the door that performs it._
|
|
302
|
+
_Instruction: Describe complex logic, state machines, or consistency models. Tie each state transition to the door that performs it and choose the smallest view that makes the behavior clear. Use indented `text` pseudocode for algorithms, a call tree for runtime control flow, Mermaid for interaction or data flow, and a topic-matched `diff` for changes to an existing state or control-flow shape. Include failure, retry, cancellation, idempotency, and concurrency behavior whenever reachable; keep unknowns for §9 rather than inventing them._
|
|
303
|
+
|
|
304
|
+
```text
|
|
305
|
+
on(settle_payment)
|
|
306
|
+
if request is a replay
|
|
307
|
+
return the recorded settlement
|
|
308
|
+
validate the authorized charge
|
|
309
|
+
if provider call fails
|
|
310
|
+
return named retryable error
|
|
311
|
+
persist settlement
|
|
312
|
+
return settlement
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
```text
|
|
316
|
+
publishDraft
|
|
317
|
+
authenticate
|
|
318
|
+
loadDraft
|
|
319
|
+
checkVersion
|
|
320
|
+
persistPublication
|
|
321
|
+
notifySubscribers
|
|
322
|
+
```
|
|
264
323
|
|
|
265
324
|
- **State Machine:** An invoice moves `DRAFT` → `LOCKED` → `PROCESSING` → `PAID`; the `PROCESSING → PAID` transition happens only through `settle_payment`.
|
|
266
325
|
- **Concurrency:** Optimistic locking on the `version` column; on the wire this surfaces as `If-Match`/`412`.
|
|
326
|
+
- **Data flow:** Show boundary input, parsing, canonical domain input, service door, adapter call, typed result/error, projection, and serialized output in a Mermaid diagram or concise arrow chain when those stages are reachable.
|
|
267
327
|
|
|
268
328
|
## 6. Alternatives Considered
|
|
269
329
|
|
|
270
|
-
_Instruction: Prove you thought about trade-offs — including alternative **door sets** (e.g., one god endpoint vs. distinct joints).
|
|
330
|
+
_Instruction: Prove you thought about trade-offs — including alternative **door sets** (e.g., one god endpoint vs. distinct joints). Explore materially different alternatives before locking the recommendation, then record why the selected boundary is better. Compare interface shape, seam placement, ownership, call stack, runtime topology, and module boundaries — not just names._
|
|
271
331
|
|
|
272
332
|
| Option | Pros | Cons | Reason for Rejection |
|
|
273
333
|
| ------------------------------------------- | ------------------------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------ |
|
|
@@ -289,7 +349,7 @@ _Instruction: This is where "keep the dangerous doors few and honest" and "the a
|
|
|
289
349
|
|
|
290
350
|
## 8. Test Plan
|
|
291
351
|
|
|
292
|
-
_Instruction: Test the doors at their promises and their refusals — not just the happy path. Every exit in rubric #5 deserves a test. The interactive verification is what lets a human or another agent confirm the feature is correct without reading the bodies — the stranger-across-time test, made executable._
|
|
352
|
+
_Instruction: Test the doors at their promises and their refusals — not just the happy path. Every exit in rubric #5 deserves a test. Plan vertical red-green-refactor (RGR) slices through public doors and seams: each slice starts with one failing behavior test, adds the smallest implementation that turns it green, then refactors without changing the behavior. Do not write all tests first as a horizontal batch. The interactive verification is what lets a human or another agent confirm the feature is correct without reading the bodies — the stranger-across-time test, made executable._
|
|
293
353
|
|
|
294
354
|
- **Unit Tests:** each door's named failure variants; the *refusals* (e.g., a type/construction test proving `settle_payment` cannot accept anything but an `AuthorizedCharge`).
|
|
295
355
|
- **End-to-End Tests:** full domain flows named by joint (register → authenticate → authorize → settle), driven through the real wire doors of §5.2.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 HumanLayer
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: show-me
|
|
3
|
+
description: Help the user understand the current topic visually with concise diagrams, code-shape sketches, and focused HTML artifacts.
|
|
4
|
+
license: MIT. LICENSE.txt has complete terms
|
|
5
|
+
metadata:
|
|
6
|
+
author: HumanLayer
|
|
7
|
+
github-repo: https://github.com/humanlayer/skills
|
|
8
|
+
github-path: plugins/show-me/skills/show-me
|
|
9
|
+
github-ref: refs/heads/main
|
|
10
|
+
github-tree-sha: d57a0a0afc2800a4cc7a7765e65a9c6c18e67b4f
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
Help the user understand the current topic of conversation visually. Skip the preamble and keep prose brief. Pick the smallest view that makes the key point clear.
|
|
14
|
+
|
|
15
|
+
- Show logic or an algorithm as pseudocode:
|
|
16
|
+
|
|
17
|
+
```text
|
|
18
|
+
on(save)
|
|
19
|
+
if content is unchanged
|
|
20
|
+
return cached result
|
|
21
|
+
write new content
|
|
22
|
+
return fresh result
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- Show runtime control flow as a call tree:
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
submitForm
|
|
29
|
+
createSession
|
|
30
|
+
persistPrompt
|
|
31
|
+
launchAgent
|
|
32
|
+
navigateToSession
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
- Show UI structure as a component tree, including state and module boundaries that matter:
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
<SessionPage> (apps/example/src/routes/session.tsx)
|
|
39
|
+
useSessionEvents()
|
|
40
|
+
<SessionToolbar>
|
|
41
|
+
<RunSkillButton> (packages/ui)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- Show file responsibility or a broad refactor as a shallow file tree:
|
|
45
|
+
|
|
46
|
+
```text
|
|
47
|
+
src/
|
|
48
|
+
├── commands/ # parses user actions
|
|
49
|
+
├── sessions/ # owns session state
|
|
50
|
+
└── transport/ # sends API requests
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
- Show component interaction, control flow, or data flow with Mermaid:
|
|
54
|
+
|
|
55
|
+
```mermaid
|
|
56
|
+
sequenceDiagram
|
|
57
|
+
participant User
|
|
58
|
+
participant UI
|
|
59
|
+
participant Daemon
|
|
60
|
+
User->>UI: choose command
|
|
61
|
+
UI->>Daemon: send expanded prompt
|
|
62
|
+
Daemon-->>UI: stream result
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
- Use `diff` when the point is what changes and the surrounding shape already exists. Match the diff shape to the topic.
|
|
66
|
+
|
|
67
|
+
For a component change:
|
|
68
|
+
|
|
69
|
+
```diff
|
|
70
|
+
<SessionPage>
|
|
71
|
+
useSessionEvents()
|
|
72
|
+
<SessionToolbar>
|
|
73
|
+
+ <RunSkillButton />
|
|
74
|
+
<SessionTimeline>
|
|
75
|
+
+ <SkillResultCard />
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
For a file-layout change:
|
|
79
|
+
|
|
80
|
+
```diff
|
|
81
|
+
src/
|
|
82
|
+
├── commands/
|
|
83
|
+
+│ └── show-me.ts # expands the slash command
|
|
84
|
+
├── sessions/
|
|
85
|
+
-└── transport.ts
|
|
86
|
+
+└── transport/
|
|
87
|
+
+ ├── client.ts
|
|
88
|
+
+ └── stream.ts
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
For a call-tree or call-stack change:
|
|
92
|
+
|
|
93
|
+
```diff
|
|
94
|
+
submitForm
|
|
95
|
+
createSession
|
|
96
|
+
persistPrompt
|
|
97
|
+
+ expandSkillMention
|
|
98
|
+
launchAgent
|
|
99
|
+
- navigateToSession
|
|
100
|
+
+ navigateToSession
|
|
101
|
+
+ subscribeToEvents
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
For a state or control-flow change:
|
|
105
|
+
|
|
106
|
+
```diff
|
|
107
|
+
on(save)
|
|
108
|
+
- write content
|
|
109
|
+
+ if content is unchanged
|
|
110
|
+
+ return cached result
|
|
111
|
+
+ write new content
|
|
112
|
+
+ invalidate cache
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
- Show the whole block when most of it is new, when omitted context would hide ownership or order, or when the user needs a copyable target shape:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
function expandSkill(command: string): string {
|
|
119
|
+
const skillName = command.slice(1)
|
|
120
|
+
return `use the ${skillName} skill`
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
- For a visual UI, layout, state comparison, or concept too dense for Mermaid, write one focused HTML file — a diagram, an infographic, or a short slide deck, whichever fits the point. Match the product's colors, type, spacing, and components; use real labels and data; support desktop and mobile. Then open it for the user:
|
|
125
|
+
|
|
126
|
+
Use Atomic's `bash` tool to run:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
if [ "$(uname -s)" = "Darwin" ] && command -v open >/dev/null 2>&1; then
|
|
130
|
+
open "path/to/show-me-{description}.html"
|
|
131
|
+
elif [ "$(uname -s)" = "Linux" ] && command -v xdg-open >/dev/null 2>&1; then
|
|
132
|
+
xdg-open "path/to/show-me-{description}.html"
|
|
133
|
+
else
|
|
134
|
+
printf 'Open this file: %s\n' "path/to/show-me-{description}.html"
|
|
135
|
+
fi
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### guidance
|
|
139
|
+
|
|
140
|
+
Place each visual next to the short text it supports. Keep only the calls, files, props, states, and boundaries needed to answer the user's current question or the options to resolve the current discussion point.
|
|
141
|
+
|
|
142
|
+
You may use one of these, you may use several, it is unlikely you will use all of them. Use your judgement and don't overwhelm the user.
|
|
143
|
+
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { basename } from "node:path";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { assertWorkflowBudget, type WorkflowBudget } from "../shared/budget.js";
|
|
3
4
|
import type {
|
|
4
5
|
WorkflowDefinition,
|
|
5
6
|
WorkflowInputBindings,
|
|
@@ -133,6 +134,10 @@ function freezeInputBindings(binding: WorkflowWorktreeInputBinding | undefined):
|
|
|
133
134
|
});
|
|
134
135
|
}
|
|
135
136
|
|
|
137
|
+
function freezeBudget(budget: WorkflowBudget | undefined): WorkflowBudget | undefined {
|
|
138
|
+
return budget === undefined ? undefined : Object.freeze({ ...budget });
|
|
139
|
+
}
|
|
140
|
+
|
|
136
141
|
export function workflow<
|
|
137
142
|
const TInputs extends WorkflowInputSchemaMap = Record<never, never>,
|
|
138
143
|
const TOutputs extends WorkflowOutputSchemaMap = WorkflowOutputSchemaMap,
|
|
@@ -166,6 +171,7 @@ export function workflow<
|
|
|
166
171
|
if (!Number.isFinite(heartbeatIntervalMinutes) || heartbeatIntervalMinutes < 0) {
|
|
167
172
|
throw new TypeError("workflow: heartbeatIntervalMinutes must be a non-negative finite number");
|
|
168
173
|
}
|
|
174
|
+
assertWorkflowBudget(spec.budget, "workflow: budget");
|
|
169
175
|
|
|
170
176
|
const name = resolveWorkflowName(spec.name);
|
|
171
177
|
const normalizedName = normalizeWorkflowName(name);
|
|
@@ -173,6 +179,7 @@ export function workflow<
|
|
|
173
179
|
const frozenInputs = freezeSchemaMap(spec.inputs ?? ({} as TInputs));
|
|
174
180
|
const frozenOutputs = freezeSchemaMap(spec.outputs);
|
|
175
181
|
const inputBindings = freezeInputBindings(spec.worktreeFromInputs);
|
|
182
|
+
const budget = freezeBudget(spec.budget);
|
|
176
183
|
const run: WorkflowRunFn<WorkflowInputsFromSchemas<TInputs>, WorkflowOutputsFromSchemas<TOutputs>> = async (ctx) =>
|
|
177
184
|
specRun(ctx);
|
|
178
185
|
|
|
@@ -186,6 +193,7 @@ export function workflow<
|
|
|
186
193
|
inputs: frozenInputs,
|
|
187
194
|
outputs: frozenOutputs,
|
|
188
195
|
...(inputBindings !== undefined ? { inputBindings } : {}),
|
|
196
|
+
...(budget !== undefined ? { budget } : {}),
|
|
189
197
|
run,
|
|
190
198
|
};
|
|
191
199
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { TSchema } from "typebox";
|
|
2
2
|
export type { Static, TSchema } from "typebox";
|
|
3
|
-
export type { AgentSessionAdapter, CompleteAdapter, CompleteStageOpts, GitWorktreeSetupOptions, GitWorktreeSetupResult, PromptAdapter, PromptOptions, ResolvedInputs, RunResult, RunStatus, StageAdapters, StageContext, StageExecutionMeta, StageMcpOptions, StageOptions, StageOutputOptions, StagePromptOptions, StageSendUserMessageOptions, StageSessionCreateOptions, StageSessionCreateResult, StageSessionEvent, StageSessionRuntime, StageSnapshot, StageStatus, StageUserMessageContent, StageUserMessageDelivery, ToolNodeSnapshot, WorkflowAction, WorkflowArtifact, WorkflowChainOptions, WorkflowChildResult, WorkflowContextMode, WorkflowControlEvent, WorkflowCustomToolDefinition, WorkflowCustomUiComponent, WorkflowCustomUiFactory, WorkflowCustomUiKeybindings, WorkflowCustomUiOptions, WorkflowCustomUiOverlayHandle, WorkflowCustomUiOverlayOptions, WorkflowCustomUiTheme, WorkflowCustomUiTui, WorkflowDetails, WorkflowDetailsMode, WorkflowDetailsStatus, WorkflowExecutionMode, WorkflowExecutionPolicy, WorkflowExitOptions, WorkflowExitStatus, WorkflowInputBindings, WorkflowInputSchema, WorkflowInputSchemaMap, WorkflowInputValues, WorkflowIntercomSummary, WorkflowMaxOutput, WorkflowMcpPort, WorkflowModelAttempt, WorkflowModelCatalogPort, WorkflowModelFallbackFields, WorkflowModelInfo, WorkflowModelUsage, WorkflowModelValue, WorkflowOutputMode, WorkflowOutputSchema, WorkflowOutputSchemaMap, WorkflowOutputValues, WorkflowParallelOptions, WorkflowPersistencePort, WorkflowProgressSummary, WorkflowRunChildArgs, WorkflowRunChildOptions, WorkflowRunChildOptionsArgument, WorkflowRunOutput, WorkflowRuntimeConfig, WorkflowSerializableObject, WorkflowSerializablePrimitive, WorkflowSerializableValue, WorkflowSharedTaskDefaults, WorkflowTaskContext, WorkflowTaskContextInput, WorkflowTaskOptions, WorkflowTaskResult, WorkflowTaskSessionFields, WorkflowTaskSessionOptions, WorkflowTaskStep, WorkflowThinkingLevel, WorkflowToolContext, WorkflowToolError, WorkflowToolFailure, WorkflowToolOptions, WorkflowToolOutcome, WorkflowToolPrimitive, WorkflowToolReturnOptions, WorkflowToolSuccess, WorkflowToolThrowOptions, WorkflowUIAdapter, WorkflowUIContext, WorkflowWorktreeInputBinding, } from "./shared/authoring-contract.js";
|
|
3
|
+
export type { AgentSessionAdapter, CompleteAdapter, CompleteStageOpts, EffectiveBudget, GitWorktreeSetupOptions, GitWorktreeSetupResult, PromptAdapter, PromptOptions, ResolvedInputs, RunResult, RunStatus, StageAdapters, StageContext, StageExecutionMeta, StageMcpOptions, StageOptions, StageOutputOptions, StagePromptOptions, StageSendUserMessageOptions, StageSessionCreateOptions, StageSessionCreateResult, StageSessionEvent, StageSessionRuntime, StageSnapshot, StageStatus, StageUserMessageContent, StageUserMessageDelivery, ToolNodeSnapshot, WorkflowAction, WorkflowArtifact, WorkflowBudget, WorkflowChainOptions, WorkflowChildResult, WorkflowContextMode, WorkflowControlEvent, WorkflowCustomToolDefinition, WorkflowCustomUiComponent, WorkflowCustomUiFactory, WorkflowCustomUiKeybindings, WorkflowCustomUiOptions, WorkflowCustomUiOverlayHandle, WorkflowCustomUiOverlayOptions, WorkflowCustomUiTheme, WorkflowCustomUiTui, WorkflowDetails, WorkflowDetailsMode, WorkflowDetailsStatus, WorkflowExecutionMode, WorkflowExecutionPolicy, WorkflowExitOptions, WorkflowExitStatus, WorkflowInputBindings, WorkflowInputSchema, WorkflowInputSchemaMap, WorkflowInputValues, WorkflowIntercomSummary, WorkflowMaxOutput, WorkflowMcpPort, WorkflowModelAttempt, WorkflowModelCatalogPort, WorkflowModelFallbackFields, WorkflowModelInfo, WorkflowModelUsage, WorkflowModelValue, WorkflowOutputMode, WorkflowOutputSchema, WorkflowOutputSchemaMap, WorkflowOutputValues, WorkflowParallelOptions, WorkflowPersistencePort, WorkflowProgressSummary, WorkflowRunChildArgs, WorkflowRunChildOptions, WorkflowRunChildOptionsArgument, WorkflowRunOutput, WorkflowRuntimeConfig, WorkflowSerializableObject, WorkflowSerializablePrimitive, WorkflowSerializableValue, WorkflowSharedTaskDefaults, WorkflowTaskContext, WorkflowTaskContextInput, WorkflowTaskOptions, WorkflowTaskResult, WorkflowTaskSessionFields, WorkflowTaskSessionOptions, WorkflowTaskStep, WorkflowThinkingLevel, WorkflowToolContext, WorkflowToolError, WorkflowToolFailure, WorkflowToolOptions, WorkflowToolOutcome, WorkflowToolPrimitive, WorkflowToolReturnOptions, WorkflowToolSuccess, WorkflowToolThrowOptions, WorkflowUIAdapter, WorkflowUIContext, WorkflowWorktreeInputBinding, } from "./shared/authoring-contract.js";
|
|
4
4
|
import type * as AuthoringContract from "./shared/authoring-contract.js";
|
|
5
5
|
import type { AuthoredWorkflowSpec as SharedAuthoredWorkflowSpec, WorkflowInputsFromSchemas, WorkflowOutputsFromSchemas, WorkflowProvidedInputsFromSchemas } from "./shared/workflow-authoring-types.js";
|
|
6
6
|
export type { WorkflowInputsFromSchemas, WorkflowOutputsFromSchemas, WorkflowProvidedInputsFromSchemas, } from "./shared/workflow-authoring-types.js";
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
} from "./completed-catalog-stage-groups.js";
|
|
24
24
|
import { isDurableWorkflowResumable } from "./resume-eligibility.js";
|
|
25
25
|
import { resolveDurableEntry } from "./resume-runtime.js";
|
|
26
|
-
import { priorRunElapsedMs, RUN_TIMING_CHECKPOINT_NAME } from "./run-timing.js";
|
|
26
|
+
import { priorRunElapsedMs, RUN_TIMING_CHECKPOINT_NAME, RUN_USAGE_CHECKPOINT_NAME } from "./run-timing.js";
|
|
27
27
|
import { WORKFLOW_HEARTBEAT_ANCHOR_CHECKPOINT_NAME } from "./workflow-heartbeat-anchor.js";
|
|
28
28
|
|
|
29
29
|
/**
|
|
@@ -32,7 +32,10 @@ import { WORKFLOW_HEARTBEAT_ANCHOR_CHECKPOINT_NAME } from "./workflow-heartbeat-
|
|
|
32
32
|
* to skip them or they surface as phantom cached tool nodes.
|
|
33
33
|
*/
|
|
34
34
|
function isReservedRunCheckpointHash(argsHash: string): boolean {
|
|
35
|
-
return
|
|
35
|
+
return (
|
|
36
|
+
[RUN_TIMING_CHECKPOINT_NAME, RUN_USAGE_CHECKPOINT_NAME].includes(argsHash) ||
|
|
37
|
+
argsHash === WORKFLOW_HEARTBEAT_ANCHOR_CHECKPOINT_NAME
|
|
38
|
+
);
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
import {
|
|
@@ -458,7 +458,7 @@ function stageRunTopology(
|
|
|
458
458
|
};
|
|
459
459
|
}
|
|
460
460
|
|
|
461
|
-
function isModelAttempts(value:
|
|
461
|
+
export function isModelAttempts(value: unknown): boolean {
|
|
462
462
|
return (
|
|
463
463
|
Array.isArray(value) &&
|
|
464
464
|
value.every((attempt) => {
|
|
@@ -23,6 +23,8 @@ export interface WorkflowRunResumeCandidate {
|
|
|
23
23
|
/** Explicitly false when durable state or referenced artifacts are missing. */
|
|
24
24
|
readonly hasDurableCheckpoint?: boolean;
|
|
25
25
|
readonly artifactsIntact?: boolean;
|
|
26
|
+
/** True only for the engine-owned budget_exceeded blocked rail. */
|
|
27
|
+
readonly budgetSystemOwnedStop?: boolean;
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
/** Derive the paused/blocked state used by every workflow resume surface. */
|
|
@@ -46,9 +48,9 @@ export function isWorkflowRunResumable(candidate: WorkflowRunResumeCandidate): b
|
|
|
46
48
|
}
|
|
47
49
|
return (
|
|
48
50
|
(candidate.status === "failed" && candidate.endedAt !== undefined && candidate.resumable !== false) ||
|
|
49
|
-
(candidate.
|
|
50
|
-
candidate.
|
|
51
|
-
candidate.
|
|
51
|
+
(candidate.resumable === true &&
|
|
52
|
+
candidate.failureRecoverability === "recoverable" &&
|
|
53
|
+
(candidate.endedAt === undefined || candidate.budgetSystemOwnedStop === true))
|
|
52
54
|
);
|
|
53
55
|
}
|
|
54
56
|
|
|
@@ -16,13 +16,16 @@
|
|
|
16
16
|
* cross-ref: packages/workflows/src/shared/timing.ts elapsedRunMs
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import
|
|
19
|
+
import { restoreBudgetState } from "../shared/persistence-restore-helpers.js";
|
|
20
|
+
import type { RunBudgetAccountingState, RunSnapshot } from "../shared/store-types.js";
|
|
20
21
|
import { elapsedRunMs } from "../shared/timing.js";
|
|
21
22
|
import type { DurableWorkflowBackend } from "./backend.js";
|
|
23
|
+
import { durableHash } from "./durable-hash.js";
|
|
22
24
|
import type { DurableToolCheckpoint } from "./types.js";
|
|
23
25
|
|
|
24
|
-
/** Reserved checkpoint
|
|
26
|
+
/** Reserved checkpoint names AND args-hashes for run-level meter records. */
|
|
25
27
|
export const RUN_TIMING_CHECKPOINT_NAME = "workflow-run-timing";
|
|
28
|
+
export const RUN_USAGE_CHECKPOINT_NAME = "workflow-run-usage";
|
|
26
29
|
|
|
27
30
|
/**
|
|
28
31
|
* Debounce granularity for run-timing updates, matching the stage-session
|
|
@@ -43,34 +46,43 @@ export function priorRunElapsedMs(backend: DurableWorkflowBackend, workflowId: s
|
|
|
43
46
|
return elapsedMs;
|
|
44
47
|
}
|
|
45
48
|
|
|
49
|
+
/** Prior token/cost accounting recorded durably, or undefined when absent/malformed. */
|
|
50
|
+
export function priorRunAccounting(
|
|
51
|
+
backend: DurableWorkflowBackend,
|
|
52
|
+
workflowId: string,
|
|
53
|
+
): RunBudgetAccountingState | undefined {
|
|
54
|
+
const output = backend.getToolOutput(workflowId, RUN_USAGE_CHECKPOINT_NAME);
|
|
55
|
+
if (typeof output !== "object" || output === null || Array.isArray(output)) return undefined;
|
|
56
|
+
return restoreBudgetState({ accounting: (output as Record<string, unknown>).accounting })?.accounting;
|
|
57
|
+
}
|
|
46
58
|
/**
|
|
47
59
|
* Record the run's current total elapsed time (prior + this session) durably.
|
|
48
60
|
*
|
|
49
61
|
* Skipped when the workflow has no durable progress yet (a timing record with
|
|
50
62
|
* nothing to resume would only manufacture resumability), when the elapsed
|
|
51
63
|
* value did not grow past the last record, or — with `debounce` — while the
|
|
52
|
-
* value stays inside the last 30 s bucket.
|
|
64
|
+
* value stays inside the last 30 s bucket. Usage shares only the progress guard.
|
|
53
65
|
*/
|
|
54
66
|
export function recordRunTimingCheckpoint(
|
|
55
67
|
backend: DurableWorkflowBackend,
|
|
56
68
|
run: RunSnapshot,
|
|
57
69
|
options?: { readonly debounce?: boolean; readonly now?: number },
|
|
58
70
|
): boolean {
|
|
59
|
-
const
|
|
60
|
-
if (checkpoint === undefined) return false;
|
|
61
|
-
backend.recordCheckpoint(checkpoint);
|
|
71
|
+
const checkpoints = [runTimingCheckpoint(backend, run, options), runUsageCheckpoint(backend, run, options?.now)];
|
|
72
|
+
if (checkpoints.every((checkpoint) => checkpoint === undefined)) return false;
|
|
73
|
+
for (const checkpoint of checkpoints) if (checkpoint !== undefined) backend.recordCheckpoint(checkpoint);
|
|
62
74
|
return true;
|
|
63
75
|
}
|
|
64
76
|
|
|
65
|
-
/** Await the timing
|
|
77
|
+
/** Await the timing and usage writes so an active turn observes persistent storage faults at once. */
|
|
66
78
|
export async function recordRunTimingCheckpointAsync(
|
|
67
79
|
backend: DurableWorkflowBackend,
|
|
68
80
|
run: RunSnapshot,
|
|
69
81
|
options?: { readonly debounce?: boolean; readonly now?: number },
|
|
70
82
|
): Promise<boolean> {
|
|
71
|
-
const
|
|
72
|
-
if (checkpoint === undefined) return false;
|
|
73
|
-
await backend.recordCheckpointAsync(checkpoint);
|
|
83
|
+
const checkpoints = [runTimingCheckpoint(backend, run, options), runUsageCheckpoint(backend, run, options?.now)];
|
|
84
|
+
if (checkpoints.every((checkpoint) => checkpoint === undefined)) return false;
|
|
85
|
+
for (const checkpoint of checkpoints) if (checkpoint !== undefined) await backend.recordCheckpointAsync(checkpoint);
|
|
74
86
|
return true;
|
|
75
87
|
}
|
|
76
88
|
|
|
@@ -98,6 +110,25 @@ function runTimingCheckpoint(
|
|
|
98
110
|
completedAt: now,
|
|
99
111
|
};
|
|
100
112
|
}
|
|
113
|
+
function runUsageCheckpoint(
|
|
114
|
+
backend: DurableWorkflowBackend,
|
|
115
|
+
run: RunSnapshot,
|
|
116
|
+
now = Date.now(),
|
|
117
|
+
): DurableToolCheckpoint | undefined {
|
|
118
|
+
const accounting = run.budgetState?.accounting;
|
|
119
|
+
if (accounting === undefined || backend.listCheckpoints(run.id).length === 0) return undefined;
|
|
120
|
+
const checkpointId = `run-usage:${durableHash({ accounting })}`;
|
|
121
|
+
if (backend.getToolCheckpoint(run.id, RUN_USAGE_CHECKPOINT_NAME)?.checkpointId === checkpointId) return undefined;
|
|
122
|
+
return {
|
|
123
|
+
kind: "tool",
|
|
124
|
+
workflowId: run.id,
|
|
125
|
+
checkpointId,
|
|
126
|
+
name: RUN_USAGE_CHECKPOINT_NAME,
|
|
127
|
+
argsHash: RUN_USAGE_CHECKPOINT_NAME,
|
|
128
|
+
output: { accounting },
|
|
129
|
+
completedAt: now,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
101
132
|
|
|
102
133
|
/**
|
|
103
134
|
* Elapsed time a freshly-created run inherits from its predecessor:
|
|
@@ -101,7 +101,9 @@ export interface CreateToolPrimitiveInput {
|
|
|
101
101
|
* quit already closed admission for this workflow tree; the call never runs.
|
|
102
102
|
*/
|
|
103
103
|
readonly admitToolCall?: () => ToolCallAdmission;
|
|
104
|
-
/**
|
|
104
|
+
/** Deterministic boundary hooks before dispatch and after a tool node settles. */
|
|
105
|
+
readonly beforeToolCall?: () => void | Promise<void>;
|
|
106
|
+
readonly afterToolCall?: () => void | Promise<void>;
|
|
105
107
|
readonly trackExecution?: <T>(execution: Promise<T>) => WorkflowToolExecutionAdmission | undefined;
|
|
106
108
|
/** Observe a logical throwing-mode failure before graph publication or promise rejection. */
|
|
107
109
|
readonly onFailureObserved?: (error: unknown, nodeId: string) => void;
|
|
@@ -185,11 +187,28 @@ export function createToolPrimitive(input: CreateToolPrimitiveInput): WorkflowTo
|
|
|
185
187
|
noteCancelled: () => admission?.noteCancelled?.(),
|
|
186
188
|
releaseAdmission: () => lease?.release(),
|
|
187
189
|
};
|
|
190
|
+
const settleAfterBoundary = (settle: () => void): void => {
|
|
191
|
+
let afterToolCall: void | Promise<void>;
|
|
192
|
+
try {
|
|
193
|
+
afterToolCall = input.afterToolCall?.();
|
|
194
|
+
} catch (error) {
|
|
195
|
+
rejectExecution(error);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (afterToolCall === undefined) {
|
|
199
|
+
settle();
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
void afterToolCall.then(settle, rejectExecution);
|
|
203
|
+
};
|
|
188
204
|
void executeToolInvocation(input, ordinals, name, args, fn, options, control, captureCallbackSource(fn))
|
|
189
205
|
// Backstop for a throw before either explicit release point; the lease
|
|
190
206
|
// release itself is idempotent.
|
|
191
207
|
.finally(() => lease?.release())
|
|
192
|
-
.then(
|
|
208
|
+
.then(
|
|
209
|
+
(value) => settleAfterBoundary(() => resolveExecution(value)),
|
|
210
|
+
(error) => settleAfterBoundary(() => rejectExecution(error)),
|
|
211
|
+
);
|
|
193
212
|
return execution;
|
|
194
213
|
}) as WorkflowToolPrimitive;
|
|
195
214
|
}
|
|
@@ -204,6 +223,9 @@ async function executeToolInvocation<T extends WorkflowSerializableValue>(
|
|
|
204
223
|
control: ToolInvocationAdmissionControl,
|
|
205
224
|
source: string | undefined,
|
|
206
225
|
): Promise<WorkflowToolInvocationResult<T>> {
|
|
226
|
+
input.throwIfCancelled();
|
|
227
|
+
const beforeToolCall = input.beforeToolCall?.();
|
|
228
|
+
if (beforeToolCall !== undefined) await beforeToolCall;
|
|
207
229
|
input.throwIfCancelled();
|
|
208
230
|
if (
|
|
209
231
|
options?.retriesAllowed === true &&
|