Skip to content

santos/dev: crew family, Pareto crewing and the Model Pool, custom connections - #1108

Merged
AbirAbbas merged 751 commits into
devfrom
santos/dev
Sep 21, 2026
Merged

AbirAbbas merged 751 commits into
devfrom
santos/dev

Conversation

@santoshkumarradha

@santoshkumarradha santoshkumarradha commented Sep 17, 2026

Copy link
Copy Markdown
Member

Integration branch santos/dev into dev. Draft: it is the running index of what the branch carries, and it stays a draft until the worker harness is complete, the independent end-to-end pass on Spark is green, and Santosh has tested it himself.

State

The worker harness

One loop under /task and codeaf do, with a plan store per run, a plan seat, a work seat, a check seat, and a review round after each piece of work (#1109).

Stopping and limits

The task page and the rail

The engine host and sign-in

The profile

Nothing keeps writing after the process closes

Tests and tooling

Also on the branch

Landings

santos/dev carries full check on Spark
164548f #1204, #1205 green
7cda67c #1211 green
b94df33 #1212, #1213, #1214 green
39ffbc5 #1215, #1221 green
fe9eb62 #1222, #1223, #1225, #1227, #1226, #1228, #1229 green
4e176a5 #1230, #1231, #1233 green, first run
608c459 #1220, #1232, #1235, #1240, #1237 green
1cbcb4b #1239, #1238 green, first run
12840ee #1234, #1242, #1243, #1241 green, first run
443c661 #1244, #1245, #1236, #1247, #1246 green, first run
8ed67b7 #1248, #1249 green on run 2; run 1 failed one temp directory cleanup in a package the stack does not touch
c3ab6e0 #1251 green on run 3; two earlier runs each failed one test in a package it does not touch, logs kept
992779e #1250 green, first run
eae7055 #1252, #1254 green, first run
7cefdf1 #1256, #1255 green, first run
24e4385 #1257 green, first run
1c21396 #1258, #1259 green; the lower commit's first run failed one host test in a package the stack does not touch, log kept
09b50f8 #1263, #1260 green, first run
137bb8c #1264 green, first run
761cdab #1267 green
b71454e #1269, #1262 green, first run
d3762f4 #1265, #1261 green, first run
da3368d #1268 green, first run
ec9953e #1271, #1272, #1270 green
70b3a3c #1266, #1274 green, first run
8832349 #1275, #1273, #1278 green, first run
cd2bf32 #1276 green, first run, gated alone
5240a37 #1280 green, first run, gated alone
b293a72 #1279, #1284 green, first run
3a154ce #1282, #1281 green, first run
cb3f0f3 #1283, #1285 green, first run
1357788 #1288 green, first run, gated alone
f796212 #1289 green, first run, gated alone
3d95e70 #1291 green, first run, gated alone
8b68293 #1290 green on rerun, first run red on one unexplained test, both logs kept
c975972 #1293 green, first run, gated alone
3d3b754 #1296 green, first run after a formatting stop, gated alone
1a692da #1298 green, first run, gated alone
952e003 #1304 the interrupted page no longer offers two answers nobody can press; green, gated alone
83de531 #1126 the footprint benchmark, its method and one day's numbers; merged by Santosh
1e0537e #1317 resume cost joins the footprint benchmark; merged by Santosh
9420891 #1305, #1308 a paused run can be carried on, and three proof steps that could not report failure; gated as one batch, 113 ok with one unrelated red in internal/lane cleared by mechanism, not by rerun
e098ca1 #1314, #1315, #1316 the needs-you marks: a permission stop says the line, sign-in and harness asks say what they wait for, the front tab stops marking an answered landing; gated as one batch, EXIT 0, 115 ok, taken under contention
32b4127 one fuzzy matcher for every picker, with subtle match highlighting; merged by Santosh
9b38c2d #1322 a landing already accepted stops saying your call; green, gated alone, EXIT 0, 116 ok

Still to come before this leaves draft


Already on dev through #1194, kept for the record

What follows reached dev on 2026-09-18 as #1194 and is unchanged since.

What a person gets

Feature What it does for you How you use it Where it is
Crew model family: open or all The three crew words (frugal, balanced, max) answer from open-weight models only, or from every model including frontier ones. models.crew.source = open|all (default all), or the open/all switch in /crew. On santos/dev (#1073)
Pareto crewing: crews computed from the live catalog Crew presets are computed from OpenRouter's prices and quality indexes on the cost-quality front, refreshed daily, instead of a hand-picked table. Any seat can be auto. /crew catalog, or models.crew.pick = catalog. On santos/dev (#1093, squash 2fb296b)
Pick mode: table, catalog, learn Choose the fixed table, today's catalog, or a crew learned from judged runs, per role (worker, checker, planner). /crew table|catalog|learn, models.crew.pick. On santos/dev (#1093, squash 2fb296b)
Learn: judged runs move the picks After each chat /task run an independent judge scores every seat (billed to a judge seat). Scores go to your own sheet and feed the picker next time, blended with the pool by observation count, so your own runs improve your own picks first. Starts from a seed built from 145 real runs, so learning works on day one. A seat you type by hand is judged too. Automatic under pick = learn. codeaf pool show lists what the picker knows. On santos/dev (#1093, squash 2fb296b)
Model Pool: collective, text-free evidence Every install can share its score rows through a relay and pull a signed index back, so everyone picks better because the others ran. Rows are computed numbers and ids under a per-install nonce: never code, prompts, paths or identity. model_pool on|read|off (default on, notice shown once). codeaf pool show|status|verify [--json]. On santos/dev (#1093, squash 2fb296b)
Public relay and GitHub mirror The relay Worker ships in-repo under relay/ with a runbook; it is live at https://codeaf.agentfield.ai/pool. The signed index is mirrored to the model-pool branch of this repo so anyone can watch it update. A team can run its own private relay with its own key (CODEAF_MODEL_POOL_RELAY_URL, _MIRROR_URL, _PUBLIC_KEY). Nothing to do; works out of the box. On santos/dev (#1093, squash 2fb296b)
The paper docs/design/model-pool/pareto-crewing.pdf ("Pareto Crewing"): the objective, estimator and picker, with figures from real runs. Read it. On santos/dev (#1093, squash 2fb296b)
Named, multiple, switchable custom connections Connect several OpenAI-compatible endpoints (local models, proxies), each with its own name, key and model list; switch between them, including mid-session; rename one and every stored pick follows. /model groups models under each connection's name. Switching onto a keyless default service is refused with a reason. /connectCustom OpenAI-compatible API; /settings → Providers → add / edit / active connection. On santos/dev (#1092, squash 825ef2c; Santosh e2e-tested locally against a local OpenAI-compatible proxy)
Worker harness: one loop under /task and do The internal hands (patch, doc, web, image) become one worker loop with a plan store, shared by the chat /task door and codeaf do. No new surface; fewer steps and lower cost per task (smoke: 4/4 parts in 28 steps vs 3/4 in 42). #1109, draft, wave 1 landing

Bug fixes on the branch (question answer-box keys, landing answer fate, retry avoiding the failed upstream, recovery evidence, taxonomy lane read, and, with #1092, mid-session /connect now reaching the live conversation on the engine road, a pre-existing hole) are not listed above; each has its own change note under docs/changes/unreleased/.

Known limits, stated on purpose

  • The learned picker is greedy on its estimate; there is no exploration yet, so a model that is never picked is never scored. The next algorithm pass (Thompson sampling, hierarchical cap, drift) is a follow-up PR.
  • The judge runs on the chat /task door only; codeaf do, ask and remote sessions do not feed the pool yet.
  • The pool index is read at start-up (24 h cache); codeaf pool verify pulls a fresh copy, read at the next start.
  • A pool cell becomes readable at 3 rows, from one install or three (the paper says three installs); follow-up.

Relay status

Live at https://codeaf.agentfield.ai/pool (healthz 200, index signed, codeaf pool verify good). The model-pool branch exists and carries the current signed index (refreshed hourly by a stand-in job until #1116 puts the workflow on dev). The pool is primed with the same measured runs the embedded seed encodes, so the published index carries real cells from day one.

End-to-end pass on Spark (independent session, done)

All 12 steps pass on #1093's head (cf4ea35; final fd29d79 differs by a runbook sentence). Report: spark:~/src/e2e-learn-2026-09-17.md with KNOWN-LIMITS.md and evidence/INDEX.md beside it; 18 capped runs, about $26.

Step Result
1 fresh install, learn, judged tasks, own sheet + outbox flushed pass (first pass failed: judge picked a rate-limited free model; fixed)
2 rows reach the relay, fold, publish pass (index 1789681435, 19 cells)
3 fresh install D reads the pool and picks differently from the seed pass (pool: worker gpt-5.6-sol; seed only: glm-5.3-flash)
4 privacy: rows are numbers/ids/nonce; read and off send nothing pass
5 resilience: bogus relay → mirror → seed; task still runs; verify good pass
6 manual /model override under learn is still judged pass
7 crew words × open/all, table vs catalog, real tasks on frugal-open and max-all pass
8 auto/catalog/learn seats resolve on headless doors pass (first pass fell to the table; fixed)
9 /crew chooser and /settings pool row with notice pass (note: pick mode is a settings row, not a chooser switch)
10 `pool show status
11 codeaf models, --refresh, daily cache pass
12 headless do/exec under a learn crew pass (fixed with 8; judge on headless doors stays a follow-up)

Follow-ups from the e2e, now on santos/dev: a task's thread stops its parts before closing and a machinery cut records its reason (#1118); a decision card under --yolo takes its default (#1117); the auto crew row shows the rung it actually resolved to (#1119); a bare auto row under picked from = catalog reads the catalog's figures alone (#1120). codeaf do seats from the settings the run will use, so a fresh profile with the key in the environment reads the catalog, not the table (#1122); the relay accepts the exec and run doors (#1121); a launch's pool errands stop when the process closes instead of writing into a closed profile (#1124); a launch compiles internal/verify's patterns on first use and the engine host runs on a capped scheduler (#1125); the client's min_installs floor counts installs, as the relay's does (#1127); the relay folds a retried batch once by its nonce (#1128), carries an acceptable metric beside the judge score (#1129), publishes only cells that meet the floor (#1131) and writes once per cell per batch (#1136); a refused row leaves the outbox and the file is compacted (#1130, #1133); the pool errand tracker's critical sections unlock from a defer (#1134); a run a process death left unjudged, and any headless door's landing, is judged on the next chat start (#1132). the finished-tree check reads a project cloned into a subdirectory (#1135); the exec, run and do doors leave a pending record for the sweep (#1137, #1138, #1139); the wall admits a first round it cannot cost and refuses an overrun it cannot hold, so a long codeaf do lands instead of re-planning to the wall (#1144), with the delivery gate over a grown job root pinned by a test (#1147); pool show, pool verify and pool status print both metrics, the pending judge records and the last sweep (#1141, #1142); a test's crash fixture stays in a profile the test owns, and the package's test floor clears any profile the environment named, and the opening-hint test reads a profile it made and pins the greeting contract both ways (#1145, #1150, #1153), and tui3's package law makes every surface-building helper name a profile of its own (#1173); the manual's status row says whose ledger it is (#1143); the seed index is regenerated from the live relay by one in-repo command, so a fresh install's fallback is a verified copy of the pool (#1148); pool show --cells lists the held index's cells with their installs and dims (#1149); a landing that leaves gitignored build products behind stays complete and the reconciled row names them (#1152); codeaf version on an unstamped binary names the condition, no .git directory for the toolchain to read, instead of blaming a Makefile target (#1151). pool status says when the relay dropped a row and why, instead of the row vanishing silently (#1154); a job teardown records the group leader's identity at launch and never signals a process group the kernel has since handed to someone else (#1155); a codeaf do continuation after an overrun continues the plan it has from the recorded turns instead of re-planning from scratch (#1156); the process sets a soft memory limit of half the machine's memory, nothing below a 512 MiB floor, an explicit GOMEMLIMIT untouched, carried into engine --daemon (#1158), and the bound is the smallest finite one of physical memory and the cgroup v2/v1 limits walked from the process's own level to the root (#1162); pool status says when this run cached the index, so the seed-to-cache flip is visible in the run that caused it (#1161). The pool's copy of a seat's id is the bare vendor/id, so a fresh install on the default model (whose seat carries codeaf's alias marker) no longer has its first rows refused by the relay, and the judge picker's same-vendor exclusion works on it (#1163); no cmd/codeaf test can push rows to the default relay, the package floor pins a dead address (#1164); the relay bounds each judge's fitted severity to ±10, clamps every adjusted score to the rubric, publishes mean and sd on one scale and falls back to the median when spread is zero, which is what had published a cell at 103.5 (#1165); the relay can purge stored rows from a fixture vendor and refuse vendors not in ALLOWED_VENDORS (#1167), with ALLOWED_VENDORS set to the catalog's vendors plus codeaf (#1177, Worker version 47102a26; 624 fixture keys purged from the live store); the built-in seed is regenerated from the relay's first clean publish after the purge, 18 cells, no fixture rows (#1180); a job's shell no longer inherits the host's TMUX handle and gets a profile-owned TMUX_TMPDIR, so a command the model runs cannot kill the terminal hosting codeaf (#1166); an unattended run (--yolo, nobody to answer) is refused a git push, merge or rebase that would move a branch it did not create, including the branch the checkout began on, while its own task branch stays pushable and attended sessions are untouched (#1168, the road it took is in #1160's note); pool status says the install's identity, the judged total across all doors, and "none yet" when no landing has been judged (#1169); an exec run's worker spend is minted into the usage ledger at the door, one row per run, so the status row, the cap and the pool row count it (#1170); the task door honours a stored models.crew word (every arm had resolved to balanced) and a pinned tier row survives pick=learn instead of being recomputed (#1171); a landing card raised on the check road under --yolo takes its default and records that the dial answered and that the check ran out of time, instead of parking a headless run forever, while conflict, shift and ground landings still park and now say so in the record (#1174); the model catalog warm is seated on the profile's start-up errand tracker so a process close joins it, instead of a warm outliving the process and writing its cache into whatever state root was current (#1179); the auditor's calls tag themselves auditor with the node they check, and its usage rows carry the auditor role and the task, so a ledger tells an audit call from a session turn (#1181); a headless do --json whose node ends failed or unverified names the kept branch and the verdict in its envelope, so nobody digs through tasks.json for kept work (#1182); the settle turn a landing note wakes under task.settle auto runs under the checker's own bound, a call ceiling and a share of the run's cap, one call for a clean tree with no declared check, and records "not settled within its bound" instead of running to the wall, which had been a one-to-two-dollar tax on most headless runs (#1183); the chat-side tasks text and a resumed session's record line name a failed or unverified node's kept task/<slug> branch and its verdict word, the same two words the headless envelope carries (#1184); the identical-effects test is deterministic, its forty saves carrying distinct call signatures so the turn's repetition guard cannot end the turn and the leash is the only thing the assertion is about, which closes a margin-of-one race that had read as a load flake since #284 (#1185); a non-verified codeaf exec or codeaf run names its kept branch and verdict in its envelope through the one result-envelope seam the three headless doors share, so do, exec and run answer alike (#1186); a task worker parked on a command it started (a bash call the 30-second background clock promoted to a job) waited the node's whole allowance when the job never ended, which is what held a DOE run silent for 53 minutes under a live cap; the park now has a bound of its own, a third of the allowance, posts "the park was not settled within its bound" and hands the turn back while the command keeps running, with the diagnosis at docs/notes/yolo-task-worker-parked-on-promoted-bash.md (#1187); the model pool runbook's plan line says metered, not capped (#1188, #1189, docs only); the status row says working while the door's task subtree turns, read from the same signal the tab strip draws from so the two cannot disagree, with the manual's screen and task-page sections moved to match (#1191); a job subtree is cut when it passes a CPU or process bound, 60% of the machine sustained for 30 s or 128 processes when nothing else bounds it, the process backstop alone under a cgroup or affinity quota, and the run learns it as "the job subtree was not settled within its bound", which is what a model that spawns infinite loops under a dollar cap had escaped (#1190); a note records what a task's audit step can execute and where that boundary is set, the declared checks plus read-only git and coreutils, which is why a brief that declares no check leaves a cautious auditor nothing it may run (#1192, docs only); the prose lexer registry loads a curated set of forty languages on first use instead of parsing all 279 of chroma's lexers at init, plain text for the rest, which takes about 3 ms off cold start and 2 MB off the binary, with chroma's files and licence carried beside the copies (#1193). Still open, PRs in progress: the footprint benchmark method and table under docs/benchmarks (#1126); Pareto crewing v2 with a model-free grader (#1123, Santosh reviews). The "task-branch merge fails 1 in 3" observation was the test driver killing runs mid-check, not a product defect.

Related, not on this branch

Before this leaves draft

Slice line

This branch is held at d39a4e308 as a reviewable slice. Two landings since the slice was first cut: the drafted-with footer links to the CodeAF repository rather than to agentfield (#1329), and a held landing offers its answer and stops demanding one (#1331). santos/dev2 carries the same head and is where work goes once Abir picks this up. Work that lands after this goes on the next shared branch so the slice stops growing under review.

AbirAbbas added a commit that referenced this pull request Sep 18, 2026
… custom connections (#1194)

Lands the integration branch santos/dev (#1108, head 6db5125, 152 commits over dev) on dev as one squash, plus two fixes that kept its touched-packages job red: the pool start-up test empties the CI word it is not about (649bb4b), and a zombie leader no longer keeps a job's process group alive on Linux, which had delayed every finished background job by the two-second termination grace since #1155 (053e6a9). #1108 carries the full account; the 94 change entries under docs/changes/unreleased/ are the per-change record.

The commits folded in, oldest first:

5c413ef Keep archived evidence readable while tasks finish
3705a0c Document recovery evidence retrieval change
7834cc7 tui3, manual: a question's own answer box answers to the whole caret vocabulary
99d66e9 task: Add open/all source toggle to crew config
c1e4cb5 docs: the change entry for the crew model family toggle
e63b439 crew: round-1 review fixes
304a5a2 crew: round-2 review fix
65477c0 task: Add open/all selector to /crew TUI picker
9189ef7 Merge branch 'task/add-open-all-selector-to-crew-tu-9e8cc0' into feat/crew-open-all-toggle
ba9c12d crew: reconcile the picker onto config's source helpers
ea05bc0 crew: a chosen family counts as an answered crew
e1a037e Merge remote-tracking branch 'origin/dev' into feat/crew-open-all-toggle
f6c2924 crew: the open table takes the picks we landed on
eff44b5 docs: the change entry carries the open-table move and the family gate
71ce9be crew: the family reaches the fallback, the settings skin, and the row's line
b8ca5f5 crew: round-1 review fixes on the new table
8db5708 crew: round-2 review fixes
ce9759e crew: round-3 review fixes
956e1f5 crew: round-4 review fixes
37ad27f crew: fixes from the first Opus round
6ffb958 crew: the fixes from the Opus confirmation round
72b0274 crew: the comments the atomic write left behind
70d0e20 crew: round-four fixes from the Opus review
234300e Merge branch 'dev' into feat/crew-open-all-toggle
894292f crew: round-five fixes from the Opus review
fba77f9 An answered landing carries its answer's fate when it is raised again, and an in-flight settle holds the question down (#1077)
e694450 Changelog entry for the landing-answer fate (#1086)
64ab7ea crew: round-six fixes from the Opus review
0700164 crew: round-seven fixes from the Opus review
af09aa8 The review round's four findings: claim ownership, decider changes, recordable answers, plain words
c24af02 crew: round-eight fixes from the Opus review
289d372 The tests the landing's answer carries: fate, hold-down, redraw, claim doors
7e38346 The take-down comes before every mint, and the tui3 walk reads the newest card
dbe1be0 The review rounds' last word: the release owns its claim, and the docs say what the code does
4cc4945 The Opus review: a release owns its generation, and every failed round still says its word
a5cce0d The done card says who is deciding, so the clause is a duplication, not the fact
6bd276e Merge fix/1077-landing-reraise into santos/dev
2710169 Merge feat/crew-open-all-toggle into santos/dev
b459399 Merge fix/question-answer-box-caret-keys into santos/dev
a384920 Merge codex/recovery-evidence into santos/dev
dc22e64 cmd/codeaf: the role-door tests read the shipped mastermind instead of a copy of it
39f528f release: the pin-example check reads the guide the examples moved to
6439d3c session, tui3: gofmt the three files santos/dev merged unformatted
8540379 tui3: a question's answer box kills to the end of the line on ctrl+k
d93ec61 session: the late-attach proposal test holds the work until the replay is read
a81ead4 session: the landing-fate tests read today's clock instead of a fixed date
8fe4fff provider: the retry-avoid list rides the call's context into the knobs
a266da3 provider: the retry-avoid veto narrows the wire through one field law
197d893 exec: a node retry asks the router to avoid the lanes that failed it
2d02cc0 provider: the set-servable law's doc comment keeps its paragraph break
e1d0e75 exec: the retry test names its helper and spells its wall in time units
e7d65a5 docs: the change note for the retry that avoids the failed upstream
11decbd Merge fix/provider-failover into santos/dev
c099a51 Merge dev into santos/dev
e47d92b provider, exec: the lane a failure names is a wire fact provider reads (#1105)
396be97 Merge dev into santos/dev
3b97f50 Merge dev into santos/dev
ea3b5c9 telemetry: the send path drops events that cannot name their version
573b288 cmd/codeaf: keep smoke runs from sending usage counts
e8d627c docs: the change note for the usage-count audit fixes
11bec8d chat: a hosted session's usage counts are read off the events it receives
b9d75b3 chat: the hosted-count tee is tested against the tally it must produce
39fa08d telemetry: stamp a version in the lifecycle tests
681e2ad chat: count the hosted boot agent once, and conversations opened beside
825ef2c connect, settings: named, multiple, switchable custom connections (#1092)
f4d3f9a Merge branch 'dev' into fix/telemetry-audit
76fec37 ci: the Model Pool mirror workflow and the signature check it runs
2fb296b crew: Pareto crewing, the learned picker and the Model Pool (#1093)
a065ebb Merge remote-tracking branch 'origin/dev' into santos/dev
80ac250 Merge remote-tracking branch 'origin/feat/pool-mirror-workflow' into santos/dev
6092393 Merge remote-tracking branch 'origin/fix/telemetry-audit' into santos/dev
ef808dd session: a decision card under --yolo takes its default (#1117)
70a9e1f session: a task's thread stops its parts before it closes, and a machinery cut says so (#1118)
c4df8f3 crew: an auto seat's rung names the pick that ran, once, for both doors (#1119)
a9fcf9b crew: a bare auto row under picked from = catalog reads the catalog's figures alone (#1120)
13be0dc relay: accept the exec and run doors, not only task and do (#1121)
90f29ae changes: name the #1120 note by its PR and stamp the pr field
0b77926 do: seat from the settings the run will use (#1122)
32148d8 changes: stamp pr: 1120 on the #1120 note
113ed62 pool: a launch's start-up errands stop when the process closes (#1124)
6c98ddc verify, codeaf: a launch compiles fewer patterns and a surface holds fewer Ps (#1125)
50fe6a3 pool: the min_installs floor counts installs on the client (#1127)
076cde8 relay: fold a resubmitted row once by its nonce (#1128)
8339ae7 relay: carry the acceptable metric beside role_quality (#1129)
0367c44 outbox: a refused row leaves the outbox and the rest of its batch is sent (#1130)
09aecd2 relay: the index holds only the cells that meet the min_installs floor (#1131)
ae7c05d outbox: a Send compacts the file to the rows it still holds (#1133)
9e24f72 pool: the errand tracker's critical sections unlock from a defer (#1134)
358a569 relay: fold a submit batch with one read and one write per distinct cell and day (#1136)
a1d2eb1 pool: judge landed-but-unjudged runs on the next chat start (#1132)
3c67228 verify: take the finished-tree check at a project cloned into a subdirectory (#1135)
263625b exec: a headless run leaves a pending judge record at its tail (#1137)
8be373d run: the headless task door leaves a pending judge record (#1138)
a336b24 do: leave a pending landing for the Model Pool's judge (#1139)
8820a56 cmd/codeaf: one readPendingRows test helper, not two (#1140)
49760fb pool: show and verify print both metrics the index carries (#1141)
6eaeb25 pool: status shows the pending judge records and the last sweep (#1142)
7c00b5f codeaf: a test's crash fixture stays in a profile the test owns (#1145)
fc2eb35 manual: the status row's ledger is the tree's, not the conversation's (#1143)
a06b53a test: pin the delivery gate over a grown job root (#1147)
7231347 resident: the wall refuses a first replan round it cannot hold (#1144)
5e25755 cmd/codeaf: the package's test floor clears a profile the environment named
4dd1c56 Revert "cmd/codeaf: the package's test floor clears a profile the environment named"
9075f86 pool: the seed index is regenerated from the relay by seedgen (#1148)
a5c44f3 pool: show --cells lists the held index's cells with installs and dims (#1149)
8259967 cmd/codeaf: the package test floor clears a profile the environment named (#1150)
81568aa session: report gitignored build products a run leaves, without gating on them (#1152)
da49105 internal/tui3: the opening hint test owns its profile and pins both doors
72de3bc docs/changes: #1151 — the opening hint test owns its profile
499083d Revert "docs/changes: #1151 — the opening hint test owns its profile"
beb31ec Revert "internal/tui3: the opening hint test owns its profile and pins both doors"
25b8b7d version: an unstamped binary names the condition, not a Makefile target (#1151)
c833b42 pool: a dropped row keeps the relay's reason and status shows it (#1154)
cbb7d6d processgroup: verify a job group's identity before signalling it (#1155)
efe498e internal/tui3: the opening hint test owns its profile and pins both doors (#1153)
17b3b0e do: a continuation continues the plan it has instead of re-planning (#1156)
22cf3ce changes: correct the #1155 and #1156 notes' pr fields (#1157)
8e85b0f cmd/codeaf: a surface runs under a soft memory limit drawn from the machine (#1158)
ef1e927 task: status row accounting (#1159)
44f572e pool: status's index line says what this run's fetch did to the cache (#1161)
3600fc9 pool: the pool's copy of a seat's id is the bare vendor/id (#1163)
a2ebd13 cmd/codeaf: no test can push pool rows to the default relay (#1164)
4fb7f05 docs: the road a chat door bash push took under --yolo (#1160)
69219a1 cmd/codeaf: the soft memory limit is the smallest real bound, not the host s memory (#1162)
2b155b4 relay: bound judge severity, clamp adjusted scores, publish mean and sd on one scale (#1165)
2fb5532 relay: purge fixture vendors and refuse them at the door (#1167)
5750418 exec: a job's shell cannot reach the tmux server hosting codeaf (#1166)
eb7b465 internal/session: an unattended run may not move a branch it did not create (#1168)
80f7706 pool: status says the identity, the judged total and that nothing has landed (#1169)
1cb7333 exec: an exec run's worker spend reaches the usage ledger (#1170)
96bb940 changes: the #1169 note's title parses and reads as one line (#1172)
627236d config: the task door honors a stored crew word, and a pin survives the learned pick (#1171)
593092b internal/tui3: a test may not read a profile it did not create (#1173)
ad8be1e session: a landing card under --yolo takes its default instead of parking the run (#1174)
03d49a6 gitignore: the .furrow ids the engine writes into a workspace (#1175)
46edbdd internal/tui3: gofmt the profile-law helpers from #1173 (#1176)
3179e36 relay: ALLOWED_VENDORS names the catalog's vendors and codeaf (#1177)
397a145 docs: why a settled /task leaves commits on task/<slug>, not the worktree branch (#1178)
915ff3a pool: regenerate the seed index from the relay's first clean publish (#1180)
d9b4243 cmd/codeaf: the model warm stops when the process closes (#1179)
bd2dcf3 session: the auditor's calls say they are the auditor's and which node they check (#1181)
c6ed47c task: json unverified branch (#1182)
360352a session: a settle turn runs under the checker's own bound, not the run's wall (#1183)
814e1af session: the tasks text and record line name a failed/unverified node's kept branch and verdict (#1184)
7c25a26 internal/session: the identical-effects test stops riding the turn guard (#1185)
6e7d0ed cmd/codeaf: exec and run envelopes name a non-verified node's kept branch and verdict (#1186)
f4079d7 session: bound a worker's park on a promoted command (#1187)
994689f docs: the model pool runbook names the Workers Paid plan and what it costs (#1188)
c315be1 docs: the runbook's plan line says metered, not capped, and nothing more (#1189)
2e80c60 internal/tui3: the status word says working while a task subtree turns (#1191)
99363ca session: bound a job subtree's CPU and process count (#1190)
6db5125 docs: what the audit check seat can execute, and where that boundary is set (#1192)
649bb4b cmd/codeaf: the pool start-up test empties the CI word it is not about
053e6a9 processgroup: a zombie leader does not keep a job's group alive
b4e66f5 docs/changes: the entry for #1194

Co-Authored-By: Santosh kumar <29346072+santoshkumarradha@users.noreply.github.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@AbirAbbas

Copy link
Copy Markdown
Collaborator

Merged into dev through #1194 at Abir's request, as one squash of this branch's head 6db5125 plus two fixes for the tests that kept touched packages red here on every run today:

  • TestWirePoolIndexStartsTheRefreshAndThePush counted one errand under GitHub Actions because poolcfg.Resolve reads the runner's CI=true as read-only; the test now empties CI for its duration.
  • TestTurnBoundaryReportsRunningAndOneTerminalTransition overran its 4 s bound because, since processgroup: verify a job group's identity before signalling it #1155, Group.Alive probed a group whose leader is still a zombie with kill(-pgid, 0), so every finished background job on Linux waited out the 2 s termination grace before being reported done. Alive reads the group's members from /proc now and ignores zombies (internal/processgroup/members_linux.go), with contract tests.

Nothing on santos/dev itself was changed. Its 152 commits are on dev as one squash, so the next step on this branch is a reset onto dev; #1109, #1123 and #1126 still base on it. #1111 and #1116 are contained in what landed.

One observation from the smoke, not a blocker: codeaf do … --json wrote hi.txt and passed its gate, but the checklist row read a file named hi.txt exists — not reached · the run changed hi.txt.

santoshkumarradha and others added 27 commits September 18, 2026 12:07
The spine draws its samples in concurrent goroutines by design, and two tests counted the fake client's calls with a plain int. Under load an increment could be lost, so no sample saw the third call and the draw read 3,3,3. The counter is now atomic.Int64 in levels_test.go and named_test.go; every assertion is unchanged. Test-only.

Evidence (Spark): -race on the unfixed tree reports the race at levels_test.go:87 from spineWithProgress goroutines, product clean; after the fix go test ./internal/plan -race -count=200 ok; build, changes check, guard and namelaw green on 65249bf, tree aa25dee before and after.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
santoshkumarradha and others added 17 commits September 20, 2026 12:43
…nd (#1290)

A firing copied the conversation's approval policy, which on a fresh install
prompts a person, while also being told it was inside a task where no person
exists. Every decision came back a refusal. What survived was the read-only
lift, so a watch could not run one shell command, not even one the person had
explicitly allowed. A real watch fired thirty-nine times over four days, spent
real money and did nothing.

A firing now carries the policy a task node carries: allow everything except
approval's critical floor. The floor still turns an allow into a prompt for the
shapes that destroy a disk or drop the machine, and a prompt with nobody to ask
is a refusal the firing can read and report. The calls that act in the person's
name outside this machine stay refused, and a bash call whose arguments cannot
be read stays refused.

The owner decided this on 2026-09-20, after being shown the shape and what it
widens. The probe road is deliberately untouched: it is a different function
with a different risk, and a watch's probe command never meets this gate at all.
A conversation whose sub-harness had stopped on a question reported that it
was working. The question was registered and open, but the reading that
decides whether a person is needed never looked at that lane, so no row
appeared on home, no mark was drawn, and nothing moved until somebody
happened to open the conversation. Work stopped in silence.

Two halves, and either alone is worse than neither. The reading now counts
that lane beside the others. And the lane banks its question at the desk the
sentence is read from, rather than only raising it, so the row carries what
is being asked instead of an attention mark with nothing under it.

Three sibling lanes share the shape and are deliberately left: sign-in, the
sub-harness offer and the harness design card all raise without banking, so
each draws a mark with no sentence today. They belong to a change about that
desk, and a fix that quietly repaired four lanes while claiming one would be
a fix nobody could review.

The reading also split along the seam its own comments had drawn. One part
reads every lane under the lock and hands back what the answer is made of;
the other decides which answer a person should read first. The lock
discipline is unchanged and no lane's behaviour moved.
)

A row that was live when the process went away came back stamped failed and
stopped, and the run's row said it had ended when codeaf closed. Two untrue
things in one line: that the work went wrong, and that somebody ended it.

It now reads interrupted, which means nothing is driving it and everything
it did is kept. The word lives on the reading rather than in the surfaces,
so one rung on the state ladder and one on the presence ladder give the
tier, the mark, the two answers and the sentence, and no surface grows a
table of its own.

Continuing sits in the person's tier and is never the model's answer,
because continuing spends money.

The ask and its two answers are carried here rather than in the cell that
draws them, because a your-call row is required by its own type to carry an
ask, and splitting it would ship a card with no answers for one gate cycle.

The old sentence is deleted rather than left unused, and the manual page
that quoted it is corrected in the same change.

A known window while the piece that continues work is still to come: a
conversation that genuinely ends cuts its run, and a reopen still stamps
that run failed. That is named rather than left to be discovered.

The const block is reformatted here because the new sentences widened it.
It reached a gate unformatted because the check that should have caught it
read an exit code from a tool that reports by printing.
…in (#1298)

A run's stored row carried its number, title, state, cost and timings, and
nothing about where its work is. No folder, no branch, no ground. After a
reopen the product knew a run had existed and had no idea where to look for
what it did.

The row now carries the copy, and the road that makes a copy is never asked
twice. That road mints a new branch name with a random suffix on every call
and clears the directory it is handed, which is safe only because the sole
thing that can be sitting there is that run's own wreckage. Called a second
time for a run being resumed it would delete the work and hand back an empty
branch that looks correct.

A run with no copy recorded is refused, in words naming what is missing, and
nothing on that road may carve a directory, cut a branch or derive a path.
Every row on disk today predates this field, so that refusal is the answer
those runs get forever rather than a stopgap. A copy gone from disk is
refused too, naming the branch, because the branch is the one thing a person
can still act on.

The directory is derived from the run's number. The branch is not: it is
random and nothing else writes it down. That asymmetry is the whole reason
this record exists, and a reader who believes both are reconstructible will
delete this change.

Every publish replaces the row and only the first knows where the work is,
so the carry-forward lives inside the one function every publisher goes
through rather than in its callers. A notice naming a copy of its own still
wins, as the more recent reading.
…give (#1304)

The page said an interrupted row offers two answers, continue it or leave
it. Nothing draws them. There are three readers of a task's two answers in
the tree and a run's row reaches none of them: it never enters the index the
needs panel reads, it never holds the state the landing question requires,
and it never matches the question the record card looks for.

What is true stays. The row does wear the asking mark and does wait for a
person, through the surface's own node rather than through the record, so
that clause is kept rather than softened away with the rest.

In place of the promise the page states the limit: nothing picks an
interrupted run up yet, no key, command or background pass does, and nothing
ever will without being asked. That is the sentence a person came to the
page for.
* task: benchmark methodology

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* readme: the one-binary claim is measured, not asserted

Startup, on-disk footprint and idle memory against six other agent CLIs, placed
next to the sentence they substantiate. Memory is PSS because the two-process
design shares one binary's text; the metric the fastest competitor wins is in
the table rather than only in the linked write-up.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* readme: cut an opening sentence the window section already makes, and a badge

The second sentence of the opening paragraph restated the one-window-and-tasks
framing that "One window for every project" and "What a factory is" carry in
full. The model-pool badge repeated a link the section's own prose and the
design document already give. Kept as its own commit so the cuts can be
dropped without losing the measurements above.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* docs, readme: the footprint method, the full named table, and a five-row summary

The cell wrote measure-cli.sh but no methodology, and a three-row table that omitted the two strongest honest rows while keeping two we lose. This adds docs/benchmarks/README.md (method, the four rules the numbers depend on, and the known limits including the wakeup figure being unusable), docs/benchmarks/results-2026-09-17.md (the full table with every CLI named, including the three rows where another binary beats this one and the row where it could not be measured at all), and replaces the README summary with five rows.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* changes: note the footprint table entry for #1126

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* readme: restore the hero block the footprint section displaced

My own edit script matched from the new heading to the next ## and so removed everything between, which is the hero: the early-preview notice with its Discord and issue links, the overview screenshot and its alt text, the demo video and its caption. They are restored untouched and the footprint section now sits between them and Install, which is where it belongs anyway. The em dash in the closing sentence is gone, and the table ranges read x to y rather than using a dash.

The two listed README cuts are unchanged and remain in their own commit (cbc1d0d).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* readme: leave the top-level README as it was; the footprint table stays under docs/benchmarks

Santosh reviewed the README hunk and asked for it to go. The method, the
script and the full named table remain in docs/benchmarks/ and the change
note now says so.

* docs/benchmarks: the results file, not the README, is the script's output

* changes: name the #1126 note for what it notes

* docs/benchmarks: record the machine's load average around every timed phase

load1/5/15 land in phase=meta, and again immediately before and after the
startup and idle windows, read from /proc/loadavg with a builtin read.
Telemetry only: no default changes, no new dependency.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* docs/benchmarks: the README now says what the script does and how to invoke it

The example invocation was flags the script does not have (--name/--home
parse as NAME and HOME), cold start was documented as best-of-N with no
word of the discarded warm-ups or the warm cache behind it, and the README
claimed every table in the results file was this script's output when the
on-disk and one-turn tables are not. Load recording documented.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* docs/benchmarks: the method review — per-column verdicts against the competitor test

Three columns fail the read (version rows contradicted by the box, on-disk
rule stated nowhere and unreproducible, one-turn sampler uncommitted); the
startup, frame, idle and strace columns hold with caveats. Facts 3 and 4
caveated, not invalidating. Evidence named for everything still open.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* docs/benchmarks: the rerun that tests the review, and what it settles

The method review above was taken by reading. This adds the measuring: all
seven CLIs again in one session on a quiet box, at ONE build for every column
(4f002ca2), with the one-minute load recorded beside every figure and sampled
six times across a full minute before the run began.

Startup and first frame stand and are published with the load beside them.
Several figures move a long way from 2026-09-17 — claude's first frame 572 to
429 ms, cursor-agent's 1,298 to 592, pi's startup 330-360 to 146, omp's first
frame 400 to 1,254 the other way — which is the load finding demonstrated
rather than argued.

The idle memory column's verdict MOVES, from "fair with caveat" to "cannot
decide, and not reproducible as it stands". Every one of the seven measured
procs=1, where the table has CodeAF at 2 and cursor-agent at 5. It cuts both
ways and is nobody's win: CodeAF's PSS came out 38.7 MB against 66.4, and
cursor-agent's 194.4 against 412.7. Three explanations were tested and killed —
not the sweep or the choice of HOME (sweep_home is teardown only, and a
controlled pair under a dedicated home and under the operator's own both gave
procs=1), not a product change (cursor-agent is the same version string), and
not a different script (the script was committed before the results). What is
left is the invocations, which were never recorded.

Also records what each CLI actually resolves to. opencode is not on a login
PATH at all and exists as two different products; pi is a JavaScript bundle;
codex and cursor-agent resolve to different binaries depending on how the
measuring shell started. The header's own two-build split needs no caveat: the
package-init changes have landed, so measuring at one build removes it.

The run's log and the rig it was taken with are committed beside the note, so
the next person to ask this question starts from a recorded rig rather than
from a reconstruction.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* benchmarks: make CLI teardown workspace-specific

* docs: correct defective memory measurement

* benchmarks: split teardown survivor accounting

* docs: repair benchmark method review prose

* docs: record c245 teardown accounting checks

* benchmarks: retain launched process ancestry

* benchmarks: state which processes the idle figure sums

The script sums the processes of the launched tree that are alive at the
last sample of the idle window, and the README did not say so. A reader
comparing two CLIs could not tell whether a helper that exited during the
window was charged or not.

The rule is stated with its reason: a steady-state figure must not depend
on how long the window was, which is what summing every process ever seen
would produce. The transient cost is reported in peak RSS instead, and
procs_seen is named beside procs so a CLI that churns helpers is visible
rather than hidden behind a single count.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* docs: the 2026-09-20 footprint tables, their drivers, and the hero figure

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* docs: classify benchmarks under docs/benchmarks/performance

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* docs: sharper performance benchmark gist

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* docs: neutral review voice, explicit multiples in the gist

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* docs: gist as bullets

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

---------

Co-authored-by: codeaf <agentfield-bot@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* docs: resume cost joins the footprint benchmark

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* docs: change note for the resume table

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* docs: the quick table joins the gist

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* docs: table outside the list, blank lines around it

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

---------

Co-authored-by: codeaf <agentfield-bot@users.noreply.github.com>
…y its work is already in

An interrupted run's store holds every step it took and, since the copy record
landed, its row knows where that work is. This is the one door that starts work
on it again: it adopts the recorded working copy and the store already on disk,
hands both to the same engine under the same seats, and lets the supervisor's
ordinary next pass release the claims whose owner is gone. No resume machinery,
no second scheduler.

IT HAS NO CALLER, ON PURPOSE. Continuing seats workers and spends money, and a
door that spends money reachable before anything means to reach it is not a
smaller version of the feature but a worse thing than no door. The card that
offers the choice is the next change; until it lands a person cannot reach this
from any surface.

The refusals name what is in the way: a run that never stopped, a number with no
run behind it, a run that already said its last word, and a copy that was never
written down or is gone. The refusal for a run with no copy is the SAME sentence
the row shows where the offer would otherwise be, taken from one constant, so
the reading and the answer cannot drift into two spellings of one fact.

The reading gains that sentence as a sentence rather than as a flag, and the run
spec is now built in one place so the start road and this door cannot diverge
the day a cap moves.

Every refusal is proved twice: that it said no in the person's words, and that
nothing started. The test that matters puts a worker's file in a run's copy,
carries the run on, and reads the file back: the road this door could have taken
instead clears that directory (#1298 shows it doing so).

Fourth cell of the settled close-window design (#1224).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ne tested form

Three shapes of proof step were each written more than once and each was
incapable of reporting the failure it was checking for.

gofmt -l prints the files it would reformat and exits 0 whether it printed any
or none, so a step reading its exit code reports green on every tree that has
ever existed. A pipeline ending in a reader hands back the reader's status, so
`cmd 2>&1 | tail -25; echo "exit=$?"` printed exit=0 underneath a screen of
FAIL, which does not merely hide a red but manufactures a green directly below
the evidence. And a -run pattern is a second copy of the list of tests in a
file: when the two drift, go test answers a filter matching nothing with ok,
because from its side nothing failed, so a named test that never ran reads
exactly like one that passed.

scripts/proof.sh is the one written form of all three. THE ACCEPTANCE IS THE
FAILING ARM: internal/ci/prooflib_test.go runs each function twice, once where
the thing it checks is sound and once where it is broken, and each broken arm
also pins what the plain command says about the same situation, because in all
three cases the obvious command reports success. A guard against invisible
failure that has only ever been seen passing is the thing it was written to
prevent, wearing its own name.

No caller is converted here, on purpose. If the library is right the
conversions are mechanical; if it is wrong, that is better learned on one
caller than on all of them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…e caret parks where you type (#1321)

* task: Implement fzf v2 fuzzy matcher universally

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* fuzzy: land the remaining surface cutover (autonomy, connections, memory, folder search, rewind, registry) and manual pages

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* fuzzy: state the gap price the code and tests charge

fzf's own algo.go charges scoreGapStart (3) for the first skipped byte
and scoreGapExtension (1) for each byte after it in the same gap; the
const-block comment claimed the first byte paid extension too, which the
alignment, the gap-price test, and fzf's source all contradict.

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* task: Fuzzy match highlighting across the search surfaces

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* task: Fix the terminal cursor park for the settings search box

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

* changelog: the 1321 change entries, renumbered from the task placeholders, with the caret entry

Co-Authored-By: codeaf <agentfield-bot@users.noreply.github.com>

---------

Co-authored-by: codeaf <agentfield-bot@users.noreply.github.com>
…es the model (#1325)

* exec, session: the commit co-author links to the CodeAF account, and assisted-by names the model

The co-author address gains the account's numeric id, 267109073+, the form
GitHub renders as a link with the account's avatar, and a new Assisted-by
line above it names the model that wrote the commit, so git
interpret-trailers can answer who typed it beyond the account. The
Assisted-by line is filled at the belt render, the one surface that knows
the session's configured model; the leaf loop's standing contract carries
the co-author alone, as before, because exec is handed facts about the
model and never its name. The manual pages show both lines, and the fixed
arm's prefix waiver rises by the 162 bytes the wider belt fact costs.

Assisted-by: CodeAF (z-ai/glm-5.3)
Co-Authored-By: CodeAF <267109073+agentfield-bot@users.noreply.github.com>

* changelog: the co-author links to the CodeAF account, and assisted-by names the model

---------

Co-authored-by: CodeAF <267109073+agentfield-bot@users.noreply.github.com>
@santoshkumarradha

Copy link
Copy Markdown
Member Author

@AbirAbbas heads up, not a review request yet: this branch is now a closed slice at bc116d35c, and it is clean.

Evidence on that exact head, fresh clone, on Spark:

  • Full make check: EXIT 0, 116 packages ok, 0 failures.
  • Package laws: rc 0.
  • Change entries: 80, all well formed.
  • gofmt -l: nothing listed.
  • go build ./... and GOOS=windows go build ./...: both rc 0.

Landed since the last pass, newest first:

The PR stays in draft on purpose. Work from here goes on the next shared branch so this slice stops growing while it is being read. Say if you would rather have it split before you start.

…1329)

agentfield.ai/github redirects to Agent-Field/agentfield, so every pull
request, issue and comment codeaf drafted pointed a reader at the wrong
repository. agentfield.ai/github/codeaf redirects to Agent-Field/CodeAF
and is already serving, so the three attribution constants, their fixtures
and the two manual pages take the longer path. The utm parameters and the
rest of each line are unchanged.
The squash of #1329 carried the code and left its entry behind.
)

A landing question is raised for unverified and retired for every other
state, so a failed node has no question object and never had one. The
reading was inventing one out of the ending's shape alone, and a row
nobody was waiting on stood in the needs-you group for the rest of the
session. Attention goes; the tier, the word, the reason and the
take-it-anyway answers stay.
…id for

The refactor that made GenerateImage one road for the belt's tool and the
command line's image door (the slice landing as #1108) dropped the guard the
belt-only tool had carried since it was written: an empty or blank prompt was
answered "Invalid arguments: prompt is required" and cost nothing. Without it
the request went to the provider, which billed the call and answered with an
image that could not be read, and the tool reported that as the provider's
fault ("returned an unreadable image").

The guard is back, on the shared road so both doors refuse the same call the
same way, beside the model and reference checks that already refuse before
the request is sent. The video and music doors never lost theirs.

TestGenerateImageRefusesAnEmptyPromptAndCostsNothing pins it: a missing prompt
and a prompt of spaces are both tool errors naming the prompt, and the fake
painter sees no generation. It fails on the slice's head without the guard.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@AbirAbbas
AbirAbbas marked this pull request as ready for review September 21, 2026 14:50

@AbirAbbas AbirAbbas left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed and verified 2026-09-21: gates green locally and in CI, the default road checked live on both roads against a dev baseline, the one regression the slice introduced fixed on the branch with a contract test. Evidence and the full write-up are in the review ledger.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants