gateproof 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +132 -350
- package/dist/act.d.ts +45 -0
- package/dist/act.d.ts.map +1 -1
- package/dist/act.js +22 -0
- package/dist/act.js.map +1 -1
- package/dist/action-executors.d.ts +17 -0
- package/dist/action-executors.d.ts.map +1 -1
- package/dist/action-executors.js +60 -0
- package/dist/action-executors.js.map +1 -1
- package/dist/assert.d.ts +20 -0
- package/dist/assert.d.ts.map +1 -1
- package/dist/assert.js +32 -0
- package/dist/assert.js.map +1 -1
- package/dist/authority.d.ts +34 -0
- package/dist/authority.d.ts.map +1 -0
- package/dist/authority.js +141 -0
- package/dist/authority.js.map +1 -0
- package/dist/cli/gateproof.js +76 -0
- package/dist/cli/gateproof.js.map +1 -1
- package/dist/filepath-backend.d.ts +64 -0
- package/dist/filepath-backend.d.ts.map +1 -0
- package/dist/filepath-backend.js +126 -0
- package/dist/filepath-backend.js.map +1 -0
- package/dist/filepath-protocol.d.ts +214 -0
- package/dist/filepath-protocol.d.ts.map +1 -0
- package/dist/filepath-protocol.js +239 -0
- package/dist/filepath-protocol.js.map +1 -0
- package/dist/filepath-runtime.d.ts +100 -0
- package/dist/filepath-runtime.d.ts.map +1 -0
- package/dist/filepath-runtime.js +190 -0
- package/dist/filepath-runtime.js.map +1 -0
- package/dist/http-backend.d.ts +9 -0
- package/dist/http-backend.d.ts.map +1 -1
- package/dist/http-backend.js +50 -8
- package/dist/http-backend.js.map +1 -1
- package/dist/index.d.ts +11 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/prd/index.d.ts +2 -0
- package/dist/prd/index.d.ts.map +1 -1
- package/dist/prd/index.js +4 -0
- package/dist/prd/index.js.map +1 -1
- package/dist/prd/loop.d.ts +160 -0
- package/dist/prd/loop.d.ts.map +1 -0
- package/dist/prd/loop.js +462 -0
- package/dist/prd/loop.js.map +1 -0
- package/dist/prd/runner.d.ts +2 -5
- package/dist/prd/runner.d.ts.map +1 -1
- package/dist/prd/runner.js +154 -122
- package/dist/prd/runner.js.map +1 -1
- package/dist/prd/scope-defaults.d.ts +75 -0
- package/dist/prd/scope-defaults.d.ts.map +1 -0
- package/dist/prd/scope-defaults.js +235 -0
- package/dist/prd/scope-defaults.js.map +1 -0
- package/dist/prd/types.d.ts +79 -0
- package/dist/prd/types.d.ts.map +1 -1
- package/dist/report.d.ts +70 -0
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +183 -0
- package/dist/report.js.map +1 -1
- package/package.json +10 -2
package/README.md
CHANGED
|
@@ -1,279 +1,89 @@
|
|
|
1
1
|
# gateproof
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Software is built in reverse. You know what you want before you know how to get there. TDD proved the idea: write the test first, then make it pass. Gateproof takes the next step.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Write stories. Attach gates. Let agents iterate until reality matches intent.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
A **gate** observes real evidence (logs, telemetry), acts (browser, shell, deploy), and asserts outcomes. A **story** is a gate with a name and a place in a plan. A **prd.ts** is a list of stories in dependency order. The agent's only job is to make the next failing gate pass.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
1. PRD defines stories (what should exist)
|
|
11
|
-
2. Gates verify reality (does it work?)
|
|
12
|
-
3. Agent gets PRD + gate failure (minimal context)
|
|
13
|
-
4. Agent fixes, gates re-run
|
|
14
|
-
5. Iterate until all gates pass
|
|
9
|
+
## The thesis
|
|
15
10
|
|
|
16
|
-
|
|
17
|
-
- PRD is single source of truth (clear intent, minimal context)
|
|
18
|
-
- Gates provide concrete feedback (not vague requirements)
|
|
19
|
-
- Agent gets context only when needed (efficient)
|
|
20
|
-
- Iteration ensures correctness (converges to working code)
|
|
11
|
+
Plans are solid. Implementation is liquid.
|
|
21
12
|
|
|
22
|
-
|
|
13
|
+
Any codebase can be scoped down to stories and a `prd.ts`. Multiple agents can work the same plan, falling through the same checkpoints. Once a gate passes, previous work can't break -- the gate proves it. The skill shifts from writing code to defining the right guardrails.
|
|
23
14
|
|
|
24
|
-
|
|
25
|
-
- **PRD (`prd.ts`)** — authority on intent, order, and dependencies (if you use the PRD runner)
|
|
26
|
-
- **Gate implementations** — authority on how reality is observed
|
|
27
|
-
- **gateproof runtime** — authority on enforcement only
|
|
15
|
+
Gates are checkpoints that keep agents safe. They don't decide intent. They verify reality.
|
|
28
16
|
|
|
29
|
-
|
|
17
|
+
## Why this works
|
|
30
18
|
|
|
31
|
-
|
|
19
|
+
Formal verification research established that the relationship between a specification and its implementation — called **refinement** — is itself a testable property. You don't need a theorem prover to get value from this idea. You can test refinement cheaply by running the system and checking that its behavior satisfies the spec.
|
|
32
20
|
|
|
33
|
-
|
|
21
|
+
Gateproof distills this into three primitives:
|
|
34
22
|
|
|
35
|
-
**
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
- Save and run: `bun run prd.ts`.
|
|
23
|
+
1. **Observe** — collect real evidence (logs, telemetry) from a running system
|
|
24
|
+
2. **Act** — trigger real behavior (browser navigation, shell commands, deploys)
|
|
25
|
+
3. **Assert** — check that the evidence satisfies the specification
|
|
39
26
|
|
|
40
|
-
|
|
41
|
-
```text
|
|
42
|
-
@prdts-maker Create prd.ts for:
|
|
43
|
-
- User can sign up
|
|
44
|
-
- Email verification works (depends on signup)
|
|
45
|
-
- User can log in (depends on verification)
|
|
46
|
-
Include gate files under ./gates/.
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## CLI: npx gateproof prdts
|
|
27
|
+
Each gate is a refinement check: does the running system's behavior refine what the story claims? The PRD orders these checks by dependency, so failures localize to the first broken obligation.
|
|
50
28
|
|
|
51
|
-
|
|
29
|
+
This is a deliberate simplification. We trade random input generation and exhaustive coverage for something an engineer can write in minutes and an agent can iterate against in a loop. The gate is the contract. The loop is the proof search.
|
|
52
30
|
|
|
53
|
-
|
|
54
|
-
echo "Build a signup flow with email verification and login" | npx gateproof prdts --stdout
|
|
55
|
-
npx gateproof prdts --in stories.txt --out prd.ts
|
|
56
|
-
```
|
|
31
|
+
> Lineage: the *observe → act → assert* pattern draws on property-based testing ideas from [Chen, Rizkallah et al. — "Property-Based Testing: Climbing the Stairway to Verification" (SLE 2022)](https://doi.org/10.1145/3567512.3567520), which demonstrated that refinement properties can serve as a practical, incremental path toward verified systems.
|
|
57
32
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
Paste mode (interactive stdin):
|
|
33
|
+
## Install
|
|
61
34
|
|
|
62
35
|
```bash
|
|
63
|
-
|
|
64
|
-
# paste a prompt, then Ctrl-D
|
|
36
|
+
bun add gateproof
|
|
65
37
|
```
|
|
66
38
|
|
|
67
|
-
|
|
39
|
+
## Minimal gate
|
|
68
40
|
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
```
|
|
41
|
+
```ts
|
|
42
|
+
import { Gate, Act, Assert, createHttpObserveResource } from "gateproof";
|
|
72
43
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
5. All gates pass → done
|
|
83
|
-
|
|
84
|
-
**Why minimal context:**
|
|
85
|
-
- Agent starts with PRD only (no full codebase upfront)
|
|
86
|
-
- Agent gets context only when gates fail (just-in-time)
|
|
87
|
-
- PRD stays as authority (what to build)
|
|
88
|
-
- Gates provide concrete feedback (what's wrong)
|
|
44
|
+
const result = await Gate.run({
|
|
45
|
+
name: "post-deploy",
|
|
46
|
+
observe: createHttpObserveResource({
|
|
47
|
+
url: "https://api.example.com/health",
|
|
48
|
+
}),
|
|
49
|
+
act: [Act.wait(500)],
|
|
50
|
+
assert: [Assert.noErrors()],
|
|
51
|
+
stop: { maxMs: 10_000 },
|
|
52
|
+
});
|
|
89
53
|
|
|
90
|
-
|
|
91
|
-
```bash
|
|
92
|
-
# patterns/prd/agent-iteration-loop.sh
|
|
93
|
-
while true; do
|
|
94
|
-
bun run prd.ts || {
|
|
95
|
-
# Gate failed - agent gets PRD + failure output
|
|
96
|
-
agent --context prd.ts --failure "$(cat gate-output.txt)"
|
|
97
|
-
# Agent fixes, loop continues
|
|
98
|
-
}
|
|
99
|
-
break # All gates passed
|
|
100
|
-
done
|
|
54
|
+
if (result.status !== "success") process.exit(1);
|
|
101
55
|
```
|
|
102
56
|
|
|
103
|
-
|
|
104
|
-
- Max failures (default: 5) → auto-pause if stuck
|
|
105
|
-
- Git diff check → agent must make changes
|
|
106
|
-
- Pause file → manual control
|
|
107
|
-
|
|
108
|
-
This solves the context management problem: agents don't need full codebase context upfront. They get minimal context (PRD), concrete feedback (gate failures), and iterate until correct.
|
|
109
|
-
|
|
110
|
-
## Anatomy of a prd.ts (1 list)
|
|
111
|
-
|
|
112
|
-
1. **Instructions**: each story title encodes behavior + evidence + scope (the agent's marching orders).
|
|
113
|
-
2. **Stories**: `stories[]` holds `{ id, title, gateFile, dependsOn?, progress? }` in execution order.
|
|
114
|
-
3. **Gates**: `gateFile` points at a gate script that observes logs, acts, and asserts evidence.
|
|
115
|
-
4. **Loop state**: `runPrd(...)` returns success or the `failedStory` plus gate evidence (actions/stages/errors).
|
|
116
|
-
5. **Loop instructions**: on failure, feed the agent `prd.ts` + gate output, fix code, re-run PRD until pass.
|
|
117
|
-
|
|
118
|
-
## Stories as gates
|
|
119
|
-
|
|
120
|
-
A PRD (Product Requirements Document) defines stories. Stories are gates. Each story references a gate file. The gate file verifies the story against reality.
|
|
121
|
-
|
|
122
|
-
Reality is the source of truth; gates make it enforceable in CI.
|
|
57
|
+
## Stories + PRD
|
|
123
58
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
```typescript
|
|
127
|
-
// prd.ts
|
|
128
|
-
import { definePrd } from "gateproof/prd";
|
|
59
|
+
```ts
|
|
60
|
+
import { definePrd, runPrd } from "gateproof/prd";
|
|
129
61
|
|
|
130
|
-
|
|
62
|
+
const prd = definePrd({
|
|
131
63
|
stories: [
|
|
132
64
|
{
|
|
133
65
|
id: "user-signup",
|
|
134
|
-
title: "User can sign up",
|
|
135
|
-
gateFile: "./gates/
|
|
136
|
-
progress: ["signup_page_live", "user_created"],
|
|
66
|
+
title: "User can sign up with email",
|
|
67
|
+
gateFile: "./gates/signup.gate.ts",
|
|
137
68
|
},
|
|
138
69
|
{
|
|
139
70
|
id: "email-verification",
|
|
140
71
|
title: "User receives verification email",
|
|
141
|
-
gateFile: "./gates/
|
|
72
|
+
gateFile: "./gates/verify.gate.ts",
|
|
142
73
|
dependsOn: ["user-signup"],
|
|
143
|
-
progress: ["email_sent", "verification_link_valid"],
|
|
144
74
|
},
|
|
145
|
-
] as const,
|
|
75
|
+
] as const,
|
|
146
76
|
});
|
|
147
77
|
|
|
148
|
-
|
|
149
|
-
if (
|
|
150
|
-
const { runPrd } = await import("gateproof/prd");
|
|
151
|
-
const result = await runPrd(prd);
|
|
152
|
-
if (!result.success) {
|
|
153
|
-
if (result.failedStory) console.error(`Failed at: ${result.failedStory.id}`);
|
|
154
|
-
process.exit(1);
|
|
155
|
-
}
|
|
156
|
-
process.exit(0);
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
Each story references a gate file. The gate file uses gateproof's API:
|
|
161
|
-
|
|
162
|
-
```typescript
|
|
163
|
-
// gates/user-signup.gate.ts
|
|
164
|
-
import { Gate, Act, Assert } from "gateproof";
|
|
165
|
-
import { CloudflareProvider } from "gateproof/cloudflare";
|
|
166
|
-
|
|
167
|
-
export async function run() {
|
|
168
|
-
const provider = CloudflareProvider({
|
|
169
|
-
accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
|
|
170
|
-
apiToken: process.env.CLOUDFLARE_API_TOKEN!,
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
const result = await Gate.run({
|
|
174
|
-
name: "user-signup",
|
|
175
|
-
observe: provider.observe({ backend: "analytics", dataset: "worker_logs" }),
|
|
176
|
-
act: [Act.browser({ url: "https://app.example.com/signup" })],
|
|
177
|
-
assert: [
|
|
178
|
-
Assert.noErrors(),
|
|
179
|
-
Assert.hasAction("user_created"),
|
|
180
|
-
],
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
return { status: result.status };
|
|
184
|
-
}
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
**gateproof does not own your PRD’s intent or state.** If you choose to use `gateproof/prd`, your PRD must match a small capsule shape (`stories[]` with `id/title/gateFile/dependsOn?/progress?`). The optional `progress` list is for your own tracking (or agent guidance); gateproof does not interpret or mutate it. Otherwise, orchestrate gates however you want — gateproof only cares about executing gate files.
|
|
188
|
-
|
|
189
|
-
Stories execute in dependency order. The runner stops on first failure. Progress is not declared. It is proven.
|
|
190
|
-
|
|
191
|
-
## How it works
|
|
192
|
-
|
|
193
|
-
The PRD defines stories. Stories reference gate files. Gate files use gateproof's API. Gates can be enforced in CI before merge/deploy.
|
|
194
|
-
|
|
195
|
-
**The sequence:** PRD story → gate file → gate execution → story marked "done" only when gate passes.
|
|
196
|
-
|
|
197
|
-
**For agent iterations:** PRD → gate fails → agent fixes → gate re-runs → loop until pass.
|
|
198
|
-
|
|
199
|
-
Run your PRD:
|
|
200
|
-
|
|
201
|
-
```bash
|
|
202
|
-
bun run prd.ts
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
Run agent iteration loop:
|
|
206
|
-
|
|
207
|
-
```bash
|
|
208
|
-
bash patterns/prd/agent-iteration-loop.sh
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
## Hardening `prd.ts` (recommended)
|
|
212
|
-
|
|
213
|
-
Treat `prd.ts` like code: typecheck + validate before push + enforce in CI.
|
|
214
|
-
|
|
215
|
-
- **Validate PRD**:
|
|
216
|
-
|
|
217
|
-
```bash
|
|
218
|
-
bun run prd:validate
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
- **Pre-push (default for everyone on your team)**: add to your `prepush` script (Husky calls it).
|
|
222
|
-
|
|
223
|
-
```json
|
|
224
|
-
{
|
|
225
|
-
"scripts": {
|
|
226
|
-
"prepush": "bun run typecheck && bun run prd:validate && bun test"
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
- **CI**: run the validator before running PRD/tests.
|
|
232
|
-
|
|
233
|
-
```yaml
|
|
234
|
-
- name: Validate PRD
|
|
235
|
-
run: bun run prd:validate
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
- **Monorepo**: validate any PRD file by path.
|
|
239
|
-
|
|
240
|
-
```bash
|
|
241
|
-
bun run scripts/prd-validate.ts packages/api/prd.ts
|
|
78
|
+
const result = await runPrd(prd);
|
|
79
|
+
if (!result.success) process.exit(1);
|
|
242
80
|
```
|
|
243
81
|
|
|
244
|
-
##
|
|
245
|
-
|
|
246
|
-
- [Effect and Schema: Gateproof's Foundation](docs/effect-and-schema.md)
|
|
247
|
-
|
|
248
|
-
## Writing good gates (agent-first)
|
|
249
|
-
|
|
250
|
-
Gates can fail loudly. They can also pass on silence if you write weak assertions.
|
|
82
|
+
## Assertions
|
|
251
83
|
|
|
252
|
-
|
|
253
|
-
- **Don’t rely on absence-only checks**: `Assert.noErrors()` alone can pass if you collect no logs.
|
|
254
|
-
- **Treat observability as part of the system**: your confidence is bounded by what you can observe.
|
|
84
|
+
`Assert.noErrors()`, `Assert.hasAction(name)`, `Assert.hasStage(name)`, `Assert.custom(name, fn)`, `Assert.authority(policy)`.
|
|
255
85
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
- **Not a planner or orchestrator**: gateproof executes gates; your PRD (or CI) decides what to run and in what context.
|
|
259
|
-
- **Not a truth oracle**: if your backend drops logs, a gate can be wrong. Gateproof can’t fix missing telemetry.
|
|
260
|
-
- **Enforcement is external**: gateproof returns results; CI/CD decides whether to block merge/deploy.
|
|
261
|
-
|
|
262
|
-
## Common objections (and answers)
|
|
263
|
-
|
|
264
|
-
- **"Isn't this just E2E tests?"** Similar goal, different anchor. Gates are evidence-first (logs/telemetry + explicit assertions), not DOM-only. The contract is: observe → act → assert → evidence.
|
|
265
|
-
|
|
266
|
-
- **"What about flaky telemetry?"** Gates don't fix missing telemetry. They make the dependency explicit. If your backend drops logs, a gate can be wrong — but you'll know immediately, not in production.
|
|
267
|
-
|
|
268
|
-
- **"Isn't this overhead?"** It can be. The pitch isn't "gate everything." It's "gate the few transitions that are expensive to get wrong." Start with one critical path.
|
|
269
|
-
|
|
270
|
-
- **"Will this lock us in?"** Gates are just TypeScript files. If you stop using gateproof, you keep the scripts and the intent. No vendor lock-in.
|
|
271
|
-
|
|
272
|
-
## Quick Start
|
|
273
|
-
|
|
274
|
-
The API is minimal: three concepts (Gate, Act, Assert). Here's a gate:
|
|
275
|
-
|
|
276
|
-
```typescript
|
|
86
|
+
```ts
|
|
277
87
|
import { Gate, Act, Assert } from "gateproof";
|
|
278
88
|
import { CloudflareProvider } from "gateproof/cloudflare";
|
|
279
89
|
|
|
@@ -283,157 +93,129 @@ const provider = CloudflareProvider({
|
|
|
283
93
|
});
|
|
284
94
|
|
|
285
95
|
const result = await Gate.run({
|
|
286
|
-
name: "
|
|
96
|
+
name: "checkout-flow",
|
|
287
97
|
observe: provider.observe({ backend: "analytics", dataset: "worker_logs" }),
|
|
288
|
-
act: [Act.browser({ url: "https://
|
|
289
|
-
assert: [
|
|
98
|
+
act: [Act.browser({ url: "https://app.example.com/checkout" })],
|
|
99
|
+
assert: [
|
|
100
|
+
Assert.noErrors(),
|
|
101
|
+
Assert.hasAction("checkout_started"),
|
|
102
|
+
Assert.custom("has-total", (logs) => logs.some(l => (l as { data?: { total?: number } }).data?.total > 0)),
|
|
103
|
+
],
|
|
104
|
+
stop: { maxMs: 15_000 },
|
|
290
105
|
});
|
|
291
|
-
|
|
292
106
|
if (result.status !== "success") process.exit(1);
|
|
293
107
|
```
|
|
294
108
|
|
|
295
|
-
|
|
109
|
+
## Agent gates
|
|
296
110
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
### Gate.run(spec)
|
|
300
|
-
Run a gate. Returns a result with status, logs, and evidence.
|
|
301
|
-
`spec.name` is optional metadata for labeling a gate.
|
|
302
|
-
|
|
303
|
-
### Actions
|
|
304
|
-
```typescript
|
|
305
|
-
Act.exec("command") // Run shell command
|
|
306
|
-
Act.browser({ url, headless? }) // Browser automation (needs playwright)
|
|
307
|
-
Act.wait(ms) // Sleep
|
|
308
|
-
Act.deploy({ worker }) // Deploy marker
|
|
309
|
-
```
|
|
111
|
+
Spawn an AI agent in an isolated container, observe its NDJSON event stream, and assert what it's allowed to do.
|
|
310
112
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
113
|
+
```ts
|
|
114
|
+
import { Gate, Act, Assert } from "gateproof";
|
|
115
|
+
import { setFilepathRuntime, CloudflareSandboxRuntime } from "gateproof";
|
|
116
|
+
import { getSandbox } from "@cloudflare/sandbox";
|
|
117
|
+
|
|
118
|
+
// 1. Wire up your container runtime (once at startup)
|
|
119
|
+
setFilepathRuntime(new CloudflareSandboxRuntime({
|
|
120
|
+
getSandbox: (config) => getSandbox(env.Sandbox, `agent-${config.name}`),
|
|
121
|
+
}));
|
|
122
|
+
|
|
123
|
+
// 2. Run the gate
|
|
124
|
+
const container = await runtime.spawn({
|
|
125
|
+
name: "fix-auth",
|
|
126
|
+
agent: "claude-code",
|
|
127
|
+
model: "claude-sonnet-4-20250514",
|
|
128
|
+
task: "Fix the null pointer in src/auth.ts",
|
|
129
|
+
});
|
|
318
130
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
{
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
131
|
+
const observe = createFilepathObserveResource(container, "fix-auth");
|
|
132
|
+
|
|
133
|
+
await Gate.run({
|
|
134
|
+
name: "fix-auth-bug",
|
|
135
|
+
observe,
|
|
136
|
+
act: [Act.wait(300_000)],
|
|
137
|
+
assert: [
|
|
138
|
+
Assert.noErrors(),
|
|
139
|
+
Assert.hasAction("commit"),
|
|
140
|
+
Assert.hasAction("done"),
|
|
141
|
+
Assert.authority({
|
|
142
|
+
canCommit: true,
|
|
143
|
+
canSpawn: false,
|
|
144
|
+
forbiddenTools: ["delete_file"],
|
|
145
|
+
}),
|
|
146
|
+
],
|
|
147
|
+
stop: { idleMs: 5000, maxMs: 300_000 },
|
|
148
|
+
});
|
|
333
149
|
```
|
|
334
150
|
|
|
335
|
-
|
|
151
|
+
`Assert.authority()` enforces governance policies against the agent's actual behavior — what it committed, spawned, and which tools it used.
|
|
336
152
|
|
|
337
|
-
|
|
153
|
+
## Writing good gates
|
|
338
154
|
|
|
339
|
-
|
|
340
|
-
import { definePrd, runPrd } from "gateproof/prd";
|
|
155
|
+
The hardest part of gateproof is not the library — it's writing gates that actually prove what you think they prove.
|
|
341
156
|
|
|
342
|
-
|
|
343
|
-
stories: [
|
|
344
|
-
{
|
|
345
|
-
id: "story-1",
|
|
346
|
-
title: "First story",
|
|
347
|
-
gateFile: "./gates/story-1.gate.ts",
|
|
348
|
-
},
|
|
349
|
-
{
|
|
350
|
-
id: "story-2",
|
|
351
|
-
title: "Second story",
|
|
352
|
-
gateFile: "./gates/story-2.gate.ts",
|
|
353
|
-
dependsOn: ["story-1"],
|
|
354
|
-
},
|
|
355
|
-
] as const, // keep story IDs as literal types
|
|
356
|
-
});
|
|
157
|
+
**A weak gate passes on silence.** If your system emits no logs and your only assertion is `Assert.noErrors()`, the gate passes vacuously. Nothing was tested. Use `requirePositiveSignal: true` on stories, or assert specific evidence (`Assert.hasAction`, `Assert.hasStage`).
|
|
357
158
|
|
|
358
|
-
|
|
359
|
-
if (!result.success) {
|
|
360
|
-
console.error(`Failed at: ${result.failedStory?.id}`);
|
|
361
|
-
process.exit(1);
|
|
362
|
-
}
|
|
363
|
-
```
|
|
159
|
+
**A good gate is falsifiable.** Ask: "what broken implementation would still pass this gate?" If the answer is "many," the gate is too weak. Tighten it until a broken system fails.
|
|
364
160
|
|
|
365
|
-
|
|
366
|
-
- Validates dependencies (unknown IDs and cycles throw)
|
|
367
|
-
- Topologically sorts stories by `dependsOn`
|
|
368
|
-
- Executes gates in order
|
|
369
|
-
- **Stops on first failure**
|
|
161
|
+
**Start narrow, then widen.** One specific assertion that catches a real failure is worth more than ten vague ones. You can always add assertions later — you can't take back a false pass.
|
|
370
162
|
|
|
371
|
-
##
|
|
163
|
+
## The loop
|
|
372
164
|
|
|
373
|
-
|
|
165
|
+
Gate fails. Agent reads the failure evidence. Agent fixes code. Gate re-runs. Loop until pass.
|
|
374
166
|
|
|
375
|
-
|
|
376
|
-
interface Backend {
|
|
377
|
-
start(): Effect.Effect<LogStream, ObservabilityError>;
|
|
378
|
-
stop(): Effect.Effect<void, ObservabilityError>;
|
|
379
|
-
}
|
|
380
|
-
```
|
|
167
|
+
**Bring your own agent** — the loop takes any async function:
|
|
381
168
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
- Cloudflare Workers Logs API
|
|
385
|
-
- CLI Stream (local dev)
|
|
386
|
-
- Custom backends
|
|
169
|
+
```ts
|
|
170
|
+
import { runPrdLoop } from "gateproof/prd";
|
|
387
171
|
|
|
388
|
-
|
|
172
|
+
await runPrdLoop("./prd.ts", {
|
|
173
|
+
agent: async (ctx) => {
|
|
174
|
+
// ctx.failureSummary — what failed and why
|
|
175
|
+
// ctx.recentDiff — recent git changes
|
|
176
|
+
// ctx.prdContent — full PRD for context
|
|
177
|
+
// ctx.failedStory — the Story object that failed
|
|
178
|
+
// ctx.signal — AbortSignal for cancellation
|
|
389
179
|
|
|
390
|
-
|
|
391
|
-
const
|
|
180
|
+
// Use any agent: Claude Code, Cursor, Codex, custom LLM wrapper
|
|
181
|
+
const result = await yourAgent.fix(ctx.failureSummary);
|
|
182
|
+
return { changes: result.files, commitMsg: "fix: resolve failing gate" };
|
|
183
|
+
},
|
|
184
|
+
maxIterations: 5,
|
|
185
|
+
});
|
|
186
|
+
```
|
|
392
187
|
|
|
393
|
-
|
|
394
|
-
provider.observe({ backend: "analytics", dataset: "worker_logs" })
|
|
188
|
+
Or use a pre-built agent:
|
|
395
189
|
|
|
396
|
-
|
|
397
|
-
|
|
190
|
+
```ts
|
|
191
|
+
import { runPrdLoop, createOpenCodeAgent } from "gateproof/prd";
|
|
398
192
|
|
|
399
|
-
|
|
400
|
-
|
|
193
|
+
await runPrdLoop("./prd.ts", {
|
|
194
|
+
agent: createOpenCodeAgent({ apiKey: process.env.OPENCODE_ZEN_API_KEY }),
|
|
195
|
+
maxIterations: 7,
|
|
196
|
+
});
|
|
401
197
|
```
|
|
402
198
|
|
|
403
|
-
##
|
|
404
|
-
|
|
405
|
-
See `patterns/` for complete examples:
|
|
406
|
-
- `patterns/basic/` - Basic usage patterns
|
|
407
|
-
- `patterns/cloudflare/` - Cloudflare-specific patterns
|
|
408
|
-
- `patterns/ci-cd/` - CI/CD integration
|
|
409
|
-
- `patterns/advanced/` - Advanced patterns
|
|
410
|
-
- `patterns/prd/` - PRD-as-code + agent iteration loop examples
|
|
411
|
-
- `patterns/agent-first/` - Spec interview → PRD stories (agent-first)
|
|
412
|
-
- `examples/hello-world-agent/` - Minimal agent with 5 tools + end-to-end gates
|
|
413
|
-
|
|
414
|
-
Run the hello-world agent example (requires `OPENCODE_ZEN_API_KEY` and network access to `opencode.ai`):
|
|
199
|
+
## Generate a PRD from plain language
|
|
415
200
|
|
|
416
201
|
```bash
|
|
417
|
-
|
|
418
|
-
bun run examples/hello-world-agent/prd.ts
|
|
202
|
+
echo "Build a signup flow with email verification" | npx gateproof prdts --stdout
|
|
419
203
|
```
|
|
420
204
|
|
|
421
|
-
##
|
|
205
|
+
## End-to-end CLI pipeline
|
|
422
206
|
|
|
423
|
-
|
|
207
|
+
> Contributed by @grok
|
|
424
208
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
209
|
+
```bash
|
|
210
|
+
# Natural language → prd.ts → agent loop
|
|
211
|
+
echo "Build a signup flow with email verification" | npx gateproof prdts --out prd.ts
|
|
212
|
+
npx gateproof smoke ./prd.ts
|
|
213
|
+
bun run prd.ts
|
|
430
214
|
```
|
|
431
215
|
|
|
432
|
-
##
|
|
216
|
+
## Docs
|
|
433
217
|
|
|
434
|
-
|
|
435
|
-
- `playwright` (optional, for Act.browser)
|
|
436
|
-
- Cloudflare credentials (for CloudflareProvider, or bring your own backend)
|
|
218
|
+
Full documentation, tutorials, and API reference: [gateproof.dev/docs](https://gateproof.dev/docs)
|
|
437
219
|
|
|
438
220
|
## License
|
|
439
221
|
|
package/dist/act.d.ts
CHANGED
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent configuration for Filepath container execution.
|
|
3
|
+
*
|
|
4
|
+
* Specifies which agent runtime, model, and task to run inside
|
|
5
|
+
* an isolated container. The container communicates via NDJSON
|
|
6
|
+
* on stdout/stdin using the Filepath Agent Protocol (FAP).
|
|
7
|
+
*/
|
|
8
|
+
export interface AgentActConfig {
|
|
9
|
+
/** Display name for the agent (used in logs and tree UI) */
|
|
10
|
+
name: string;
|
|
11
|
+
/** Agent runtime: "claude-code" | "codex" | "cursor" | or a custom Docker image */
|
|
12
|
+
agent: string;
|
|
13
|
+
/** Model to use (e.g. "claude-sonnet-4-20250514", "gpt-4o") */
|
|
14
|
+
model: string;
|
|
15
|
+
/** Task description — sent as FILEPATH_TASK env var */
|
|
16
|
+
task: string;
|
|
17
|
+
/** Git repository URL to clone into /workspace */
|
|
18
|
+
repo?: string;
|
|
19
|
+
/** Additional environment variables for the container */
|
|
20
|
+
env?: Record<string, string>;
|
|
21
|
+
/** Timeout for the entire agent run in ms (default: 300_000 = 5 min) */
|
|
22
|
+
timeoutMs?: number;
|
|
23
|
+
}
|
|
1
24
|
export type Action = {
|
|
2
25
|
_tag: "Deploy";
|
|
3
26
|
worker: string;
|
|
@@ -14,6 +37,9 @@ export type Action = {
|
|
|
14
37
|
command: string;
|
|
15
38
|
cwd?: string;
|
|
16
39
|
timeoutMs?: number;
|
|
40
|
+
} | {
|
|
41
|
+
_tag: "Agent";
|
|
42
|
+
config: AgentActConfig;
|
|
17
43
|
};
|
|
18
44
|
export declare namespace Act {
|
|
19
45
|
function deploy(config: {
|
|
@@ -29,5 +55,24 @@ export declare namespace Act {
|
|
|
29
55
|
cwd?: string;
|
|
30
56
|
timeoutMs?: number;
|
|
31
57
|
}): Action;
|
|
58
|
+
/**
|
|
59
|
+
* Run an AI agent in a Filepath container.
|
|
60
|
+
*
|
|
61
|
+
* The agent executes in an isolated container with a git repo at /workspace.
|
|
62
|
+
* It communicates via the Filepath Agent Protocol (FAP) — NDJSON events on
|
|
63
|
+
* stdout that get mapped to Gateproof Log entries for gate assertions.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```ts
|
|
67
|
+
* Act.agent({
|
|
68
|
+
* name: "fix-auth",
|
|
69
|
+
* agent: "claude-code",
|
|
70
|
+
* model: "claude-sonnet-4-20250514",
|
|
71
|
+
* task: "Fix the authentication bug in src/auth.ts",
|
|
72
|
+
* repo: "https://github.com/org/repo",
|
|
73
|
+
* })
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
function agent(config: AgentActConfig): Action;
|
|
32
77
|
}
|
|
33
78
|
//# sourceMappingURL=act.d.ts.map
|
package/dist/act.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"act.d.ts","sourceRoot":"","sources":["../src/act.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,MAAM,GACd;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACrE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC5B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"act.d.ts","sourceRoot":"","sources":["../src/act.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,mFAAmF;IACnF,KAAK,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,MAAM,GACd;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACrE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC5B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACnE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,cAAc,CAAA;CAAE,CAAC;AAE9C,yBAAiB,GAAG,CAAC;IACnB,SAAgB,MAAM,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAEzD;IAED,SAAgB,OAAO,CAAC,MAAM,EAAE;QAC9B,GAAG,EAAE,MAAM,CAAC;QACZ,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,GAAG,MAAM,CAOT;IAED,SAAgB,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAEvC;IAED,SAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAEzF;IAED;;;;;;;;;;;;;;;;;OAiBG;IACH,SAAgB,KAAK,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAEpD;CACF"}
|
package/dist/act.js
CHANGED
|
@@ -21,5 +21,27 @@ export var Act;
|
|
|
21
21
|
return { _tag: "Exec", command, cwd: opts?.cwd, timeoutMs: opts?.timeoutMs };
|
|
22
22
|
}
|
|
23
23
|
Act.exec = exec;
|
|
24
|
+
/**
|
|
25
|
+
* Run an AI agent in a Filepath container.
|
|
26
|
+
*
|
|
27
|
+
* The agent executes in an isolated container with a git repo at /workspace.
|
|
28
|
+
* It communicates via the Filepath Agent Protocol (FAP) — NDJSON events on
|
|
29
|
+
* stdout that get mapped to Gateproof Log entries for gate assertions.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* Act.agent({
|
|
34
|
+
* name: "fix-auth",
|
|
35
|
+
* agent: "claude-code",
|
|
36
|
+
* model: "claude-sonnet-4-20250514",
|
|
37
|
+
* task: "Fix the authentication bug in src/auth.ts",
|
|
38
|
+
* repo: "https://github.com/org/repo",
|
|
39
|
+
* })
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
function agent(config) {
|
|
43
|
+
return { _tag: "Agent", config };
|
|
44
|
+
}
|
|
45
|
+
Act.agent = agent;
|
|
24
46
|
})(Act || (Act = {}));
|
|
25
47
|
//# sourceMappingURL=act.js.map
|