Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
153 commits
Select commit Hold shift + click to select a range
f5d7c73
the layout marker records the data branch
suleimansh Sep 3, 2026
76d9400
agent-data DECISIONS.md: read as a stranger, line by line
suleimansh Sep 3, 2026
b9940ed
tickets: the holder is AGENT_ID, nothing imported from the branches s…
suleimansh Sep 3, 2026
02e1d21
tickets live on the agent-data branch (packages/framework/prompts/REA…
suleimansh Sep 3, 2026
abb80c0
tickets live on the agent-data branch (packages/framework/prompts/SPE…
suleimansh Sep 3, 2026
2a34e55
tickets live on the agent-data branch (packages/framework/prompts/pre…
suleimansh Sep 3, 2026
57cafbd
tickets live on the agent-data branch (packages/framework/prompts/pre…
suleimansh Sep 3, 2026
55cd7cd
tickets live on the agent-data branch (packages/framework/prompts/pre…
suleimansh Sep 3, 2026
01c976b
tickets: the holder is AGENT_ID, nothing imported from the branches s…
suleimansh Sep 3, 2026
9664e56
tickets: the holder is AGENT_ID, nothing imported from the branches s…
suleimansh Sep 3, 2026
2c0490a
tickets live on the agent-data branch (packages/framework/src/SPEC.md)
suleimansh Sep 3, 2026
add6d50
tickets live on the agent-data branch (packages/framework/src/agent.t…
suleimansh Sep 3, 2026
77786bd
tickets live on the agent-data branch (packages/framework/src/agent.t…
suleimansh Sep 3, 2026
1f04c87
tickets live on the agent-data branch (packages/framework/src/auto-pm…
suleimansh Sep 3, 2026
c2c7048
tickets live on the agent-data branch (packages/framework/src/cli.ts)
suleimansh Sep 3, 2026
6bb61d2
tickets: the holder is AGENT_ID, nothing imported from the branches s…
suleimansh Sep 3, 2026
d13db46
tickets live on the agent-data branch (packages/framework/src/daemon-…
suleimansh Sep 3, 2026
8aac5db
tickets live on the agent-data branch (packages/framework/src/daemon-…
suleimansh Sep 3, 2026
fd07b0b
tickets live on the agent-data branch (packages/framework/src/daemon-…
suleimansh Sep 3, 2026
1ffbf08
tickets live on the agent-data branch (packages/framework/src/daemon-…
suleimansh Sep 3, 2026
0dd35e4
tickets live on the agent-data branch (packages/framework/src/dashboa…
suleimansh Sep 3, 2026
c8dc765
tickets live on the agent-data branch (packages/framework/src/dashboa…
suleimansh Sep 3, 2026
0ac1c1e
tickets live on the agent-data branch (packages/framework/src/dashboa…
suleimansh Sep 3, 2026
6d5498f
tickets live on the agent-data branch (packages/framework/src/dashboa…
suleimansh Sep 3, 2026
2327d39
tickets live on the agent-data branch (packages/framework/src/dashboa…
suleimansh Sep 3, 2026
bd6040a
tickets live on the agent-data branch (packages/framework/src/dashboa…
suleimansh Sep 3, 2026
28a2ceb
tickets live on the agent-data branch (packages/framework/src/dashboa…
suleimansh Sep 3, 2026
d9b34a4
tickets live on the agent-data branch (packages/framework/src/dashboa…
suleimansh Sep 3, 2026
0713525
tickets live on the agent-data branch (packages/framework/src/dashboa…
suleimansh Sep 3, 2026
f7e5bb2
tickets live on the agent-data branch (packages/framework/src/dashboa…
suleimansh Sep 3, 2026
40a5328
tickets live on the agent-data branch (packages/framework/src/e2e/har…
suleimansh Sep 3, 2026
a92dcff
tickets live on the agent-data branch (packages/framework/src/e2e/sto…
suleimansh Sep 3, 2026
b9f3d47
tickets live on the agent-data branch (packages/framework/src/framewo…
suleimansh Sep 3, 2026
2431154
tickets live on the agent-data branch (packages/framework/src/layout.…
suleimansh Sep 3, 2026
89efdbc
tickets live on the agent-data branch (packages/framework/src/layout.ts)
suleimansh Sep 3, 2026
8cf2fa5
tickets live on the agent-data branch (packages/framework/src/project…
suleimansh Sep 3, 2026
a8140e3
tickets live on the agent-data branch (packages/framework/src/system-…
suleimansh Sep 3, 2026
5eff742
tickets live on the agent-data branch (packages/framework/src/system-…
suleimansh Sep 3, 2026
d9f1e3d
tickets live on the agent-data branch (packages/framework/src/system-…
suleimansh Sep 3, 2026
533a051
tickets live on the agent-data branch (packages/framework/src/system-…
suleimansh Sep 3, 2026
03f73bf
tickets live on the agent-data branch (packages/framework/src/todo-lo…
suleimansh Sep 3, 2026
49f605e
tickets live on the agent-data branch (packages/framework/src/todo-lo…
suleimansh Sep 3, 2026
c7aca88
tickets live on the agent-data branch (packages/framework/src/todo-lo…
suleimansh Sep 3, 2026
83d0f03
tickets live on the agent-data branch (packages/framework/src/todo-lo…
suleimansh Sep 3, 2026
d9a8aaf
branches: .branches/agent-data is the data branch's, not agent data's…
suleimansh Sep 3, 2026
15a4131
branches: .branches/agent-data is the data branch's, not agent data's…
suleimansh Sep 3, 2026
ee1904f
branches: .branches/agent-data is the data branch's, not agent data's…
suleimansh Sep 3, 2026
60f2a35
branches: .branches/agent-data is the data branch's, not agent data's…
suleimansh Sep 3, 2026
1f71ad5
branches: .branches/agent-data is the data branch's, not agent data's…
suleimansh Sep 3, 2026
0560e69
branches: .branches/agent-data is the data branch's, not agent data's…
suleimansh Sep 3, 2026
eb5b4a0
branches: .branches/agent-data is the data branch's, not agent data's…
suleimansh Sep 3, 2026
f2099bf
branches: .branches/agent-data is the data branch's, not agent data's…
suleimansh Sep 3, 2026
0db37df
tickets: the holder is AGENT_ID, nothing imported from the branches s…
suleimansh Sep 3, 2026
a1c98f2
tickets live on the agent-data branch (packages/skill-tickets/SKILL.S…
suleimansh Sep 3, 2026
998f986
tickets: the holder is AGENT_ID, nothing imported from the branches s…
suleimansh Sep 3, 2026
13f1730
tickets live on the agent-data branch (packages/skill-tickets/SPEC.md)
suleimansh Sep 3, 2026
4a35c17
tickets: the holder is AGENT_ID, nothing imported from the branches s…
suleimansh Sep 3, 2026
4fc6502
tickets live on the agent-data branch (packages/skill-tickets/src/SPE…
suleimansh Sep 3, 2026
a8f5291
tickets: the holder is AGENT_ID, nothing imported from the branches s…
suleimansh Sep 3, 2026
a797125
tickets: the holder is AGENT_ID, nothing imported from the branches s…
suleimansh Sep 3, 2026
4221b10
tickets: the holder is AGENT_ID, nothing imported from the branches s…
suleimansh Sep 3, 2026
5a90b31
tickets live on the agent-data branch (packages/skill-tickets/src/cli…
suleimansh Sep 3, 2026
afad2df
tickets: the holder is AGENT_ID, nothing imported from the branches s…
suleimansh Sep 3, 2026
50cf684
tickets: the holder is AGENT_ID, nothing imported from the branches s…
suleimansh Sep 3, 2026
6e28b56
tickets: the holder is AGENT_ID, nothing imported from the branches s…
suleimansh Sep 3, 2026
8960e7b
tickets live on the agent-data branch (packages/skill-tickets/src/loc…
suleimansh Sep 3, 2026
fcbdcbd
tickets live on the agent-data branch (packages/skill-tickets/src/nam…
suleimansh Sep 3, 2026
d1184fb
tickets live on the agent-data branch (packages/skill-tickets/src/nam…
suleimansh Sep 3, 2026
db91e73
tickets live on the agent-data branch (packages/skill-tickets/src/nam…
suleimansh Sep 3, 2026
9fc4d12
tickets live on the agent-data branch (packages/skill-tickets/src/nam…
suleimansh Sep 3, 2026
c7cc512
tickets live on the agent-data branch (packages/skill-tickets/src/que…
suleimansh Sep 3, 2026
1095684
tickets live on the agent-data branch (packages/skill-tickets/src/que…
suleimansh Sep 3, 2026
d86ef34
tickets live on the agent-data branch (packages/skill-tickets/src/sto…
suleimansh Sep 3, 2026
4f789c5
tickets live on the agent-data branch (packages/skill-tickets/src/sto…
suleimansh Sep 3, 2026
4a36e8a
tickets live on the agent-data branch (packages/skill-tickets/src/sto…
suleimansh Sep 3, 2026
94a0f3e
tickets live on the agent-data branch (packages/skill-tickets/src/sto…
suleimansh Sep 3, 2026
7c4f9e6
tickets: the holder is AGENT_ID, nothing imported from the branches s…
suleimansh Sep 3, 2026
ebc6182
tickets: the holder is AGENT_ID, nothing imported from the branches s…
suleimansh Sep 3, 2026
f481574
tickets: the holder is AGENT_ID, nothing imported from the branches s…
suleimansh Sep 3, 2026
746023e
agent-data: cold read 2 applied (DECISIONS.md)
suleimansh Sep 3, 2026
1a9cf05
agent-data: cold read 2 applied (SPEC.md)
suleimansh Sep 3, 2026
3320054
agent-data: cold read 2 applied (package.json)
suleimansh Sep 3, 2026
9ad359f
agent-data: cold read 3 applied (DECISIONS.md)
suleimansh Sep 3, 2026
153165e
agent-data: cold read 4 applied (DECISIONS.md)
suleimansh Sep 3, 2026
45f52f0
agent-data: cold read 5 applied (DECISIONS.md)
suleimansh Sep 3, 2026
24ad9bc
agent-data: cold read 6 applied (DECISIONS.md)
suleimansh Sep 3, 2026
ea22de2
agent-data: cold read 7 applied (DECISIONS.md)
suleimansh Sep 3, 2026
c865de7
agent-data: no remote means no origin, as DECISIONS.md says (file-bra…
suleimansh Sep 3, 2026
7e61bb4
agent-data: no remote means no origin, as DECISIONS.md says (file-bra…
suleimansh Sep 3, 2026
c2c1f7b
agent-data: no remote means no origin, as DECISIONS.md says (file-bra…
suleimansh Sep 3, 2026
2d28276
agent-data: no remote means no origin, as DECISIONS.md says (file-bra…
suleimansh Sep 3, 2026
aa6f5e2
agent-data: cold read 8 applied (DECISIONS.md)
suleimansh Sep 3, 2026
5da428e
agent-data: cold read 9 applied (DECISIONS.md)
suleimansh Sep 3, 2026
e6bc203
agent-data: cold read 10 applied (DECISIONS.md)
suleimansh Sep 3, 2026
a1db3eb
agent-data: cold read 11 applied (DECISIONS.md)
suleimansh Sep 3, 2026
7e88847
agent-data: cold read 12 applied (DECISIONS.md)
suleimansh Sep 3, 2026
df1fc96
agent-data: read as a stranger, line by line (DECISIONS.md)
suleimansh Sep 3, 2026
5dde73f
skill-branches: read as a stranger, line by line (DECISIONS.md)
suleimansh Sep 3, 2026
950d09f
skill-branches: read as a stranger, line by line (SKILL.md)
suleimansh Sep 3, 2026
78d60e2
branches: only an agent-* birth branch is ever deleted (reclaim.ts)
suleimansh Sep 3, 2026
77cd526
branches: only an agent-* birth branch is ever deleted (reclaim.test.ts)
suleimansh Sep 3, 2026
012bb2c
branches: only an agent-* birth branch is ever deleted (reclaim.SPEC.md)
suleimansh Sep 3, 2026
86e22c1
branches: only an agent-* birth branch is ever deleted (reclaim.test.…
suleimansh Sep 3, 2026
7a4936c
branches: cold read 1 applied (DECISIONS.md)
suleimansh Sep 3, 2026
ce5ffa1
branches: cold read 1 applied (SKILL.md)
suleimansh Sep 3, 2026
4e5f48a
branches: cold read 2 applied (DECISIONS.md)
suleimansh Sep 3, 2026
3e0603a
branches: cold read 2 applied (SKILL.md)
suleimansh Sep 3, 2026
ce59364
branches: the birth-branch guard test does not assume the default bra…
suleimansh Sep 3, 2026
1540fd0
branches: cold read 3 applied (DECISIONS.md)
suleimansh Sep 3, 2026
9ebd758
branches: cold read 3 applied (SKILL.md)
suleimansh Sep 3, 2026
f50e119
branches: cold read 4 applied (DECISIONS.md)
suleimansh Sep 3, 2026
0fe56b8
branches: cold read 4 applied (SKILL.md)
suleimansh Sep 3, 2026
be4a1e0
branches: a forced removal logs to stderr, not into the CLI's JSON (w…
suleimansh Sep 3, 2026
07d9b0a
tickets: read as a stranger, line by line (DECISIONS.md)
suleimansh Sep 3, 2026
54a30b9
tickets: read as a stranger, line by line (SKILL.md)
suleimansh Sep 3, 2026
a17596d
tickets: cold read 1 applied (DECISIONS.md)
suleimansh Sep 3, 2026
3821607
tickets: cold read 1 applied (SKILL.md)
suleimansh Sep 3, 2026
945f2dc
tickets: cold read applied, the command contract stated (DECISIONS.md)
suleimansh Sep 3, 2026
3baf7e9
tickets: cold read applied, the command contract stated (SKILL.md)
suleimansh Sep 3, 2026
be984cf
branches: cold read applied, the command contract stated (DECISIONS.md)
suleimansh Sep 3, 2026
274a318
tickets: cold read 3 applied (DECISIONS.md)
suleimansh Sep 3, 2026
18ea438
tickets: cold read 3 applied (SKILL.md)
suleimansh Sep 3, 2026
533c504
branches: cold read 3 applied (DECISIONS.md)
suleimansh Sep 3, 2026
19a899a
tickets: cold read 4 applied (DECISIONS.md)
suleimansh Sep 3, 2026
9a71cc9
branches: cold read 4 applied (DECISIONS.md)
suleimansh Sep 3, 2026
8231980
tickets: cold read 5 applied (DECISIONS.md)
suleimansh Sep 3, 2026
8a69661
tickets: cold read 5 applied (SKILL.md)
suleimansh Sep 3, 2026
379a6e2
tickets: put takes the tickets/ path form like every other command (c…
suleimansh Sep 3, 2026
e65a576
tickets: put takes the tickets/ path form like every other command (c…
suleimansh Sep 3, 2026
5f81018
tickets: put takes the tickets/ path form like every other command (c…
suleimansh Sep 3, 2026
ab78524
tickets: put takes the tickets/ path form like every other command (c…
suleimansh Sep 3, 2026
6b210f4
tickets: cold read 6 applied (DECISIONS.md)
suleimansh Sep 3, 2026
8b8579d
branches: cold read 6 applied (DECISIONS.md)
suleimansh Sep 3, 2026
af51c90
tickets: cold read 7 applied (DECISIONS.md)
suleimansh Sep 3, 2026
3daa703
tickets: cold read 7 applied (SKILL.md)
suleimansh Sep 3, 2026
c8e071a
branches: cold read 7 applied (DECISIONS.md)
suleimansh Sep 3, 2026
53a02d8
tickets: cold read 8 applied (DECISIONS.md)
suleimansh Sep 3, 2026
72db7fa
tickets: cold read 8 applied (SKILL.md)
suleimansh Sep 3, 2026
b9ff77b
branches: cold read 8 applied (DECISIONS.md)
suleimansh Sep 3, 2026
a7afd4f
tickets: cold read 9 applied (DECISIONS.md)
suleimansh Sep 4, 2026
eed4fa0
tickets: cold read 9 applied (SKILL.md)
suleimansh Sep 4, 2026
8169939
tickets: cold read 10 applied (DECISIONS.md)
suleimansh Sep 4, 2026
ba61615
branches: cold read 10 applied (DECISIONS.md)
suleimansh Sep 4, 2026
6dc3cdd
tickets: cold read 11 applied (DECISIONS.md)
suleimansh Sep 4, 2026
df0593b
tickets: cold read 11 applied (SKILL.md)
suleimansh Sep 4, 2026
0075858
tickets: cold read 12 applied (DECISIONS.md)
suleimansh Sep 4, 2026
bd0b1c1
tickets: cold read 13 applied (DECISIONS.md)
suleimansh Sep 4, 2026
7902b83
tickets: cold read 13 applied (SKILL.md)
suleimansh Sep 4, 2026
06382b2
tickets: cold read 14 applied (DECISIONS.md)
suleimansh Sep 4, 2026
5ee268c
tickets: cold read 15 applied (DECISIONS.md)
suleimansh Sep 4, 2026
1d959f8
tickets: cold read 15 applied (SKILL.md)
suleimansh Sep 4, 2026
a283573
tickets: cold read 16 applied (DECISIONS.md)
suleimansh Sep 4, 2026
175f254
tickets: cold read 16 applied (SKILL.md)
suleimansh Sep 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .the-framework/LAYOUT
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
framework-dir: .the-framework
tickets-branch: tickets
data-branch: agent-data
logs-branch: agents-logs
archive-dir: agents
events-file: events.jsonl
Expand Down
103 changes: 73 additions & 30 deletions packages/agent-data/DECISIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,83 @@ Non-obvious decisions only, grouped by business-logic flow. Anything not listed
to the implementer's judgment. Flag conflicts instead of silently deviating.

## The package
- A **library, not a skill**. A skill is a `SKILL.md` an agent reads, plus a command it
runs; this package has neither, because only code uses it. Skills never import each
other's code; this library is what every skill imports.
- Package name = branch name: `@gemstack/agent-data` manages the `agent-data` branch, so it
is called the same.
- `.branches/` holds every extra checkout of the project — each agent's, and the data
branch's — so its name is defined here. It starts with a dot to keep tools' `*` patterns
out of it: every checkout inside is a full working copy of the project. Hidden through
git's own exclude file, never a committed `.gitignore`: the project's files are not the
library's to change.
- A **library, not a skill**: no `SKILL.md` and no command, because only code uses it.
Skills import this library; they never import each other.
- Package name = branch name: `@gemstack/agent-data` is the library for the `agent-data`
branch, but no function here hardcodes it: every one that touches a branch takes the
branch as an argument.
- `.branches/` holds a project's persistent checkouts, one directory per branch: the
agents' own, made by other packages, and the data branch's, made here. The directory
name is exported; the branch a caller names is checked out at `.branches/<branch>`. It
starts with a dot so a `*` glob skips it: every checkout inside is a full copy of the
project, and a type-checker or test runner that descends into N copies runs N times.
Hidden through `info/exclude`, never a committed `.gitignore`: the library must not
touch the project's tracked files. Writing the rule is best-effort: a git dir it cannot
write to still leaves the checkout standing. The rule goes in the common git dir: a
per-worktree `info/exclude` is never read, and one line there covers every checkout. A
checkout deleted by hand leaves git's registration behind: prune before adding, or the
add fails on the stale registration.
- Every git call has a time budget by subcommand: a read 10s, network and `worktree add`
120s, everything else 30s. `worktree` goes by its second word (`add` slow, `list` a
read, the rest a write) and `branch` by its own words (bare or with a listing flag it
reads; `-D`, `-m` or a new branch name writes); an unlisted subcommand pays the write
budget rather than risk cutting a write short. A killed `push` may have half landed, so
a timeout is reported as a timeout, never as a rejected push.

## The branch
- A branch of the project's repository holds the agents' data — tickets, the queue — like
`gh-pages` holds a site; code branches hold only code. Pushed and pulled on every
change, so every machine sees the same files.
`gh-pages` holds a site; code branches hold only code. Pushed by every write, and pulled
independently of any write, so a machine that writes nothing still ends up with what the
others pushed.
- One branch for all skills, each with its own folder or file on it. Not one branch per
skill: every extra branch would need its own checkout on disk and its own sync failure to
report.
- A branch that does not exist yet is born empty, with no parent commit, so no code commit
is ever in its history.
- The branch name is written once, here, as `DATA_BRANCH`; every other package imports it.
skill: every extra branch would need its own checkout on disk and its own sync failure
to report.
- A branch missing locally is adopted from origin's copy; one that exists nowhere is
created as an orphan branch, empty and with no parent commit, so no code commit is ever
in its history.
- The branch name is written once, in `names`, as `DATA_BRANCH`; every other package
imports it. `names` is its own entry point and has no node imports, so browser-side code
can import it without the git code.
- Read from anywhere in the repository, no checkout needed: a file comes from the
persistent checkout under `.branches/` when there is one, else the local branch, else
origin's copy; a directory listing always comes off a ref. A read can ask for a fresh
copy: fetch, then read origin's ref instead of the checkout, for a reader whose checkout
may trail what others pushed. A one-shot command opens the branch once: one fetch, then
every read off that one ref (origin's copy, or the local branch when origin has none);
it reads origin's copy because its own writes go straight to the remote and never move
the local branch. A read never fails: a missing file, a missing branch and a git that
could not run all read as absent, so a caller cannot tell an unreachable store from an
empty one.

## Flow: a write
Fetch what others pushed → make the change → commit → push. If the push is rejected
because someone pushed in between, start over on top of their changes.
Fetch what others pushed → make the change → commit → push.

- A write is handed over as a small function ("add this line"), not as a finished
commit, so starting over is just running it again on the new files. Never a force
push. After two failed pushes the write reports the failure and the commit stays local.
The next write's fetch carries it when it still applies on top of the remote, and drops
it when it does not: the remote wins, only the current change runs again, and nobody is
told.
- Two writers. A long-running process, the program that starts agents, writes in its own
checkout, `.branches/agent-data`, one write at a time. A command an agent runs writes in
a temporary copy of the branch, pushes, and deletes the copy; it never touches the
process's checkout, whose next write commits everything it finds there and resets it
when the write fails. A command's write that cannot be pushed fails; nothing of it waits.
- Two writers. A long-lived process (a daemon) writes in its own checkout,
`.branches/<branch>`, one write at a time; the pull takes its turn in the same queue: it
is that same cycle with an empty change. A command an agent runs writes in a throwaway
worktree outside the project, at the remote's tip (parentless when origin has no such
branch yet), pushes, and deletes it whether or not the push landed; it never touches the
process's checkout: that checkout's next write commits everything it finds there, and a
failed write resets it, so a second writer's files would land in the wrong commit or be
wiped. A command's write races at the push like the process's, twice in all; a push that
still fails throws, and nothing is left behind to retry. The process's write never
throws: its callers are background ticks.
- A write is a re-runnable function, not a finished commit: a lost race just runs it again
on the new files. The commit message is the caller's too: fixed, or a function run after
the change, since a write that batches several edits only knows what it did once it is
done. The lost attempt's commit is wound back first, so the change lands once and not
twice. Never a force push. After two failed pushes the write reports the failure and the
commit stays local in the process's checkout. The next write or pull rebases it onto
what the remote has by then and pushes the stranded commit together with the new one;
when the rebase conflicts, the checkout is reset to origin's tip and every unpushed
commit goes with it: the remote wins, only the current change runs again, and what was
dropped is never reported.
- An op is handed a directory and writes into it as it likes; `BranchFileFs` is the file
seam an op can take instead of the disk (the type and its node implementation ship here,
the op does the injecting, so it is testable off disk), and it creates parent
directories: git keeps no empty directory, so a skill's folder is gone with its last
file and absent on a branch just born.
- The remote is always `origin`, and a repository without one counts as remote-less
whatever other remotes it has: the process's write commits locally and reports no error;
a command's write refuses, as an outcome it returns, not as a throw; the pull reports an
error: it has no remote to converge with.
6 changes: 3 additions & 3 deletions packages/agent-data/SPEC.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
A branch of a project's repository used as a file store, as an npm library: the one branch every skill a coding agent uses keeps its files on — `agent-data` — checked out once under the project's `.branches/`, written through one cycle every write goes through (sync with origin, apply the change, commit, push, and re-apply the change when the push loses a race), written one-shot from any clone, pulled eagerly, and readable from anywhere in the repository without holding a copy. The git runner and the exclude rule that machinery is built on ship with it, and so do the two names every consumer needs: the checkouts directory and the data branch.
A branch of a project's repository used as a file store, as an npm library: `agent-data`, the one branch every skill a coding agent uses keeps its files on, checked out once under the project's `.branches/`. Every write goes through one cycle sync with origin, apply the change, commit, push, re-apply when the push loses a racefrom a long-lived process's checkout, or one-shot from any clone. It ships the git runner and the exclude rule that machinery is built on, plus the two names every consumer needs: the checkouts directory and the data branch.

A library, not a skill: it is read by code, never by an agent, so it ships no `SKILL.md` and no command. It knows git and the filesystem, nothing else — the caller names the branch and decides what the files mean. Every skill that keeps files on the branch depends on it; no skill depends on another.

## Business logic — TL;DR

- **The two names** (`names`) - `.branches`, the directory every checkout of a project lives under, and `agent-data`, the shared data branch: defined once, importable without pulling in git.
- **Running git** (`git`) - one runner with a time budget per subcommand, a timeout told apart from a git failure, the checkout a directory is in, and a push whose failure is git's own reason.
- **Running git** (`git`) - one runner with a time budget per subcommand, a timeout told apart from a git failure, whether a directory is in a working tree and which checkout it is in, and a push whose failure is git's own reason.
- **Hidden from the project's git** (`git-exclude`) - a rule in git's own exclude list, never a tracked file, covering every worktree of the repository at once.
- **A branch used as a file store** (`file-branch`) - a branch the caller names, holding files nobody edits in a working tree: born parentless or adopted from origin, checked out under `.branches/` for a long-lived process that funnels every write through one serialized cycle, written one-shot from any clone through a throwaway worktree, pulled eagerly, and readable from anywhere in the repository without holding a copy.
- **A branch used as a file store** (`file-branch`) - a branch the caller names, holding files nobody edits in a working tree: born parentless or adopted from origin, written through one serialized cycle from a long-lived process's checkout or one-shot from any clone, readable from anywhere without holding a copy.
- **The entry point** (`index`) - everything above in one place for a caller to import.

## Before modifying/creating SPEC.md files
Expand Down
2 changes: 1 addition & 1 deletion packages/agent-data/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@gemstack/agent-data",
"version": "0.0.1",
"description": "A branch of the project's repository used as a file store for coding-agent skills: the agent-data branch, checked out under .branches/, every write one sync-commit-push cycle that re-applies on a lost racefrom a long-lived process through its checkout, or one-shot from any clone.",
"description": "A branch of the project's repository used as a file store for coding-agent skills. Checked out under .branches/; every write is one sync-commit-push cycle that re-applies when it loses a race, from a long-lived process or one-shot from any clone.",
"license": "MIT",
"repository": {
"type": "git",
Expand Down
4 changes: 2 additions & 2 deletions packages/agent-data/src/file-branch.SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ A branch used as a file store: a branch of the project's repository holding file
- **Conflicts resolve toward origin** - the checkout is nobody's working tree, so origin always wins and the local intent is re-applied on top.
- **A failed operation leaves nothing half-written** - the checkout is put back to its committed state before the failure is reported.
- **The eager pull** - the same cycle applying no change: it converges this machine with origin, pushes anything a failed cycle stranded, and creates the checkout if there is none.
- **A repository with no remote** - fine for a funneled write, an error for the pull, a refusal for a detached write.
- **A repository with no remote** - no `origin`, whatever other remotes it has: fine for a funneled write, an error for the pull, a refusal for a detached write.
- **Reading from anywhere in the repository** - the persistent checkout when this location has one, else the branch's ref, else origin's copy of it, so a reader holds no copy of the files; a read can ask for a fresh copy instead.
- **A reader that fetches once** - a reader opening many files fetches up front, picks one ref, and takes every read off it.
- **A detached one-shot write** - a command in any clone writes through a throwaway worktree on origin's tip and pushes straight to the branch, never touching the persistent checkout.
Expand Down Expand Up @@ -120,7 +120,7 @@ It reports why it could not converge — a push origin rejected, or no origin to

#### Business logic

A repository with no remote is fine for a funneled write: the commit is safe locally, and the write reports that it did not push. It is an error for the pull, whose entire job is to meet the other machines, and it is refused outright for a detached write, which has nothing but the remote to write through. A repository nothing else can reach is a state the caller has to surface, not a mode this supports.
A repository with no remote — no remote named `origin`; the module names `origin` in every fetch and push, so any other remote does not count — is fine for a funneled write: the commit is safe locally, and the write reports that it did not push. It is an error for the pull, whose entire job is to meet the other machines, and it is refused outright for a detached write, which has nothing but the remote to write through. A repository nothing else can reach is a state the caller has to surface, not a mode this supports.

### Reading from anywhere in the repository

Expand Down
2 changes: 1 addition & 1 deletion packages/agent-data/src/file-branch.test.SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ What the tests cover: a branch used as a file store, checked against real git
- **The eager pull** - a machine converges on what another machine pushed, without writing anything of its own; on a repository with no remote the pull reports that as its error.
- **Serialization** - three writes started at once run one whole cycle at a time rather than interleaving, and all three end up in the file.
- **Reads** - from the persistent checkout; from an agent's worktree of the same repository, which reads the same file while holding no copy of it, and which names the repository it was made from as where the files live; a file the branch does not have reads as absent. Once another machine has moved origin on, the plain read still answers from the checkout while a fresh read answers from origin. A plain clone that never created the branch locally reads origin's copy and lists a directory off the ref, and a directory the branch does not have lists as none.
- **A detached write** - it births the branch on origin when there is none, from a clone that gains no local branch of it; another machine's persistent checkout stays where it was until its own pull; when a push loses a race the operation is re-run against origin's fresher tip and both writers' files end up on origin; no throwaway worktree is left registered afterwards; an operation that writes nothing is no commit; and a repository with no remote is refused.
- **A detached write** - it births the branch on origin when there is none, from a clone that gains no local branch of it; another machine's persistent checkout stays where it was until its own pull; when a push loses a race the operation is re-run against origin's fresher tip and both writers' files end up on origin; no throwaway worktree is left registered afterwards; an operation that writes nothing is no commit; and a repository with no remote is refused, a remote by any name other than `origin` counting as none.

## Before modifying/creating SPEC.md files

Expand Down
3 changes: 3 additions & 0 deletions packages/agent-data/src/file-branch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,9 @@ test('a detached write lands on origin from any clone without touching the persi
const solo = await initRepo('file-branch-detached-solo-')
try {
assert.deepEqual(await writeFileBranchDetached(solo, BRANCH, 'x', async () => {}), { ok: false, reason: 'no-remote' })
// A remote by any other name does not count: every push here names `origin`.
await git(['remote', 'add', 'upstream', solo], solo)
assert.deepEqual(await writeFileBranchDetached(solo, BRANCH, 'x', async () => {}), { ok: false, reason: 'no-remote' })
} finally {
await rm(solo, RETRIED_RM)
}
Expand Down
8 changes: 6 additions & 2 deletions packages/agent-data/src/file-branch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,14 @@ function resolveDeps(deps: FileBranchDeps): Resolved {
return { git: deps.git ?? nodeGitRunner(), log: deps.log ?? (() => {}) }
}

/** Whether the repo has any remote to sync the branch with. */
/**
* Whether the repo has `origin` to sync the branch with. Every fetch and push here names
* `origin`, so a repo whose only remote is called something else is remote-less to this module:
* the stated no-remote outcomes, not a push that fails twice.
*/
async function hasRemote(cwd: string, git: GitRunner): Promise<boolean> {
try {
return (await git(['remote'], cwd)).trim().length > 0
return (await git(['remote'], cwd)).split('\n').some(line => line.trim() === 'origin')
} catch {
return false
}
Expand Down
8 changes: 4 additions & 4 deletions packages/framework/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@
"the-framework": "dist/bin.js"
},
"scripts": {
"build": "pnpm -C ../skill-tickets build && pnpm -C ../agent-driver build && node scripts/gen-prompts.mjs && tsc -p tsconfig.build.json && cd dashboard && vite build",
"build": "pnpm -C ../skill-branches build && pnpm -C ../skill-tickets build && pnpm -C ../agent-driver build && node scripts/gen-prompts.mjs && tsc -p tsconfig.build.json && cd dashboard && vite build",
"preview": "pnpm build && node dist/bin.js",
"dev": "pnpm -C ../skill-tickets build && pnpm -C ../agent-driver build && node scripts/gen-prompts.mjs && tsc -p tsconfig.build.json --watch",
"dev": "pnpm -C ../skill-branches build && pnpm -C ../skill-tickets build && pnpm -C ../agent-driver build && node scripts/gen-prompts.mjs && tsc -p tsconfig.build.json --watch",
"dev:dashboard": "cd dashboard && vite",
"dev:daemon": "cd dashboard && FRAMEWORK_DEV_DAEMON=1 vite",
"design:build": "tsx dashboard/design/build.mts",
"typecheck": "pnpm -C ../skill-tickets build && pnpm -C ../agent-driver build && node scripts/gen-prompts.mjs && tsc --noEmit && tsc --noEmit -p dashboard/tsconfig.json",
"test": "pnpm -C ../skill-tickets build && pnpm -C ../agent-driver build && node scripts/gen-prompts.mjs && tsc -p tsconfig.test.json && node scripts/run-tests.mjs && cd dashboard && vitest run",
"typecheck": "pnpm -C ../skill-branches build && pnpm -C ../skill-tickets build && pnpm -C ../agent-driver build && node scripts/gen-prompts.mjs && tsc --noEmit && tsc --noEmit -p dashboard/tsconfig.json",
"test": "pnpm -C ../skill-branches build && pnpm -C ../skill-tickets build && pnpm -C ../agent-driver build && node scripts/gen-prompts.mjs && tsc -p tsconfig.test.json && node scripts/run-tests.mjs && cd dashboard && vitest run",
"clean": "rm -rf dist dist-test src/prompts.generated.ts",
"gen:prompts": "node scripts/gen-prompts.mjs"
},
Expand Down
Loading
Loading