Plan the SCV continuous improvement loop

d72282fcb203 · AtlantisPleb · · parent fcf083f7c143

Plan the SCV continuous improvement loop

Deploy story

What this commit did to the running system — joined from the forge receipt chain, the part a commit page elsewhere cannot show.

Not deployed through the forge lane

No push, promotion, build, or deploy receipt references this commit (receipts are scanned over a bounded recent window). Changes shipped by full node replacement carry their proof in the release gate receipt instead.

Changed files

  • added docs/scv-planning.md
  • modified lib/openagents/forge/build_worker.ex
  • modified test/openagents_web/route_authority_test.exs

Diff

3 files changed, +1092 -2

docs/scv-planning.md added +1090

@@ -0,0 +1,1090 @@

1
# SCV planning
2
3
Date: 2026-08-20
4
5
Status: Proposed architecture; implementation and autonomous deployment remain disabled
6
7
## Outcome
8
9
Build an Elixir-native, durable SCV that continuously finds bounded codebase
10
improvements, implements them in isolated repository workspaces, proves each
11
candidate against an exact Git SHA, and submits admitted candidates to the Forge
12
deployment pipeline.
13
14
SCV means Space Construction Vehicle. Use SCV consistently in code,
15
documentation, configuration, and the interface. Do not introduce another name
16
for this subsystem.
17
18
An SCV is a logical long-running service, not one immortal process or one
19
unbounded model response. OTP processes may restart, provider calls may end, and
20
workspaces may be discarded. PostgreSQL, Forge commits, immutable artifacts, and
21
receipts preserve the SCV's identity and progress across those events.
22
23
The intended end state includes automatic deployment. The first implementation
24
must not bypass the repository's current safety contracts:
25
26
- GitHub remains canonical until the proof-gated cutover in
27
  [ADR 0007](decisions/0007-cut-over-to-forge-canonical-source-control-after-proof.md).
28
- Forge fleet deployment remains disabled until the isolated staging gates in
29
  the [integration hardening plan](2026-08-20-integration-hardening-and-staging-readiness-recommendations.md)
30
  pass.
31
- `SELF-EDIT-001` currently requires a human promotion. Enabling an SCV to
32
  promote a candidate requires an explicit invariant and architecture amendment,
33
  a typed service principal, and a policy-bound promotion receipt. Do not encode
34
  an SCV identity in the existing free-form `promoted_by` field and call that
35
  authorization.
36
37
The recommended first milestone is a continuously running, propose-only SCV.
38
The recommended first autonomous milestone is staging-only deployment of a
39
narrow, low-risk change class. Production autonomy is a later admission, not a
40
configuration toggle hidden inside the first release.
41
42
## Goals
43
44
An SCV should:
45
46
- Operate without a browser conversation or a person keeping a process alive.
47
- Select work from explicit evidence instead of producing undirected code churn.
48
- Use a versioned, digest-addressed SCV program and policy revision.
49
- Read and change the exact repository that Forge recognizes as source truth.
50
- Use an isolated, secret-free workspace with a complete compiler and test
51
  toolchain.
52
- Preserve every model request, tool decision, command result, commit, gate,
53
  promotion, deployment, verification, and rollback as bounded evidence.
54
- Recover after node, process, provider, and executor failures without repeating
55
  an uncertain external effect.
56
- Enforce token, cost, time, CPU, memory, disk, command, diff, commit, and
57
  deployment budgets outside the model.
58
- Keep one linear improvement history so later work includes earlier admitted
59
  improvements.
60
- Treat no change, refusal, and rollback as valid outcomes.
61
62
## Non-goals
63
64
The first SCV should not:
65
66
- Replace the existing user-scoped coding-job experience.
67
- Run as a conversational persona or compose user conversation memory into its
68
  instructions.
69
- Receive production credentials, user conversation content, profile memory,
70
  voice transcripts, or unrestricted database access.
71
- Edit its own authority policy, deployment allowlist, release gates, or
72
  evaluator and then approve that edit.
73
- Modify production data, perform destructive migrations, rotate secrets,
74
  change billing policy, or widen an authorization boundary.
75
- Run several repository-writing SCVs concurrently.
76
- Deploy a structural or unclassified candidate automatically in the first
77
  autonomous release.
78
- Treat a passing model-authored test as sufficient evidence of correctness.
79
80
## Existing foundation
81
82
The repository already implements much of the mechanical foundation. Reuse the
83
contracts, but do not force an SCV into a user-scoped abstraction whose identity
84
or bounds are wrong.
85
86
| Existing capability | Reuse | Required SCV change |
87
| --- | --- | --- |
88
| `OpenAgents.Providers.Provider`, `OpenAgents.Providers.Request`, and `OpenAgents.Providers.OpenAI` | Reuse the provider-neutral stream and event normalization | Add an SCV-specific client and program. Do not use the conversational context composer. Admit SCV-specific output and timeout bounds instead of relying on the text-turn defaults. |
89
| `OpenAgents.Tools.Registry`, `OpenAgents.Tools.Runner`, and tool receipts | Reuse schema validation, authority checks, cancellation, timeout handling, output bounds, and normalized outcomes | Add an `scv` execution surface and an SCV-only tool catalog. The model must never receive promotion, policy-edit, or deployment tools. |
90
| `OpenAgents.Work.JobServer` and `OpenAgents.Work` | Reuse the durable-step, generation-fence, forced-report, and recovery patterns | Do not add `scv` to `work_jobs.kind`. Work jobs are conversation- and owner-scoped, have a ten-minute limit, run the coding-lieutenant role program, and terminate after one report. |
91
| `OpenAgents.Work.Coding` and repository tools | Reuse exact-match edit semantics, safe path resolution, commit receipts, and branch confinement | Replace per-user approval receipts and the fixed `openagents/job-<id>` lifecycle with SCV service authority, durable run workspaces, richer Git inspection, and full test execution. |
92
| `OpenAgents.Inference` | Reuse metering concepts and server-held provider credentials | Add a service-principal ledger or generalize grants to identify an SCV. Do not invent a visitor, conversation, or machine to satisfy the current schema. |
93
| `OpenAgents.Forge.Pushes` and the WAL | Reuse the push acknowledgment barrier and immutable push receipts | Give an SCV executor a repository-scoped, branch-scoped credential. It must not receive the operator token or a credential that can update arbitrary refs. |
94
| `OpenAgents.Forge.Builder` and the build worker | Reuse isolated exact-SHA builds, structural classification, artifact verification, and bounded output | Keep the web release compiler-free. Run SCV commands in a separate worker identity and make candidate gate receipts durable outside one worker's `.git` directory. |
95
| `OpenAgents.Forge.Targets` and deployment coordinators | Reuse newest-target fencing, direct-load transactions, relup, rolling replacement, boot convergence, and receipts | Add a policy-authorized SCV promotion path that remains separate from a push. Preserve human promotion for every class outside the admitted SCV policy. |
96
| `OpenAgents.Incidents` | Reuse typed failures, bounded context, recurrence tracking, and nonrecursive repair principles | Admit sanitized incidents as possible work-item evidence. Never expose private incident context or allow a failed SCV to recursively create another SCV. |
97
| Exact-SHA release gate | Reuse `mix precommit`, focused tests, release smoke, direct-load, relup, and rolling proofs | Define which gate is mandatory for each risk class. Store the exact gate definition digest so an SCV cannot weaken the gate in the same candidate. |
98
99
The existing coding-job integration test proves the sequence through a pushed
100
branch and deliberately stops before promotion. An SCV should extend that
101
receipt chain instead of replacing it with a less governed shortcut.
102
103
## Separate bounded context
104
105
Create an `OpenAgents.SCV` bounded context. Keep generic SCV control-plane code
106
out of `OpenAgents.Work` and keep persona-specific code out of `OpenAgents.SCV`.
107
108
The context should own:
109
110
- durable SCV identity and policy revision;
111
- work discovery and admission;
112
- run leases and generation fencing;
113
- SCV program composition;
114
- provider continuations and tool-step receipts;
115
- executor requests and responses;
116
- repository workspace lifecycle;
117
- candidate and gate decisions;
118
- policy-authorized promotion requests;
119
- post-deployment observation and rollback decisions;
120
- budgets, circuit breakers, and operator controls.
121
122
Forge should continue to own Git, build, target, deployment, rollback, and boot
123
convergence. An SCV proposes source and presents policy evidence. Forge decides
124
whether an exact SHA can become a target and whether that target becomes live.
125
126
## Architecture
127
128
```text
129
sanitized evidence and operator work
130
                |
131
                v
132
      SCV work-item admission
133
                |
134
                v
135
   durable SCV coordinator and lease
136
       |                    |
137
       | provider events    | typed executor requests
138
       v                    v
139
server provider adapter   isolated SCV worker
140
                            |-- exact Forge checkout
141
                            |-- bounded file tools
142
                            |-- bounded command runner
143
                            |-- disposable database and services
144
                            `-- no production secrets
145
                                   |
146
                                   v
147
                         SCV candidate commit and push
148
                                   |
149
                                   v
150
                        immutable exact-SHA gate receipt
151
                                   |
152
                                   v
153
                     host policy and promotion receipt
154
                                   |
155
                                   v
156
                    Forge build and deployment pipeline
157
                                   |
158
                                   v
159
                    post-deployment verification window
160
                         |                     |
161
                         v                     v
162
                    admit result       promote predecessor
163
```
164
165
### Runtime placement
166
167
Keep the durable coordinator Elixir-native. Implement its lifecycle with OTP,
168
Ecto, and supervised tasks. Do not make an external coding CLI the SCV's
169
authority or durable state machine.
170
171
Run candidate code and build commands in a separate SCV worker container or
172
owned worker VM. The worker may also run Elixir, but it must use a separate
173
runtime identity and mounts from the Phoenix release. This preserves the Forge
174
build-lane rule that the web release receives no compiler, Docker socket, or
175
general command-execution authority.
176
177
The worker needs:
178
179
- read access to exact Forge objects;
180
- write access only to an SCV run ref;
181
- access to an atomic request and response channel;
182
- an ephemeral workspace, build cache, and disposable database;
183
- bounded CPU, memory, disk, process count, and wall-clock time;
184
- no production database URL, release cookie, cloud credential, Forge operator
185
  token, or user credential;
186
- no network by default, except the narrow internal endpoints required for
187
  Forge and coordinator communication.
188
189
Candidate code is untrusted during evaluation even though the worker runs in an
190
owned environment. Tests and Mix tasks can execute arbitrary repository code.
191
Do not mount any credential that candidate code could read or transmit.
192
193
## Durable execution model
194
195
An SCV remains logically active while its work occurs in bounded runs. The
196
coordinator repeats this sequence:
197
198
1. Wake on a durable work item, admitted signal, or bounded poll interval.
199
2. Acquire the repository's single-writer lease and increment its generation.
200
3. Select one work item against the current policy, budget, and integration
201
   head.
202
4. Start a bounded run with a fresh model context and isolated workspace.
203
5. Persist every requested tool step before execution.
204
6. Commit an honest terminal run result, including `no_change`, `refused`, or
205
   `budget_exhausted`.
206
7. If the run produced a candidate, advance it through gates and deployment as
207
   a separate durable state machine.
208
8. Observe the outcome, update the work item, release the lease, and return to
209
   idle.
210
211
This model supports continuous operation without infinite prompts, unbounded
212
mailboxes, permanent workspaces, or one process whose death loses the plan.
213
214
### Context and checkpointing
215
216
Start each run with bounded, source-linked context:
217
218
- the exact base SHA and SCV integration ref;
219
- the admitted work-item objective and evidence refs;
220
- the SCV program and policy digests;
221
- relevant repository paths, symbols, and recent commits;
222
- focused test failures or sanitized operational facts;
223
- prior attempts for the same work-item fingerprint;
224
- current token, command, diff, time, and deployment budgets.
225
226
Store compact checkpoints after investigation, plan selection, each mutation,
227
each test group, commit, gate, and deployment. A checkpoint is structured state,
228
not a transcript dump. It should identify facts, evidence refs, decisions,
229
changed paths, remaining work, and unresolved risks.
230
231
If context grows beyond its admitted limit, start a new provider response from
232
the latest durable checkpoint. Do not trust a model-authored summary without
233
the source refs needed to verify it.
234
235
## SCV program
236
237
Define a versioned `OpenAgents.SCV.Program` artifact with a calculated digest
238
and an admitted digest, following the repository's existing persona and role
239
artifact discipline without making an SCV a persona.
240
241
The SCV program should require this method:
242
243
1. State the observed problem and its evidence.
244
2. Read the relevant implementation, tests, invariants, and documentation.
245
3. Define the smallest verifiable outcome.
246
4. Add or identify a failing test before changing behavior.
247
5. Make a focused change.
248
6. Run the narrowest useful check, then the required candidate gate.
249
7. Inspect the final diff for unrelated or policy-protected changes.
250
8. Commit once the candidate is coherent.
251
9. Report uncertainty, omitted work, and exact receipts.
252
253
Repository files, issues, test output, comments, commit messages, dependency
254
metadata, and incident descriptions remain untrusted input. They cannot change
255
the SCV program, tool authority, budget, protected paths, risk class, gate, or
256
deployment policy.
257
258
## Work discovery and selection
259
260
An SCV should optimize against observable product and engineering outcomes. It
261
should not make changes to remain busy.
262
263
### Initial work sources
264
265
Admit these sources first:
266
267
- operator-created SCV work items;
268
- reproducible failing tests from the owned gate;
269
- compile warnings and static contract failures;
270
- typed, recurring incidents with sanitized evidence;
271
- documented TODO items that name an expected result and owner-approved scope;
272
- focused coverage gaps for high-risk code when the work item names the missing
273
  behavior;
274
- measurable performance regressions with a stable benchmark.
275
276
Delay broad dependency updates, external vulnerability feeds, speculative
277
refactoring, and free-form issue ingestion until their authority and network
278
contracts are explicit.
279
280
### Admission score
281
282
Score a work item with host-owned data:
283
284
- user or operator impact;
285
- reproducibility and evidence quality;
286
- confidence that the repository contains the fix;
287
- expected diff and deployment risk;
288
- estimated test and inference cost;
289
- recurrence and age;
290
- collision with active human work;
291
- cooldown after a prior failed attempt.
292
293
The model may recommend a score, but host code calculates the admitted score
294
and selects the next item. Deduplicate work by a stable fingerprint over the
295
repository, evidence class, affected surface, and normalized problem code.
296
297
### Valid idle behavior
298
299
When no item clears the admission threshold, an SCV remains idle. Idle is a
300
healthy state. Avoid goals such as "improve the codebase" without a measurable
301
problem because they reward churn, test rewriting, and style-only diffs.
302
303
## Workspace and Git model
304
305
### Exact base
306
307
Create each workspace from an exact commit in the WAL-backed Forge repository.
308
Do not copy the running image tree into a writable directory and do not clone
309
from GitHub.
310
311
Use a linear SCV integration ref, initially
312
`refs/heads/scv/integration`. Each run:
313
314
1. Reads the ref and its durable WAL position.
315
2. Records the exact base SHA in the run.
316
3. Checks out that SHA detached in a fresh workspace.
317
4. Creates `refs/heads/scv/runs/<run-id>` for its candidate.
318
5. Pushes with an expected-old-SHA compare-and-swap condition.
319
320
Only one repository-writing SCV runs at first, but the compare-and-swap remains
321
required. It catches operator changes, restore races, and future concurrency.
322
323
### Keeping improvements
324
325
Do not base each run on the default branch or current image independently. A
326
candidate deployed from a run branch can be absent from the next default-branch
327
clone. The SCV integration ref must advance only after the candidate reaches
328
its admitted terminal state:
329
330
- For a source-only candidate, advance after its source gate passes.
331
- For a runtime candidate, advance after Forge marks it `live` and the
332
  observation window passes.
333
- For a reverted or failed candidate, leave the integration ref on its
334
  predecessor.
335
336
Advance the integration ref through the normal authenticated Forge push path so
337
the WAL remains ref authority. Use compare-and-swap against the recorded base.
338
Do not update a bare repository ref directly from application code.
339
340
Before Forge becomes canonical, keep an SCV in propose-only mode and reconcile
341
its run refs through the existing GitHub review process. After the ADR 0007
342
cutover, decide whether `scv/integration` becomes the default branch or merges
343
into it through another policy-controlled fast-forward. Do not operate two
344
writable canonical histories.
345
346
### Workspace lifecycle
347
348
Keep a run workspace until its candidate reaches a terminal result and retain
349
only bounded diagnostic artifacts afterward. Remove it after success, refusal,
350
failure, cancellation, or rollback. Never reuse a dirty workspace for another
351
run.
352
353
## Tool and command design
354
355
Reuse repository read, grep, list, exact-edit, write, and commit semantics where
356
they fit. Add SCV-specific tools for:
357
358
- `git status`, diff, log, show, merge-base, and blame projections;
359
- file creation, deletion, and rename with path and byte bounds;
360
- focused test discovery and execution;
361
- Mix help and admitted Mix tasks;
362
- JavaScript tests through the repository's pinned package command;
363
- formatting and final diff inspection;
364
- checkpoint and candidate submission.
365
366
Do not expose a raw shell-string tool. The executor should receive an executable
367
name, argument list, working directory, environment profile, time limit, and
368
output limit as structured data. Invoke it without a shell.
369
370
A small command allowlist is safer but may be too restrictive for useful coding
371
work. Use policy profiles instead:
372
373
- A read profile admits bounded Git and source-inspection commands.
374
- A focused-test profile admits exact repository-owned test entry points and
375
  Mix tasks after validating their options.
376
- A candidate-gate profile admits only the immutable gate definition.
377
- A networked profile remains disabled in the first release.
378
379
Validate environment variables against an allowlist and build a fresh
380
environment. Never inherit the coordinator's complete environment. Redact
381
output before persistence and retain full output only in an operator-only,
382
short-lived store with a digest in the receipt.
383
384
Promotion, deployment, rollback, policy changes, budget increases, and
385
integration-ref advancement are host actions. Do not advertise them as model
386
tools.
387
388
## Durable records
389
390
Prefer separate tables over extending user-facing work rows.
391
392
### `scvs`
393
394
Store one logical SCV identity per repository:
395
396
- repository;
397
- status: `disabled`, `idle`, `running`, `paused`, or `circuit_open`;
398
- admitted program and policy revisions and digests;
399
- integration ref and admitted head SHA;
400
- owner node, lease generation, and lease expiry;
401
- current run and candidate IDs;
402
- budget window counters;
403
- last healthy and last terminal timestamps.
404
405
### `scv_work_items`
406
407
Store durable candidate work:
408
409
- source and source reference;
410
- stable deduplication fingerprint;
411
- bounded title, objective, and sanitized evidence refs;
412
- admitted risk ceiling and repository scope;
413
- priority inputs and calculated score;
414
- status: `discovered`, `admitted`, `running`, `completed`, `deferred`,
415
  `refused`, or `failed`;
416
- attempt count, cooldown, and terminal reason.
417
418
### `scv_runs`
419
420
Store one bounded execution episode:
421
422
- SCV, work item, base SHA, integration WAL position, and generation;
423
- program, policy, tool-catalog, evaluator, and gate digests;
424
- model and provider adapter IDs;
425
- phase and terminal status;
426
- token, cost, tool, command, time, CPU, memory, disk, and diff usage;
427
- structured checkpoint and bounded report;
428
- candidate ID and error code;
429
- start and completion timestamps.
430
431
### `scv_steps`
432
433
Store every provider and tool boundary in order:
434
435
- sequence, provider response and call IDs, and prior-response ID;
436
- tool version, artifact digest, arguments digest, and output digest;
437
- requested, claimed, terminal, and uncertain timestamps;
438
- executor identity, status, error code, and receipt refs;
439
- run generation and idempotency key.
440
441
Store large input and output only in a bounded, access-controlled artifact
442
store when diagnosis requires it. Database rows should contain redacted
443
excerpts and digests.
444
445
### `scv_candidates`
446
447
Store the immutable candidate decision:
448
449
- run, base SHA, candidate SHA, run ref, and changed paths;
450
- diff digest, line and byte counts, and semantic risk findings;
451
- focused-test and gate receipt refs;
452
- policy decision and policy digest;
453
- source integration, Forge target, build, deploy, and predecessor refs;
454
- observation window, measurements, terminal result, and rollback target;
455
- immutable timestamps for each transition.
456
457
Use database constraints and triggers for forward-only terminal states and
458
immutable receipt fields. Treat PubSub and UI projections as hints.
459
460
## Run state machine
461
462
Use a state machine that distinguishes source construction from deployment:
463
464
```text
465
queued
466
  -> claiming
467
  -> investigating
468
  -> editing
469
  -> focused_testing
470
  -> candidate_committed
471
  -> candidate_pushed
472
  -> gating
473
  -> policy_review
474
  -> source_admitted
475
  -> promoting
476
  -> building
477
  -> deploying
478
  -> observing
479
  -> completed
480
```
481
482
Every nonterminal state may move to `failed`, `refused`, `cancelled`,
483
`budget_exhausted`, or `superseded` where appropriate. A deployed candidate may
484
move from `observing` to `reverted`. `no_change` is a terminal result from
485
investigation or focused testing.
486
487
Do not store one generic `running` status and infer the operation from logs.
488
Recovery and operator controls need the exact durable phase.
489
490
## Change policy
491
492
An SCV's most important code is the host-owned change classifier. It should
493
combine changed paths, diff structure, AST-level findings where practical,
494
Forge's build classification, and explicit protected-surface rules. A path
495
allowlist alone cannot recognize an authorization change hidden in a general
496
module.
497
498
### Recommended risk classes
499
500
| Class | Examples | Initial action |
501
| --- | --- | --- |
502
| Source-only | Documentation, comments, tests that add coverage, and development-only diagnostics | Require human review first. After a separate source-only policy admission, gate and advance the SCV integration ref without creating a fleet target when runtime output is unchanged. |
503
| Low-risk runtime | Focused bug fix in an explicitly admitted module, no interface or state-shape change, direct-load classification, and strong regression test | Human promotion first; staging autodeploy after policy proof. |
504
| Moderate runtime | New route, changed API shape, process-state behavior, broad refactor, or cross-context behavior | Require human review and the complete exact-SHA release gate. |
505
| Structural | Dependencies, assets, runtime configuration, migrations, releases, module deletion, native code, relup, or rolling replacement | Require human review. Admit staging automation only in a later policy revision with independent evidence. |
506
| Protected | SCV policy, Forge control, authentication, authorization, secrets, billing, data rights, invariant weakening, or release-gate weakening | Never auto-approve. Require an external operator path and independent evaluation. |
507
508
### Protected surfaces
509
510
The initial policy should refuse automatic promotion when a candidate changes:
511
512
- `AGENTS.md`, `INVARIANTS.md`, the SCV program, SCV policy, or SCV evaluator;
513
- `.githooks/`, `ops/ci/`, release-gate code, coverage floors, or test filters;
514
- `OpenAgents.Forge`, deployment providers, boot convergence, or release code;
515
- authentication, authorization, token, vault, secret, route-authority, or
516
  operator modules;
517
- migrations, schemas with durable-state meaning, or database triggers;
518
- provider credential handling, inference pricing, budgets, or metering;
519
- memory consent, data rights, publication visibility, or private-data bounds;
520
- dependencies, lockfiles, Dockerfiles, Terraform, runtime configuration, or
521
  production infrastructure;
522
- existing tests whose removal or weakening reduces a protected assertion.
523
524
An SCV may propose changes in these areas on its run branch, but the candidate
525
must stop at human review. The automatic lane must evaluate the complete diff,
526
including generated files and renames.
527
528
### Policy independence
529
530
Evaluate a candidate with the policy revision that existed at the run's base
531
SHA and a host-installed minimum policy. If a candidate changes policy code,
532
tests, or configuration, those changes cannot affect its own decision.
533
534
Use a two-key rule for later policy expansion: an operator admits the new
535
policy digest, and an independent evaluator proves its regression corpus. An
536
SCV can author a policy change, but it cannot supply either approval key.
537
538
## Candidate gates
539
540
Run checks in increasing order of cost and stop on the first failure:
541
542
1. Confirm workspace cleanliness and exact base ancestry.
543
2. Validate changed paths, diff bounds, generated artifacts, and protected
544
   surfaces.
545
3. Run formatting and focused regression tests.
546
4. Run compile with warnings as errors.
547
5. Run `mix precommit`.
548
6. Commit and push the exact candidate SHA.
549
7. Run the required exact-SHA release gate in a fresh checkout.
550
8. Verify that the gate definition digest matches the base policy.
551
9. Have Forge build and classify the exact pushed SHA independently.
552
10. Compare Forge's manifest and structural findings with the SCV policy
553
    decision.
554
555
The final gate must run after commit because the repository's release receipts
556
bind an exact SHA. Run it in a fresh checkout so ignored files, a dirty worktree,
557
or the SCV's build cache cannot change the result.
558
559
For a low-risk automatic candidate, require all of these facts:
560
561
- the candidate descends from the admitted integration head;
562
- focused tests prove the reported defect or improvement;
563
- no protected surface changed;
564
- `mix precommit` passes without retries or modified thresholds;
565
- the exact-SHA gate passes in the trusted evaluator;
566
- Forge independently classifies the complete candidate as `direct_candidate`;
567
- every changed runtime module matches the narrower SCV allowlist and Forge's
568
  operator-owned allowlist;
569
- the deployment budget and cooldown admit another target;
570
- no active incident, deploy, rollback, or human freeze blocks promotion.
571
572
## Automatic promotion
573
574
Keep push and promotion separate. A pushed SCV candidate should wake an SCV
575
policy evaluator, not `OpenAgents.Forge.Targets` directly.
576
577
Add a typed promotion principal such as:
578
579
```text
580
principal_type: scv
581
principal_id: <stable-scv-id>
582
policy_digest: <admitted-policy-digest>
583
candidate_id: <immutable-candidate-id>
584
gate_receipt_ref: <exact-sha-gate-receipt>
585
decision_digest: <complete-policy-input-digest>
586
```
587
588
The promotion API should accept either an authenticated operator receipt or an
589
admitted SCV receipt. It should verify the principal and evidence server-side,
590
then insert the same append-only Forge target used by a human promotion.
591
592
This amendment preserves the useful boundary that a push never promotes
593
itself. The model cannot promote. The repository tools cannot promote. The SCV
594
coordinator can request promotion only after host code has produced the
595
admitted receipt.
596
597
Record the promotion authority class explicitly in the target schema. Do not
598
overload a display string such as `operator:scv` because it would make audits
599
and authorization ambiguous.
600
601
## Deployment and verification
602
603
### Deployment sequence
604
605
After an admitted SCV promotion:
606
607
1. Wait for Forge to build the exact SHA and persist its receipt.
608
2. Require the build classification to match the SCV policy decision.
609
3. Let Forge select and execute the admitted deployment strategy.
610
4. Wait for the target and terminal deploy receipt from durable state, not only
611
   PubSub.
612
5. Start a post-deployment observation window after the target reaches `live`.
613
6. Run candidate-specific probes plus common health and readiness checks.
614
7. Compare bounded operational measurements with the recorded predecessor
615
   baseline.
616
8. Mark the candidate complete and advance the integration ref only after the
617
   observation window passes.
618
619
An SCV must never call BEAM loading functions or cloud deployment APIs itself.
620
Forge owns those effects and their rollback contracts.
621
622
### Observation contract
623
624
Define the expected signals before promotion. Use candidate-specific signals
625
where possible:
626
627
- the new regression test remains green against the packaged or live target;
628
- `/healthz` and deployment readiness remain healthy;
629
- fleet revision and artifact identities remain consistent;
630
- affected error codes do not regress;
631
- latency, memory, mailbox, and restart measurements remain within an admitted
632
  envelope;
633
- no new anomalous incident correlates with the candidate;
634
- the candidate's intended product outcome is observable when a safe synthetic
635
  probe exists.
636
637
Avoid a single global "error rate" gate that may miss a focused regression or
638
react to unrelated traffic. Store baseline interval, candidate interval,
639
sample size, missing-data result, and comparison policy in the candidate.
640
Missing required data should refuse admission rather than count as success.
641
642
### Rollback
643
644
Forge already reverts a failed deployment transaction before it marks a target
645
`live`. Post-live regression needs a second path: promote the exact predecessor
646
as a new target with an `scv_automatic_rollback` receipt, run the normal Forge
647
pipeline, and verify convergence.
648
649
Open the SCV circuit when any automatic candidate:
650
651
- requires post-live rollback;
652
- cannot verify rollback;
653
- leaves fleet identity divergent;
654
- creates an anomalous incident in a protected plane;
655
- exceeds its observation budget without enough evidence.
656
657
After the circuit opens, an SCV may continue read-only diagnosis if policy
658
allows it, but it cannot push, integrate, promote, or deploy until an operator
659
records a resume receipt.
660
661
## Control-loop stability
662
663
Continuous improvement can become an unstable feedback loop. Add these
664
controls from the first autonomous release:
665
666
- One active repository-writing run and one active candidate per repository.
667
- A cooldown between live candidates.
668
- A daily deployment budget and a separate rollback budget.
669
- A stable work-item fingerprint and retry backoff.
670
- A limit on changed files, lines, bytes, commits, and modules.
671
- A maximum number of attempts before operator review.
672
- A ban on immediately undoing and redoing the same change without new
673
  evidence.
674
- A predecessor comparison that detects oscillation between two SHAs.
675
- A change-frequency cap per subsystem.
676
- A freeze during incidents, migrations, operator maintenance, or fleet
677
  degradation.
678
- An operator pause that takes effect before the next external effect and a
679
  kill action that cancels current provider and executor work.
680
681
Do not use deployment count or lines changed as an SCV success metric. Prefer
682
resolved reproducible failures, prevented incidents, retained regression
683
tests, measured performance improvement, rollback-free observation windows,
684
and operator acceptance.
685
686
## Budgets
687
688
Use nested budgets:
689
690
- **Step budget:** input and output bytes, provider tokens, command output,
691
  command duration, and retries.
692
- **Run budget:** model calls, tool calls, continuations, wall time, CPU, memory,
693
  disk, changed files, diff size, and commits.
694
- **Candidate budget:** gate time, build attempts, deployment attempts,
695
  observation duration, and rollback attempts.
696
- **Window budget:** daily tokens, estimated cost, runs, pushes, promotions,
697
  deployments, and rollbacks.
698
699
Store the budget snapshot on the run before execution. A later configuration
700
increase must not widen an active run. When a run reaches a bound, refuse new
701
effects, request a tool-free bounded report when possible, and commit an honest
702
`budget_exhausted` result.
703
704
Start with conservative staging values and tune them from receipts. A useful
705
initial posture is one 45-minute run at a time, one candidate in flight, a
706
15-minute post-live observation window, at most four automatic staging
707
deployments per 24 hours, and an immediate circuit open after one rollback.
708
Keep these settings operator-owned and runtime-validated instead of hard-coding
709
them in a prompt.
710
711
## Recovery and idempotency
712
713
Use PostgreSQL as the run fence, following the existing work recovery contract:
714
715
- Claim a run under a row lock, record `owner_node`, increment `generation`,
716
  and set a bounded lease.
717
- Require the current generation on every checkpoint and terminal update.
718
- Reclaim only after the prior lease expires or its node is proven absent.
719
- Keep completed steps immutable.
720
- Resume only from committed outcomes.
721
722
Classify effects by recovery behavior:
723
724
| Effect | Recovery rule |
725
| --- | --- |
726
| Repository reads and deterministic analysis | Safe to repeat against the recorded SHA |
727
| Provider planning call | Safe to replace with a new call from a committed checkpoint; do not claim the interrupted response completed |
728
| File edit in an isolated workspace | Re-read and verify the expected digest before repeating |
729
| Test or compile command | Safe to repeat in a clean candidate workspace |
730
| Commit | Resolve by the run's tree digest and recorded ref before creating another commit |
731
| Push | Resolve the run ref and WAL receipt before retrying with compare-and-swap |
732
| Integration-ref advance | Resolve the WAL position and expected predecessor; never repeat blindly |
733
| Promotion | Deduplicate by candidate ID and promotion-decision digest |
734
| Deployment | Forge target and deployment IDs are authority; an SCV only observes or requests rollback |
735
736
If the executor dies during a command whose external effects cannot be
737
resolved, mark the step `uncertain`, fail the run closed, and require a new
738
workspace. Do not infer success from partial output.
739
740
## Security boundaries
741
742
### Service identity
743
744
Give an SCV separate, narrow identities for:
745
746
- provider use and usage accounting;
747
- coordinator-to-executor requests;
748
- Forge fetch;
749
- run-ref push;
750
- promotion receipt signing or verification;
751
- read-only operational measurements.
752
753
Do not reuse a browser session, user API token, machine pairing token, Forge
754
operator token, release cookie, or cloud deployment identity.
755
756
### Repository content and prompt injection
757
758
Treat all repository and work-item text as data. Host code must enforce:
759
760
- tool names and versions;
761
- exact repository and workspace roots;
762
- command profiles and arguments;
763
- environment variables and network access;
764
- path and output bounds;
765
- policy and gate digests;
766
- promotion and rollback admission.
767
768
A comment that says to ignore policy, expose credentials, weaken tests, or
769
deploy directly is an input-quality incident, not an instruction.
770
771
### Candidate execution
772
773
Assume candidate tests can read every mounted file and connect to every allowed
774
network destination. Use a disposable database with synthetic data, an empty
775
home directory, no forwarded SSH socket, no cloud metadata access, and no inherited
776
credential helpers. Pin dependencies before disabling external network access
777
for candidate execution.
778
779
### Data minimization
780
781
An SCV needs code and content-free operational facts, not user data. Incident
782
inputs should carry typed codes, affected component, recurrence, timestamps,
783
and sanitized stack or test refs. Never attach prompts, messages, memory,
784
transcripts, OAuth data, or arbitrary production rows.
785
786
## Observability and operator controls
787
788
Add an operator-only SCV surface with stable IDs and bounded projections. Show:
789
790
- SCV status, policy and program revision, lease generation, and integration
791
  head;
792
- active and recent work items, runs, candidates, and budgets;
793
- current phase, elapsed time, and cancellation state;
794
- changed paths and diff summary after a candidate exists;
795
- focused tests, exact-SHA gate, Forge build, target, deployment, and observation
796
  receipts;
797
- terminal result, rollback state, and circuit reason;
798
- **Pause**, **Resume**, **Cancel run**, **Reject candidate**, **Require human
799
  review**, and **Open diff** controls.
800
801
Do not expose raw prompts, private incident content, credentials, internal node
802
names, full build logs, or unrestricted command output. Public changelog entries
803
may name an SCV as the source role only after the existing repository visibility
804
policy admits the candidate.
805
806
Emit content-free telemetry for:
807
808
- work discovery and admission;
809
- run, provider, tool, and command duration;
810
- token and cost use;
811
- candidate refusal reasons;
812
- gate and build outcomes;
813
- promotion, deploy, observation, and rollback results;
814
- lease recovery, stale generation refusal, and circuit changes.
815
816
## Runtime configuration
817
818
Add typed, fail-closed settings such as:
819
820
```text
821
OPENAGENTS_FEATURE_SCV
822
OPENAGENTS_SCV_MODE=observe|propose|staging_auto|production_auto
823
OPENAGENTS_SCV_REPOSITORIES=openagents.com
824
OPENAGENTS_SCV_PROGRAM_REVISION=<revision>
825
OPENAGENTS_SCV_POLICY_REVISION=<revision>
826
OPENAGENTS_SCV_EXECUTOR=<adapter>
827
OPENAGENTS_SCV_EXECUTOR_QUEUE_DIR=<absolute-path>
828
OPENAGENTS_SCV_WORKSPACE_DIR=<absolute-path>
829
OPENAGENTS_SCV_MAX_ACTIVE_RUNS=1
830
OPENAGENTS_SCV_RUN_TIMEOUT_MS=<bounded-integer>
831
OPENAGENTS_SCV_DAILY_TOKEN_BUDGET=<bounded-integer>
832
OPENAGENTS_SCV_DAILY_COST_MICROUSD=<bounded-integer>
833
OPENAGENTS_SCV_DAILY_DEPLOYMENTS=<bounded-integer>
834
OPENAGENTS_SCV_DEPLOY_COOLDOWN_MS=<bounded-integer>
835
OPENAGENTS_SCV_OBSERVATION_MS=<bounded-integer>
836
```
837
838
The runtime boundary should reject:
839
840
- any enabled mode without admitted program and policy digests;
841
- an executor path under `/tmp` in staging or production;
842
- an automatic mode before Forge deployment, boot convergence, durable
843
  artifacts, and isolated staging are enabled;
844
- `production_auto` while production deployment remains globally disabled;
845
- multiple active runs in the first policy revision;
846
- a deployment budget without an observation window and rollback authority;
847
- an SCV repository that is absent from the configured Forge repositories.
848
849
Readiness should report only enabled mode, admission status, circuit state, and
850
whether dependencies validate. It must not print paths, URLs, credentials,
851
work-item content, prompts, or internal identities.
852
853
## Suggested module layout
854
855
Keep one module per file.
856
857
```text
858
lib/openagents/scv.ex
859
lib/openagents/scv/instance.ex
860
lib/openagents/scv/work_item.ex
861
lib/openagents/scv/run.ex
862
lib/openagents/scv/step.ex
863
lib/openagents/scv/candidate.ex
864
lib/openagents/scv/program.ex
865
lib/openagents/scv/policy.ex
866
lib/openagents/scv/change_classifier.ex
867
lib/openagents/scv/budget.ex
868
lib/openagents/scv/coordinator.ex
869
lib/openagents/scv/recovery.ex
870
lib/openagents/scv/context.ex
871
lib/openagents/scv/provider_loop.ex
872
lib/openagents/scv/tool_catalog.ex
873
lib/openagents/scv/workspace.ex
874
lib/openagents/scv/executor.ex
875
lib/openagents/scv/executor/sidecar.ex
876
lib/openagents/scv/executor_protocol.ex
877
lib/openagents/scv/gate.ex
878
lib/openagents/scv/promoter.ex
879
lib/openagents/scv/observer.ex
880
lib/openagents/scv/circuit.ex
881
```
882
883
Use test adapters under `test/support`. Keep Forge changes in
884
`lib/openagents/forge/` when they generalize promotion principals or receipts;
885
do not make Forge import the SCV context.
886
887
## Implementation phases
888
889
### Phase 0: Amend contracts
890
891
1. Add an ADR for policy-authorized SCV promotion.
892
2. Amend `SELF-EDIT-001`, `docs/architecture.md`, and the route-authority ledger
893
   to distinguish human and SCV promotion receipts.
894
3. Define the first SCV program, policy, protected surfaces, risk classes, and
895
   evaluator corpus.
896
4. Define service identities and secret inventory entries.
897
5. Keep the feature disabled.
898
899
**Exit criteria:** Documentation and tests agree on who can admit an SCV
900
candidate, which changes can qualify, and how the operator stops the system.
901
902
### Phase 1: Observe and queue
903
904
1. Add SCV, work-item, run, step, and candidate schemas with database guards.
905
2. Add the coordinator, lease, budgets, recovery, pause, and circuit state.
906
3. Ingest operator work items and sanitized owned-gate failures.
907
4. Run selection and planning without repository writes.
908
5. Add the operator surface and content-free telemetry.
909
910
**Exit criteria:** An SCV runs continuously in `observe` mode, survives process
911
and node loss, deduplicates work, spends within budget, and performs no external
912
effect.
913
914
### Phase 2: Build candidates
915
916
1. Add the isolated executor protocol and worker.
917
2. Add exact Forge checkout, workspace confinement, SCV tools, command profiles,
918
   and candidate-code sandboxing.
919
3. Add focused tests, commit resolution, run-ref push, WAL receipt linking, and
920
   cleanup.
921
4. Keep every candidate propose-only.
922
923
**Exit criteria:** An SCV can reproduce, test, patch, commit, and push a bounded
924
candidate. Crash tests prove that no uncertain push or command becomes success.
925
926
### Phase 3: Gate and review
927
928
1. Add the independent change classifier and protected-surface enforcement.
929
2. Add immutable focused-test, `mix precommit`, exact-SHA release-gate, and
930
   evaluator receipts.
931
3. Add human promotion from the SCV candidate surface.
932
4. Reconstruct the complete chain from work evidence to Forge deploy receipt.
933
934
**Exit criteria:** A human can review and promote a low-risk SCV candidate with
935
all evidence visible, and every refused class stops before promotion.
936
937
### Phase 4: Automate isolated staging
938
939
Start only after ADR 0007's Forge-canonical cutover and the isolated staging
940
deployment gates pass.
941
942
1. Add typed SCV promotion principals and decision receipts.
943
2. Admit only the low-risk direct-load class.
944
3. Add post-live observation, automatic predecessor promotion, cooldown, daily
945
   deployment budgets, and the circuit breaker.
946
4. Run failure injection for provider, worker, WAL, database, build, fleet,
947
   health, observation, and rollback failures.
948
5. Hold the staging SCV through a defined soak with no unexplained state.
949
950
**Exit criteria:** Staging proves repeated exact-SHA improvements and deliberate
951
failure cases without mixed revisions, lost commits, unreceipted effects, or
952
continued deployment after rollback.
953
954
### Phase 5: Expand admitted scope
955
956
Expand one independent policy revision at a time. Possible later admissions
957
include broader direct-load modules, source-only integration, relup candidates,
958
and rolling replacement in isolated staging. Require a new regression corpus,
959
operator approval, and soak for each expansion.
960
961
Production autonomy requires a separate plan and approval after production
962
deployment itself becomes authorized. Do not infer production approval from a
963
successful staging SCV.
964
965
## Test plan
966
967
### Lifecycle and recovery
968
969
- Prove single-writer lease acquisition, renewal, expiry, and stale-generation
970
  refusal.
971
- Kill the coordinator during every phase and recover from durable state.
972
- Prove completed steps and terminal runs cannot change.
973
- Prove duplicate work evidence, provider events, executor responses, pushes,
974
  and promotion requests do not repeat effects.
975
- Prove uncertain commands fail closed.
976
977
### Workspace and executor
978
979
- Refuse absolute paths, traversal, symlink escape, invalid refs, stale bases,
980
  oversized files, and output overflow.
981
- Prove every run starts from the recorded exact SHA in a clean workspace.
982
- Prove candidate code receives no coordinator, Forge operator, database,
983
  cloud, or user credential.
984
- Prove network and environment profiles fail closed.
985
- Prove cancellation terminates descendant processes and cleans the workspace.
986
987
### Model and tools
988
989
- Validate SCV program and tool-catalog digests at boot.
990
- Reject parallel tool calls when the host supports only serial execution.
991
- Persist a tool request before execution and continue only from its committed
992
  outcome.
993
- Force a bounded report on token, continuation, command, and wall-clock limits.
994
- Run adversarial repository text that asks an SCV to expose secrets, weaken
995
  gates, expand authority, or deploy directly.
996
997
### Change policy
998
999
- Cover every source-only, low-risk, moderate, structural, and protected class.
1000
- Refuse test deletion, assertion weakening, skipped tests, coverage-floor
1001
  reduction, gate edits, renamed protected files, and generated protected
1002
  output.
1003
- Evaluate with the base policy when the candidate changes policy code.
1004
- Require both the narrower SCV allowlist and Forge allowlist.
1005
- Prove an unknown or conflicting classification fails closed.
1006
1007
### Git and receipts
1008
1009
- Prove compare-and-swap run-ref and integration-ref updates.
1010
- Prove a run includes prior admitted SCV changes.
1011
- Prove a failed or reverted candidate does not advance the integration ref.
1012
- Resolve a crash after push from the WAL receipt without pushing twice.
1013
- Reconstruct work item, run, steps, commit, push, gate, target, build, deploy,
1014
  observation, and rollback by immutable refs.
1015
1016
### Deployment
1017
1018
- Prove a push alone never promotes.
1019
- Refuse an SCV promotion without an admitted policy, exact-SHA gate, candidate
1020
  decision, and budget.
1021
- Prove an operator freeze wins every promotion race.
1022
- Prove superseded-target handling and one candidate in flight.
1023
- Inject canary, fleet, readiness, observation, and rollback failures.
1024
- Open the circuit after one rollback and require an operator resume receipt.
1025
1026
### End-to-end proof
1027
1028
Create a fixture defect with a stable failing test. An SCV should:
1029
1030
1. Admit the work item.
1031
2. Read the relevant code and invariant.
1032
3. Add or select the regression test.
1033
4. Make the smallest patch.
1034
5. Pass focused checks and `mix precommit`.
1035
6. Commit and push an exact SHA to its run ref.
1036
7. Pass the independent exact-SHA gate and change policy.
1037
8. Produce a policy-bound promotion receipt.
1038
9. Reach `live` through Forge's transactional lane.
1039
10. Pass the observation window and advance the integration ref.
1040
1041
Repeat with an injected post-live regression. The second proof must promote the
1042
predecessor, verify restoration, leave the integration ref unchanged, and open
1043
the circuit.
1044
1045
## Recommended first release
1046
1047
Ship the first SCV with this deliberately narrow posture:
1048
1049
- one repository: `openagents.com`;
1050
- one logical SCV and one active run;
1051
- `observe` and `propose` modes only;
1052
- operator-created work items plus reproducible owned-gate failures;
1053
- a dedicated SCV program, tool catalog, service principal, and budget ledger;
1054
- a sidecar executor with no production secrets or general network access;
1055
- run branches and complete receipt chains;
1056
- source, diff, focused-test, and `mix precommit` gates;
1057
- no automatic promotion, integration-ref advance, or deployment.
1058
1059
This release validates the difficult lifecycle, sandbox, Git, and evidence
1060
contracts without granting deployment authority. The next release can add
1061
human-reviewed SCV candidates. Staging autodeploy should follow only after the
1062
Forge-canonical and isolated-fleet prerequisites pass.
1063
1064
## Acceptance criteria for staging autonomy
1065
1066
Do not describe an SCV as autonomous until all of these conditions hold:
1067
1068
- An SCV resumes across coordinator and worker loss without duplicate effects.
1069
- Every candidate descends from and conditionally advances one linear
1070
  integration history.
1071
- Candidate code runs without production or operator credentials.
1072
- Host policy, not model output, determines risk, gates, budgets, and promotion.
1073
- Protected changes cannot approve themselves.
1074
- A push cannot directly create a target.
1075
- Every automatic target carries an admitted SCV principal, policy digest,
1076
  candidate ID, exact-SHA gate receipt, and decision digest.
1077
- Forge independently builds, classifies, deploys, and receipts the SHA.
1078
- Post-live verification uses predefined evidence and treats missing evidence as
1079
  failure.
1080
- Automatic rollback promotes and verifies the exact predecessor.
1081
- One rollback opens the circuit and stops further external effects.
1082
- An operator can pause, cancel, reject, and resume an SCV without editing code
1083
  or restarting the fleet.
1084
- The complete staging failure matrix and soak pass against exact retained
1085
  receipts.
1086
1087
An SCV that satisfies these criteria can make rapid iterations without making
1088
the model, workspace, or running process the source of truth. Git commits,
1089
policy decisions, gates, Forge targets, deployment receipts, and verified
1090
runtime state remain the authority at every step.
lib/openagents/forge/build_worker.ex modified +1 -1

@@ -452,7 +452,7 @@ defmodule OpenAgents.Forge.BuildWorker do

452 452
  defp digest_file!(path) do
453 453
    context =
454 454
      path
455
      |> File.stream!([], 64 * 1_024)
455
      |> File.stream!(64 * 1_024, [])
456 456
      |> Enum.reduce(:crypto.hash_init(:sha256), &:crypto.hash_update(&2, &1))
457 457
458 458
    context |> :crypto.hash_final() |> Base.encode16(case: :lower)
test/openagents_web/route_authority_test.exs modified +1 -1

@@ -6,7 +6,7 @@ defmodule OpenAgentsWeb.RouteAuthorityTest do

6 6
  test "every HTTP route and endpoint socket declares one authority, principal, and scope" do
7 7
    inventory = RouteAuthority.inventory()
8 8
9
    assert inventory != []
9
    assert [_ | _] = inventory
10 10
11 11
    Enum.each(inventory, fn entry ->
12 12
      assert entry.class in RouteAuthority.classes(), inspect(entry)

This page updates live while a promote is in flight · changelog