Skip to content

Brain index: design, runs record their files, elsewhere keyed by repository, live runs seen - #1443

Draft
santoshkumarradha wants to merge 880 commits into
devfrom
task/brain-files-and-repo-scope
Draft

santoshkumarradha wants to merge 880 commits into
devfrom
task/brain-files-and-repo-scope

Conversation

@santoshkumarradha

@santoshkumarradha santoshkumarradha commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

Brain index: design and first steps (combined)

This PR now carries both halves of the brain index work:

  • Design (was docs(design): the brain index, shared reads and separate writes #1437): docs/design/brain/DESIGN.md, covering shared reads and separate writes, the index, awareness push and pull, the planning check, and the later single-plandb refactor with its code map, plus the overlap measurement (aggregate numbers only).
  • First steps (below): runs record the files they touched, <elsewhere> is keyed by repository, live runs are seen, cut-short runs leave interrupted rows, and one run shows as one row.

Paused by the owner; stays a draft.


Two small changes from the brain design (docs/design/brain/DESIGN.md, #1437), built on the files that exist today. No new index file.

1. Runs on the worker harness record the files they touched

Before: a run on the worker harness left no row in the project's record (tasks.jsonl), so <elsewhere>, the tasks tool and the sessions rows on home never saw what it touched (0 of 138 run tasks measured).

Now: when a run ends or is stopped it writes one row, the same shape the older engine's tasks write. Its files are the working copy's own diff against the commit the copy was cut from, read before the copy is given back, so a file a worker committed itself counts too. No model call, no language or tool rules.

When that list cannot be read, the row carries filesUnread with the reason. <elsewhere> and the tasks tool then say files unknown instead of drawing it like a row that named no files.

2. <elsewhere> is keyed by repository as well as by project folder

Before: both halves read only the project folder codeaf was launched from. The same repository reached from chats filed under different folders was invisible (17 of 26 measured overlaps).

Now:

  • Every index row carries repo, the git common directory, so linked worktrees of one repository match.
  • Both halves also read every other project folder for work on a repository this chat is on: its working directory's repository, the live run's ground, and its own rows. Old rows are resolved through their ground.
  • A row from another folder carries in <project>.
  • Rows are ranked before the existing caps (6 and 6): shared files, then same repository, then same folder. Work on an unrelated repository in another folder is never shown.
  • The grammar is unchanged.

A repository that could not be resolved, or another folder's record that could not be read, is said in one line under the lead. When nothing else is there, that line is the whole block. A folder that is not a repository says nothing.

Tests (red first)

internal/session/taskrepo_test.go, committed failing in c958a7d:

  • a belt run's row names a worker-committed file and a landing-committed file
  • same-repo landed and live work from another folder, through a linked worktree; an unrelated repo is left out
  • ranking: shared files, then repository, then folder, and the cap is kept
  • an unresolvable repository is said
  • files unknown on a row whose list could not be read

The manual (internal/manual/chat/tasks.md) has two new sections and a pointer in the existing <elsewhere> section.

santoshkumarradha and others added 30 commits September 18, 2026 17:40
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ker seat)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ker seat)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…urns late (#1210)

The root's worker marks its task done in the store before it returns. When the supervisor's pass saw the store done in that gap, Start ended the run with the right outcome and an empty result. Start now reads the stored root result when the worker's return is missing. A new test holds the return back past one pass, so the gap is every run of the test.
…y, and the landing's deadline fits a real call

Deciding whether to refresh ran after every message and opened the plan store
twice each time; it now reads the rows the task sheet already carries and the
store read moves into the command. The landing waited half a second for a call
that takes two to four, so the refresh could only ever succeed in a test; it
waits six. The rail no longer draws the whole plan when its reading holds no
rows (a fallback added for a fixture), and the landing fixture sets the belt
itself instead of inheriting it from the shell that ran it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…he loop and at the landing

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…nced answer

The page the model read was six lines; it now says who is asking, when to open a
task, what an answer is, and what to do when the record does not hold it or the
person is steering. The rows, notes and task bodies are bounded, the page is
said once, and the answer is found by its braces.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The owner, 2026-09-18: a prompt is written so it is true in any setup. Two
example lines spelled a Go file and one a Go glob; they name a path and a name
pattern instead.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…one next thing (#1209)

The answer section of the chat prompt is reworded, close to byte neutral, so a reply leads with the action, uses the fewest numbered steps for a sequence, says where a multi-step job stands, gives cost in minutes or hours, states an error as cause then fix, and ends on the one thing that is the person's to do next. Shaped after the i-have-adhd skill by Ayoub Ghriss (MIT); no text copied. Fixed prefix 55,253 of 55,280; lean prefix 47,042 of 47,055.
santoshkumarradha and others added 24 commits September 23, 2026 15:02
## Summary
- #1418 stopped refusing a deliverable path by walking up until it found a directory, declining only the filesystem root. On macOS `/home` is a symlink to `/System/Volumes/Data/home`, which is a directory, so the path that prompted the change — `/home/santosh/src/af-dev2-probe/bin/codeaf` on another host — still resolved as a place on this machine and was still refused. GLM 5.3 caught this in review of #1418.
- `placeOnThisMachine` now asks about the directory the path names and nothing above it. A real folder outside the ground is still refused by name. A path whose directory is not on this machine, including one that merely begins with a directory that is, is left alone.
- The test now uses that `/home/...` path rather than a made-up prefix, so the macOS symlink case is what it pins.

## Test plan
- [x] `go test ./internal/session/ -count=1 -timeout 180s -run 'TestAPathOnAnotherMachineDoesNotRefuseTheTask|TestATaskNamingAFolderItDoesNotStandInIsRefused|TestATaskNamingACommandInItsOwnDirectoryIsAdmitted|TestATaskWhoseAcceptanceCarriesAProseSlashIsAdmitted|TestNoRoadInTheTaskEngineHasMoreEndingsThanItsLedgerRow|TestTheRatchet'`
- [ ] `go test ./internal/session/ -count=1 -timeout 12m` (the package takes about six minutes; the targeted run above is what changed)

—
Drafted with [CodeAF](https://agentfield.ai/github/codeaf?utm_source=github&utm_medium=pull_request&utm_campaign=drafted_with) · reviewed and owned by the author
… and work/ cleanup

- Fix false-positive scope matching in contract.go and skillcatalog.go where
  path components like 'users' or 'home' matched every workspace.
- Sanitize process environment in runSkillCheck to prevent credential leakage
  to candidate check.sh scripts.
- Add deferred panic recovery in Supervisor.launch and BashWorker.Run to prevent
  process crashes and inFlight deadlocks.
- Bound SKILL.md file reads to 64KB in skills.go.
- Acquire write transaction via beginWrite() for telemetry update in
  SkillFactAccessors to prevent SQLITE_BUSY under immediate locking.
- Untrack committed scratch triage files under work/ and add work/ to .gitignore.

Assisted-by: CodeAF (local/gemini-3.8-flash-high)
Co-Authored-By: CodeAF <267109073+agentfield-bot@users.noreply.github.com>
…s at a price (#1416)

* do, run, session: tests for the do door's contract, lockfiles, the check seat and the fallback receipt

These are written against the unfixed tree first: codeaf do committing the
person's own work on the run engine, running unbounded without --yes-spend,
dropping --db and --keep without a word; a belt landing leaving project
lockfiles behind; CODEAF_CHECK_MODEL never reaching a chat's run; and an
approved hand-off falling back to the older engine with an identical receipt.

* do, run, session: codeaf do edits in place, commits nothing, and stops at a price

The run engine is the road every codeaf do takes, and it swept the
directory's whole status into a commit on the checked-out branch, the
person's own uncommitted edits and untracked files included. It now keeps
the contract --dir states and the older road keeps: edited in place,
nothing committed. The folder is read before the run and after, and the
files the run names are the ones it changed (session.RunTreeSnapshot).

Without --yes-spend a run is bounded again: at the plan-price figure and at
what is left of today's limit, whichever is nearer, ending with exit 3.
--db is refused in words on this road, and --keep says where the run's
store is.

A belt landing no longer drops paths by a name project files share: the
.lock suffix and the bench-results folder are gone, and what the harness
writes is answered in one place (harnessWrote). CODEAF_CHECK_MODEL now
seats a chat run's checks. An approved hand-off the run engine could not
start says so on its receipt. The wave 6 change entry is rewritten to say
what is true.

* manual, do: the do pages are reachable for the questions they already answered, and the status check reads git's own columns

* session: an in-place run's snapshot asks where the repository is through the one asker

* changes: the do door's entry carries its pull request's number
* tui3: a run's rows and its page look like the old task rows

A run's parts are drawn through the node renderer (railEntryRows) by lending
each store row a node: the working spinner, the #id, the clock and price line,
and every part its own row in the old tree, never a done fold. A node row that
carries a run is drawn as the head of its family with the parts under it.

A run's page wears the task room's head: the trail with esc back, and the facts
rule led by the state's spinner. Steps are the room's shell rows, parts are the
rail's rows, and an empty live command draws no line (the stray ◑ $).

* changes: a run's rows and its page look like every other task (#1420)

* changes: name the ground-lint entry for its PR (1421)
…road says so (#1417)

* Tests first: one run per batch, no silent fallback, an honest orphan row

The tests, and only the seams they need (a slow or failing copy cut, a
waiting hook, the run's root handed to a belt worker, the name of the
run binding). No behaviour changes in this commit; every new test is
expected to fail on it.

* One run per batch: approved hand-offs join one run, never race to one store

Starting a run is now one critical section, from the look for a live run
until the run is registered, and every other hand-off waits at its door
and then joins the run as a child. A batch approved at one moment is one
run with every hand-off in it.

A run road that fails says "task N did not start: <reason>" on both the
proposal door and the typed /task, reads as a failure, and starts nothing
on the older engine. Only a missing run engine or plan place still takes
the older road.

A worker is bound to its run's root as well as its store path
(PLANDB_RUN), and the plandb CLI refuses a store at that path whose root
is another run's.

A run row's room takes a message as a note on its task's page. A run row
nothing drives answers that it can be stopped instead of "no task N in
this session", and a stop settles it.

* The failed-road test reads the success receipt's own words, not the word started

* Change entry for one run per batch

* The stop law names the start door by its new name; the row-message heading stops competing with how to stop a task

* The stop of a row nothing drives is said where stopping a run is

* The #1418 change entry carries its pull request, so the entries are well formed again

* The #1418 entry's pr line, and the remote-path test names no real person's folder

The pr line was left unstaged by the rename. The test spelled a real
checkout under /home, which exists on the machine it was measured on,
so it failed there.
… loop (#1427)

* session: a read sweep the chat will not hand off is handed off by the loop

Three prompt formulations taught the hand-off judgement and two model
families recited every one without acting on it: bounded lookups stayed
inline, and an explicit parallel instruction came back as sequential
calls. The loop is the one place the reading's cost is a fact rather
than an instruction, so the loop spends the hand-off itself.

A turn that reaches its third distinct read-only target — read, grep,
find, ls, the earlyTools set — is enumerating a set, and the remainder
goes to one quick task whose distilled answer comes back as the batch's
tool result. One file stays a step; two files stay a step; a mixed
batch, a bash call, or a write breaks the run. Every failure — refusal,
a held node, a waited-out budget, an empty answer — runs the batch
inline exactly as before, and the sweep is disabled for the rest of the
turn so a failure is paid once. The hook fires only where a hand-off
can run: conversations, with an engine linked.

Assisted-by: CodeAF (moonshotai/kimi-k3)

Co-Authored-By: CodeAF <267109073+agentfield-bot@users.noreply.github.com>

* fix(session): cancel abandoned quick sweep, preserve rest in mixed batch fallback, and compact keys

- Fix double-execution of non-reader tools (e.g. bash, edits) during fallback when a quick task fails or times out.
- Fix event stream ordering: do not emit EventToolFinished or appendTook before confirming the hand-off succeeded.
- Fix start timer wait: arm startTimer in select so governor-held nodes time out after 30s instead of blocking for 4m.
- Clean up abandoned quick tasks with cancelTask so orphaned tasks do not consume resources.
- Compact JSON arguments in sweepKey so whitespace variations do not register as distinct targets.
- Use briefAskLimit for user question clipping in sweepBrief instead of arbitrary 400-char truncation.
- Update phase clock with tellPhase during handoff and record noteCallOutcomes.

Co-Authored-By: CodeAF <267109073+agentfield-bot@users.noreply.github.com>

---------

Co-authored-by: agentfield-bot <agentfield-bot@users.noreply.github.com>
Co-authored-by: CodeAF <267109073+agentfield-bot@users.noreply.github.com>
…nversation always moves, --max-hours ends the window (#1430)

* engine, move, max-hours: tests first — an older engine is replaced, status names it, a held journal moves, the cap ends the window

Red on the base: an older engine holding work is deferred to, --status does
not exist, the engine's held-journal refusal reaches home as a bare string so
move-it-here never asks, nothing names or stops a window that will not let go,
an engine never honours a move request, and --max-hours leaves the window open.

* engine --status, a named --stop, the older engine gives up the slot, a held conversation always moves, --max-hours ends the window

Engine. `codeaf engine --status` / `--status-all` ask the socket which engine
holds a workspace (pid, binary, build, start, windows) and stop nothing;
`--stop` / `--stop-all` name the process they stopped. An engine from an
OLDER build (by build moment, any file, or too old to answer) is replaced
by `codeaf engine --daemon` and by every window's dial, busy or not, with
one line naming it; a newer engine is joined and a tie never replaces, so
two builds converge. Stop never signals its own process.

Move. The engine's held-journal refusal reached home as a bare string, so
enter on a conversation an in-process or older window held printed the
instruction just followed and asked nothing; it now arrives as
ErrSessionLocked and home asks the holder. An engine closes a conversation
a window asked for (it had no surface to hear the request). After fifteen
seconds unanswered the card names the holder (pid, tty, build, from its
presence record) and enter offers to stop it with SIGTERM, a second time
exiting at once. The request/presence files are frozen as a protocol
between builds (session/holder.go) and pinned by byte tests.

--max-hours. The wall stopped the work and left the window waiting for a
person; it now leaves two minutes after the wall and insists thirty
seconds later.

* changes: the entry for #1430

* manual: the new sections do not pull a task question off its page, and three probes reach them

The first words of the stop section ("a window stuck") and the time-limit
section ("a test rig", "wait") moved 'is my task stuck while it waits for
its test suite' off how-tasks-run. Reworded; probes added for engine
--status, the window that will not let go and --max-hours closing the window.

* changes: the #1430 title fits on one line
A design for one index of every run across chats and projects, written
only by the engine at run start and end, feeding the existing <elsewhere>
block, the tasks tool and a planning check. Includes the overlap
measurement and the code map for a later single plan store.
… what they are for (#1396)

* skills: read Claude Code plugins, Codex system skills and linked folders; skills with memory off; the whole shelf in the catalog; /skill over the session host

* skills: gofmt

* skills: fixture names, the attachment door leaves the ledger, e2e helper name

* skills: the plugin exclusion test checks its control first

* skills: a plugin reads the skills its manifest or marketplace entry names

* changes: the skills-from-other-tools entry

* e2e, manual: the carried-skills line is 'skills ·' and folds into the work chip

* e2e: the relevance eval counts a skill the run opened, one folder per request

* tui3: the carried-skills line stays under the message, above the work chip

* tui3: the no-shelf picker test reads the reason the clipped row keeps

* e2e: the tide and ledger skills carry their answer, so the model does not go searching

* session: the catalog asks for the skill before any other tool; the eval floor sits below the spread

* skills: the attachment rides the facts photograph, and the picker asks its doors off the update loop

* tui3: the chip press test runs the clearing door it is handed

* tui3: the shelf read keeps its place in the door line
…ewhere

A worker-harness run leaves no row naming the files it touched, and the
elsewhere block reads only the launch folder's project, so two chats on
one repository filed under different folders never see each other.
These tests pin the missing record, the repository scope, the ranking
(shared files, then repository, then folder) and the honest failure
lines.
…pository

A run on the worker harness now leaves one row in the project's record
when it ends or is stopped, naming every file it changed against the
commit its copy was cut from (a worker's own commits included). The list
comes from the working copy's diff, read before the copy is given back.
When it cannot be read the row carries filesUnread, and every reader
says "files unknown" instead of drawing it like a row that named none.

Every index row now carries its repository (the git common directory,
so linked worktrees of one repository match). The elsewhere block reads
this project folder as before and every other project folder for work
on a repository this chat is on, in both halves. Rows from another
folder say "in <project>". Rows are ranked before the existing caps:
shared files, then same repository, then same folder. A repository that
could not be resolved, or another folder's record that could not be
read, is said in one line under the lead rather than read as no
overlap.
readElsewhereWide held 38 decisions against a ceiling of 15. It is now a
small reader type whose steps (learn this chat's scope, keep one landing,
read the other folders, read one folder) are each one short method. The
other folder's name is now looked up only when something in it is kept or
could not be read.
Brings #1415, #1424, #1425, #1334, #1422, #1388, #1389 and #1426 onto the
integration branch (dev at debffab).

Conflicted files and how each was settled:

- internal/tui3/homeslash.go: the opens-a-conversation fate keeps dev2's
  /skill and dev's /manual and /folder (the union). dev's /project pin and
  the removal of /image and /ask stand. The fate-table and homeTrayCommand
  comments no longer name /image or the old /folder pin.
- internal/manual/chat/home.md: dev's three rows for /attach, the commands
  that open a conversation and the ones that answer on Home, with dev2's
  /skill /skills added to the opens-a-conversation row so the table matches
  homeFate.
- internal/tui3/notice_test.go: both sides appended tests; all are kept
  (dev2's unread and retired profile-key tests, dev's ranking and rotation
  tests). newNoticeBoard's third argument is "hints enabled", so the
  hints-off test still passes false.

Not a textual conflict, but broken by the merge:

- internal/manual/chat/compacting-over-and-over.md: the probe "what
  happened to the earlier messages" reached another page once dev2's pages
  and dev's respelled heading met. The heading carries the phrase again.

Semantic collisions checked with no change needed: Escape and double-space
against dev2's task page, rail, pickers and skill chips; one /skill row and
alias; the tip table (22 rows, no priority column, the unread-keys line is
not a tip and shows with hints disabled); ui.hints stays a read key beside
dev2's task.parallel and retired-key rows; Home's working mark and the
filtered Sessions list against dev2's stopped and interrupted runs; the
attach tray carries files and skill chips side by side; the e2e word table;
system.md; icons and banned words; change entries.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Found while merging dev into santos/dev2; every line was already stale on dev.

- attaching-files.md still said a bare /attach on Home answers "type the
  path after /attach" and that /folder is Home's browser. A bare /attach
  opens the browser for the next conversation's folder; /project pins the
  folder; /folder opens a conversation first.
- places.md said Home has no alt+<letter> keys. Home's foot draws alt+p,
  alt+e, alt+a and alt+k. The page also carried the "Where is the model
  filter in Settings" section twice, word for word; one copy is gone.
- commands.go, hometip.go and notice.go comments still placed /attach
  relative to /image, drew /ask as Home's example tip, and called /ask a
  command "on its way out".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
/skill is on Home's opens-a-conversation road, and the new conversation ran
it through a dispatcher that rewrote the box to a bare "/skill ", so
"/skills reviewer" typed on Home opened the picker with the query gone. The
dispatcher now writes the command and its words back into the box, so the
picker opens filtered on them, on Home and in a conversation alike.

commands.md's table had no /skill row; it now names /skill, its /skills
alias and what enter does on the shelf.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A worker-harness run in flight shows its window as idle outside it: no
presence row names it and the project's record has no running row. A
run cut short by its chat closing leaves no row at all. A finished run
is listed twice in its own chat's tasks answer, once from its plan and
once from the record.
…ne run one row

A run on the worker harness and the hand-offs that joined it are now in
the window's presence file, and the run writes a running row to the
project's record when it starts or is carried on. Home, the sessions
rollup, the elsewhere block and the tasks tool in other windows count it
as running instead of reading the window as idle.

A run whose conversation closes under it writes an interrupted row with
the files touched so far, untracked new files included (minus what the
harness itself writes). A run whose process died is closed on the next
open as interrupted, with the files its recorded copy holds, rather than
as failed. The run's copy record now keeps the commit it was cut from, so
a carried-on run still names every file; its final row supersedes the
interrupted one. The sessions rollup counts interrupted rows as
incomplete.

The run's own conversation lists a finished run once in its tasks answer:
the record's row is left out where the run's plan rows already list it.

The interrupted-row test now plants the next life's run row the way the
carry-on tests do, since a test session with no messages does not keep
its checkpoint across a reopen.
A real session starts a run through the task door with the run engine
seated, its presence file is read the way Home reads the machine, and
Home's own conversation bullet (#1426, session.Tasks.Running) must wear
the working mark. On clean santos/dev2 (ee0abdf) the rollup stays at
0 running for the whole run and the test fails.

A run row that is published now refreshes the window's presence file at
once instead of at the next heartbeat.
@santoshkumarradha santoshkumarradha changed the title Runs record the files they touched; elsewhere is keyed by repository Brain index: design, runs record their files, elsewhere keyed by repository, live runs seen Sep 24, 2026
Base automatically changed from santos/dev2 to dev September 25, 2026 02:20
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