gateproof 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,47 +1,150 @@
1
1
  # gateproof
2
2
 
3
- E2E testing harness. Observe logs, run actions, assert results.
3
+ Build software in reverse. PRD defines what should exist. Gates verify reality. Agent iterations refine until gates pass.
4
4
 
5
5
  ## What gateproof does
6
6
 
7
- gateproof **executes gates**. It does not define intent, plans, or workflows.
7
+ gateproof enables **agent iterations** with minimal context overhead.
8
8
 
9
- A gate is a test specification: observe logs, run actions, assert results. gateproof runs it and returns evidence.
9
+ **The workflow:**
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
10
15
 
11
- You define stories (gates) in your PRD. gateproof executes gate files.
16
+ **Why this works:**
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)
21
+
22
+ gateproof **executes gates**. It does not define intent, plans, or workflows. A gate is a test specification: observe logs, run actions, assert results. gateproof runs it and returns evidence.
12
23
 
13
24
  **Authority chain:**
14
- - **PRD (`prd.ts` / `prd.json`)** — authority on intent, order, and state
25
+ - **PRD (`prd.ts`)** — authority on intent, order, and dependencies (if you use the PRD runner)
15
26
  - **Gate implementations** — authority on how reality is observed
16
27
  - **gateproof runtime** — authority on enforcement only
17
28
 
18
- gateproof never decides *what* to build. It only decides *when you are allowed to proceed*.
29
+ gateproof never decides *what* to build. It returns results; your CI/CD decides whether you are allowed to proceed.
30
+
31
+ ## Agent skill: prdts-maker
32
+
33
+ This repo is agent-first. Use the `prdts-maker` skill to turn story bullets into a working `prd.ts`.
34
+
35
+ **How to use it:**
36
+ - Provide story bullets + dependencies in plain language.
37
+ - Ask the agent to run the `prdts-maker` skill and output a complete `prd.ts`.
38
+ - Save and run: `bun run prd.ts`.
39
+
40
+ **Example prompt:**
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
50
+
51
+ Generate a `prd.ts` from story bullets without opening the repo.
52
+
53
+ ```bash
54
+ echo "User can sign up\nEmail verification works (depends on signup)" | npx gateproof prdts --stdout
55
+ npx gateproof prdts --in stories.txt --out prd.ts
56
+ ```
57
+
58
+ This calls Opencode directly. Set `OPENCODE_ZEN_API_KEY` (or pass `--api-key`).
59
+
60
+ Paste mode (interactive stdin):
61
+
62
+ ```bash
63
+ npx gateproof prdts
64
+ # paste stories, then Ctrl-D
65
+ ```
66
+
67
+ To target a different Opencode base URL or model:
68
+
69
+ ```bash
70
+ npx gateproof prdts --endpoint https://opencode.ai/zen/v1 --model big-pickle --in stories.txt --out prd.ts
71
+ ```
72
+
73
+ ## Agent Iterations: The Loop
74
+
75
+ The core innovation: agents work from PRD only, gates verify, iterate until correct.
76
+
77
+ **The iteration loop:**
78
+ 1. Run PRD → executes gates in dependency order
79
+ 2. Gate fails → agent gets: codebase context (e.g., `AGENTS.md`) + failure output
80
+ 3. Agent fixes → makes changes to codebase
81
+ 4. Loop repeats → re-run PRD, check if gates pass
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)
89
+
90
+ **Example loop script:**
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
101
+ ```
102
+
103
+ **The guardrails:**
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.
19
109
 
20
110
  ## Stories as gates
21
111
 
22
112
  A PRD (Product Requirements Document) defines stories. Stories are gates. Each story references a gate file. The gate file verifies the story against reality.
23
113
 
24
- Reality decides when you can proceed.
114
+ Reality is the source of truth; gates make it enforceable in CI.
25
115
 
26
116
  ### prd.ts example
27
117
 
28
118
  ```typescript
29
119
  // prd.ts
30
- export const stories = [
31
- {
32
- id: "user-signup",
33
- title: "User can sign up",
34
- gateFile: "./gates/user-signup.gate.ts",
35
- status: "pending"
36
- },
37
- {
38
- id: "email-verification",
39
- title: "User receives verification email",
40
- gateFile: "./gates/email-verification.gate.ts",
41
- dependsOn: ["user-signup"],
42
- status: "pending"
120
+ import { definePrd } from "gateproof/prd";
121
+
122
+ export const prd = definePrd({
123
+ stories: [
124
+ {
125
+ id: "user-signup",
126
+ title: "User can sign up",
127
+ gateFile: "./gates/user-signup.gate.ts",
128
+ },
129
+ {
130
+ id: "email-verification",
131
+ title: "User receives verification email",
132
+ gateFile: "./gates/email-verification.gate.ts",
133
+ dependsOn: ["user-signup"],
134
+ },
135
+ ] as const, // keep story IDs as literal types
136
+ });
137
+
138
+ // Make it executable
139
+ if (import.meta.main) {
140
+ const { runPrd } = await import("gateproof/prd");
141
+ const result = await runPrd(prd);
142
+ if (!result.success) {
143
+ if (result.failedStory) console.error(`Failed at: ${result.failedStory.id}`);
144
+ process.exit(1);
43
145
  }
44
- ];
146
+ process.exit(0);
147
+ }
45
148
  ```
46
149
 
47
150
  Each story references a gate file. The gate file uses gateproof's API:
@@ -51,55 +154,111 @@ Each story references a gate file. The gate file uses gateproof's API:
51
154
  import { Gate, Act, Assert } from "gateproof";
52
155
  import { CloudflareProvider } from "gateproof/cloudflare";
53
156
 
54
- const provider = CloudflareProvider({
55
- accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
56
- apiToken: process.env.CLOUDFLARE_API_TOKEN!
57
- });
157
+ export async function run() {
158
+ const provider = CloudflareProvider({
159
+ accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
160
+ apiToken: process.env.CLOUDFLARE_API_TOKEN!,
161
+ });
162
+
163
+ const result = await Gate.run({
164
+ name: "user-signup",
165
+ observe: provider.observe({ backend: "analytics", dataset: "worker_logs" }),
166
+ act: [Act.browser({ url: "https://app.example.com/signup" })],
167
+ assert: [
168
+ Assert.noErrors(),
169
+ Assert.hasAction("user_created"),
170
+ ],
171
+ });
172
+
173
+ return { status: result.status };
174
+ }
175
+ ```
58
176
 
59
- const result = await Gate.run({
60
- name: "user-signup",
61
- observe: provider.observe({ backend: "analytics", dataset: "worker_logs" }),
62
- act: [Act.browser({ url: "https://app.example.com/signup" })],
63
- assert: [
64
- Assert.noErrors(),
65
- Assert.hasAction("user_created")
66
- ]
67
- });
177
+ **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?`). Otherwise, orchestrate gates however you want — gateproof only cares about executing gate files.
68
178
 
69
- if (result.status !== "success") process.exit(1);
179
+ Stories execute in dependency order. The runner stops on first failure. Progress is not declared. It is proven.
180
+
181
+ ## How it works
182
+
183
+ The PRD defines stories. Stories reference gate files. Gate files use gateproof's API. Gates can be enforced in CI before merge/deploy.
184
+
185
+ **The sequence:** PRD story → gate file → gate execution → story marked "done" only when gate passes.
186
+
187
+ **For agent iterations:** PRD → gate fails → agent fixes → gate re-runs → loop until pass.
188
+
189
+ Run your PRD:
190
+
191
+ ```bash
192
+ bun run prd.ts
70
193
  ```
71
194
 
72
- ### prd.json example
195
+ Run agent iteration loop:
196
+
197
+ ```bash
198
+ bash patterns/prd/agent-iteration-loop.sh
199
+ ```
200
+
201
+ ## Hardening `prd.ts` (recommended)
202
+
203
+ Treat `prd.ts` like code: typecheck + validate before push + enforce in CI.
204
+
205
+ - **Validate PRD**:
206
+
207
+ ```bash
208
+ bun run prd:validate
209
+ ```
210
+
211
+ - **Pre-push (default for everyone on your team)**: add to your `prepush` script (Husky calls it).
73
212
 
74
213
  ```json
75
214
  {
76
- "stories": [
77
- {
78
- "id": "user-signup",
79
- "title": "User can sign up",
80
- "gateFile": "./gates/user-signup.gate.ts",
81
- "status": "pending"
82
- }
83
- ]
215
+ "scripts": {
216
+ "prepush": "bun run typecheck && bun run prd:validate && bun test"
217
+ }
84
218
  }
85
219
  ```
86
220
 
87
- **gateproof does not parse or own your PRD.** It's your repo's artifact. **You decide the format. gateproof only executes the gate files your PRD references.**
88
-
89
- Stories carry state. The PRD tracks which stories are pending, in progress, or done. gateproof does not manage this state. It only enforces: proceed only when gates pass.
221
+ - **CI**: run the validator before running PRD/tests.
90
222
 
91
- ## How it works
92
-
93
- The PRD defines stories. Stories reference gate files. Gate files use gateproof's API. Gates can be enforced in CI before merge/deploy.
223
+ ```yaml
224
+ - name: Validate PRD
225
+ run: bun run prd:validate
226
+ ```
94
227
 
95
- The sequence: PRD story gate file gate execution → story marked "done" only when gate passes.
228
+ - **Monorepo**: validate any PRD file by path.
96
229
 
97
- Progress is not declared. It is proven.
230
+ ```bash
231
+ bun run scripts/prd-validate.ts packages/api/prd.ts
232
+ ```
98
233
 
99
234
  ## Design notes
100
235
 
101
236
  - [Effect and Schema: Gateproof's Foundation](docs/effect-and-schema.md)
102
237
 
238
+ ## Writing good gates (agent-first)
239
+
240
+ Gates can fail loudly. They can also pass on silence if you write weak assertions.
241
+
242
+ - **Always assert at least one positive signal**: `Assert.hasAction(...)` and/or `Assert.hasStage(...)`. If your backend can be silent, add an explicit “evidence must exist” custom assertion.
243
+ - **Don’t rely on absence-only checks**: `Assert.noErrors()` alone can pass if you collect no logs.
244
+ - **Treat observability as part of the system**: your confidence is bounded by what you can observe.
245
+
246
+ ## Limits / Non-goals
247
+
248
+ - **Not a planner or orchestrator**: gateproof executes gates; your PRD (or CI) decides what to run and in what context.
249
+ - **Not a truth oracle**: if your backend drops logs, a gate can be wrong. Gateproof can’t fix missing telemetry.
250
+ - **Enforcement is external**: gateproof returns results; CI/CD decides whether to block merge/deploy.
251
+
252
+ ## Common objections (and answers)
253
+
254
+ - **"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.
255
+
256
+ - **"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.
257
+
258
+ - **"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.
259
+
260
+ - **"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.
261
+
103
262
  ## Quick Start
104
263
 
105
264
  The API is minimal: three concepts (Gate, Act, Assert). Here's a gate:
@@ -110,14 +269,14 @@ import { CloudflareProvider } from "gateproof/cloudflare";
110
269
 
111
270
  const provider = CloudflareProvider({
112
271
  accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
113
- apiToken: process.env.CLOUDFLARE_API_TOKEN!
272
+ apiToken: process.env.CLOUDFLARE_API_TOKEN!,
114
273
  });
115
274
 
116
275
  const result = await Gate.run({
117
276
  name: "api-health-check",
118
277
  observe: provider.observe({ backend: "analytics", dataset: "worker_logs" }),
119
278
  act: [Act.browser({ url: "https://my-worker.workers.dev" })],
120
- assert: [Assert.noErrors(), Assert.hasAction("request_received")]
279
+ assert: [Assert.noErrors(), Assert.hasAction("request_received")],
121
280
  });
122
281
 
123
282
  if (result.status !== "success") process.exit(1);
@@ -163,6 +322,42 @@ Assert.custom("name", fn) // Custom: (logs) => boolean
163
322
  }
164
323
  ```
165
324
 
325
+ ## PRD Runner
326
+
327
+ gateproof provides a PRD runner that executes stories in dependency order:
328
+
329
+ ```typescript
330
+ import { definePrd, runPrd } from "gateproof/prd";
331
+
332
+ const prd = definePrd({
333
+ stories: [
334
+ {
335
+ id: "story-1",
336
+ title: "First story",
337
+ gateFile: "./gates/story-1.gate.ts",
338
+ },
339
+ {
340
+ id: "story-2",
341
+ title: "Second story",
342
+ gateFile: "./gates/story-2.gate.ts",
343
+ dependsOn: ["story-1"],
344
+ },
345
+ ] as const, // keep story IDs as literal types
346
+ });
347
+
348
+ const result = await runPrd(prd);
349
+ if (!result.success) {
350
+ console.error(`Failed at: ${result.failedStory?.id}`);
351
+ process.exit(1);
352
+ }
353
+ ```
354
+
355
+ The runner:
356
+ - Validates dependencies (unknown IDs and cycles throw)
357
+ - Topologically sorts stories by `dependsOn`
358
+ - Executes gates in order
359
+ - **Stops on first failure**
360
+
166
361
  ## Plug Your Backend
167
362
 
168
363
  gateproof works with any observability backend. Just implement the `Backend` interface:
@@ -202,18 +397,34 @@ See `patterns/` for complete examples:
202
397
  - `patterns/cloudflare/` - Cloudflare-specific patterns
203
398
  - `patterns/ci-cd/` - CI/CD integration
204
399
  - `patterns/advanced/` - Advanced patterns
400
+ - `patterns/prd/` - PRD-as-code + agent iteration loop examples
401
+ - `patterns/agent-first/` - Spec interview → PRD stories (agent-first)
402
+ - `examples/hello-world-agent/` - Minimal agent with 5 tools + end-to-end gates
403
+
404
+ Run the hello-world agent example (requires `OPENCODE_ZEN_API_KEY` and network access to `opencode.ai`):
405
+
406
+ ```bash
407
+ export OPENCODE_ZEN_API_KEY="your_key_here"
408
+ bun run examples/hello-world-agent/prd.ts
409
+ ```
205
410
 
206
411
  ## CI/CD
207
412
 
208
413
  gateproof enforces gates in CI/CD. See `patterns/ci-cd/github-actions.ts` for examples.
209
414
 
415
+ Run your PRD in CI:
416
+
417
+ ```yaml
418
+ - name: Run PRD
419
+ run: bun run prd.ts
420
+ ```
421
+
210
422
  ## Requirements
211
423
 
212
424
  - Node.js 18+ or Bun
213
425
  - `playwright` (optional, for Act.browser)
214
426
  - Cloudflare credentials (for CloudflareProvider, or bring your own backend)
215
427
 
216
-
217
428
  ## License
218
429
 
219
430
  MIT
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=gateproof.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateproof.d.ts","sourceRoot":"","sources":["../../src/cli/gateproof.ts"],"names":[],"mappings":""}