Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
6c7fe98
agent-data/DECISIONS.md: every line as short as a rebuild needs
suleimansh Sep 4, 2026
42c7524
agent-data/DECISIONS.md: three meanings the shortening lost, restored
suleimansh Sep 4, 2026
83a8f28
agent-data/DECISIONS.md: the cold read's adds (where reads look, the …
suleimansh Sep 4, 2026
9f061d1
agent-data/DECISIONS.md: the second read (no-op writes, per-file look…
suleimansh Sep 4, 2026
b96b236
agent-data/DECISIONS.md: rewrap one line
suleimansh Sep 4, 2026
eeac9da
skill-branches/DECISIONS.md: every line as short as a rebuild needs
suleimansh Sep 4, 2026
4d6dc7c
agent-data/DECISIONS.md: the third read (untracked files on a failed …
suleimansh Sep 4, 2026
a62bd6b
skill-branches/DECISIONS.md: the cold read (origin's copy on continue…
suleimansh Sep 4, 2026
6201c4b
agent-data/DECISIONS.md: the fourth read (the library's own commit me…
suleimansh Sep 4, 2026
05c2c2e
agent-data/DECISIONS.md: rewrap
suleimansh Sep 4, 2026
e97cc69
skill-branches/DECISIONS.md: the second read (links follow the branch…
suleimansh Sep 4, 2026
5d8aa8f
skill-branches/DECISIONS.md: the third read (when links are made, wha…
suleimansh Sep 4, 2026
5916851
skill-branches/DECISIONS.md: rewrap
suleimansh Sep 4, 2026
ff7f3fe
skill-tickets/DECISIONS.md: every line as short as a rebuild needs
suleimansh Sep 4, 2026
a88e882
skill-branches and skill-tickets DECISIONS.md: the fourth and first r…
suleimansh Sep 4, 2026
66bf1a2
skill-tickets/DECISIONS.md: the first read (close and the queue, what…
suleimansh Sep 4, 2026
1eac6d2
Both SKILL.md: every sentence earning its place (usage, the branch ke…
suleimansh Sep 4, 2026
8fa418a
Three files after their reads: skill-branches DECISIONS (fifth), skil…
suleimansh Sep 4, 2026
9848d3e
Four files after their reads: skill-branches DECISIONS (sixth), skill…
suleimansh Sep 4, 2026
7a77149
Both SKILL.SPEC.md follow today's SKILL.md
suleimansh Sep 4, 2026
be45050
Four files after their reads (skill-branches DECISIONS seventh, skill…
suleimansh Sep 4, 2026
83adaaf
tickets SKILL.md: the final read (close lifts too, a branch switch ch…
suleimansh Sep 4, 2026
1d28103
branches SKILL.md: the final read (a continued agent keeps its name, …
suleimansh Sep 4, 2026
d290ea6
skill-tickets/DECISIONS.md: the final read (the full row, the sync's …
suleimansh Sep 4, 2026
6c673cb
skill-branches/DECISIONS.md: the final read (the skill's install line…
suleimansh Sep 4, 2026
b3884de
branches SKILL.md: the re-read (named means branch differs from folde…
suleimansh Sep 4, 2026
408774c
tickets SKILL.md: the re-read (which fields are optional, release aft…
suleimansh Sep 4, 2026
b52fa25
branches SKILL.md: the third re-read (run inside the checkout, the in…
suleimansh Sep 4, 2026
fa5ea36
skill-tickets/DECISIONS.md: the re-read (meta.json's place and key, t…
suleimansh Sep 4, 2026
677693f
tickets SKILL.md: the third re-read (an unparseable lock versus an un…
suleimansh Sep 4, 2026
3f5da5f
skill-branches/DECISIONS.md: the re-read (the skill as it now reads, …
suleimansh Sep 4, 2026
820deaf
branches SKILL.md: the fourth re-read (a name starts with a letter or…
suleimansh Sep 4, 2026
e9408ed
skill-tickets/DECISIONS.md: round six (the check order, keys in any c…
suleimansh Sep 4, 2026
15e1175
skill-branches DECISIONS round nine (attach and prune reconcile too, …
suleimansh Sep 4, 2026
97018b8
DECISIONS rounds: skill-branches ten (when links reconcile, sizes on …
suleimansh Sep 4, 2026
d3a9bbe
DECISIONS: rewrap every bullet
suleimansh Sep 4, 2026
f4cc5d4
skill-branches/DECISIONS.md: rewrap the reclaim line
suleimansh Sep 4, 2026
d70a1bb
skill-branches/DECISIONS.md: round eleven (the leading dash, director…
suleimansh Sep 4, 2026
cfa2088
skill-branches/DECISIONS.md: round twelve (the suffix once the name f…
suleimansh Sep 4, 2026
d2c89e9
skill-tickets/DECISIONS.md: round eight (where the exclude rules are …
suleimansh Sep 4, 2026
6cc66f1
skill-tickets/DECISIONS.md: round nine (what a refusal names, the fir…
suleimansh Sep 4, 2026
f786253
skill-branches/DECISIONS.md: round thirteen (refusal payloads, reacha…
suleimansh Sep 4, 2026
f91748a
skill-tickets/DECISIONS.md: round ten (rows carry a bare file, the na…
suleimansh Sep 4, 2026
e42c4e4
skill-branches/DECISIONS.md: round fourteen (which refusals name a su…
suleimansh Sep 4, 2026
f8ef34c
skill-tickets/DECISIONS.md: round eleven (invalid-path as typed, the …
suleimansh Sep 4, 2026
c6cef92
skill-branches/DECISIONS.md: round fifteen (links judged by target na…
suleimansh Sep 4, 2026
10617b3
skill-tickets/DECISIONS.md: round twelve (unreadable priority is 5, s…
suleimansh Sep 4, 2026
7b6a37f
skill-branches/DECISIONS.md: round sixteen (the JSON keys, detail onl…
suleimansh Sep 4, 2026
5d12d0b
skill-branches/DECISIONS.md: round seventeen (detail on stdout, not-a…
suleimansh Sep 4, 2026
70ae4da
skill-tickets/DECISIONS.md: round thirteen (the batch message, locked…
suleimansh Sep 4, 2026
0760a82
skill-branches/DECISIONS.md: round eighteen (the dash and --, agent-d…
suleimansh Sep 4, 2026
69ba924
skill-tickets/DECISIONS.md: round fourteen (what the skill tells the …
suleimansh Sep 4, 2026
db1a126
skill-branches/DECISIONS.md: round nineteen (removal order, relative …
suleimansh Sep 4, 2026
2ff66b4
skill-tickets/DECISIONS.md: round fifteen (a queue edit counts only o…
suleimansh Sep 4, 2026
2ed042d
skill-branches/DECISIONS.md: round twenty, wording only, closed
suleimansh Sep 4, 2026
f2bf722
skill-tickets/DECISIONS.md: round sixteen (4000 characters, the date'…
suleimansh Sep 4, 2026
eb85431
skill-tickets/DECISIONS.md: round seventeen (the date value, name bef…
suleimansh Sep 4, 2026
2623318
skill-tickets/DECISIONS.md: round eighteen (empty items, the GitHub l…
suleimansh Sep 4, 2026
fc6913e
skill-tickets/DECISIONS.md: round nineteen (the issue reference reade…
suleimansh Sep 4, 2026
c9d6a92
skill-tickets/DECISIONS.md: round twenty (the program writes through …
suleimansh Sep 4, 2026
55918dd
skill-tickets/DECISIONS.md: round twenty-one (release never looks at …
suleimansh Sep 4, 2026
faf078f
skill-tickets/DECISIONS.md: round twenty-two, closed
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
138 changes: 65 additions & 73 deletions packages/agent-data/DECISIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,83 +2,75 @@ 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**: 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.
- A **library, not a skill**: no `SKILL.md`, no command. Skills import it; they never import
each other.
- Named for the `agent-data` branch, but no function hardcodes it: every one that touches a
branch takes it as an argument.
- `.branches/<branch>` holds a project's persistent checkouts, one per branch: the agents'
own (made by other packages) and the data branch's (made here). The directory name is
exported. Dotted so a `*` glob skips it: each checkout is a full copy, so a tool that
descends does N times the work.
- Hidden through the common git dir's `info/exclude`, never a committed `.gitignore`: the
library must not touch tracked files; a per-worktree `info/exclude` is never read, and
one line there, written once, covers every checkout. Best-effort: the checkout stands
even when the rule could not be written. Prune before adding: a hand-deleted checkout
leaves a registration that fails the add.
- Every git call has a time budget: a listed read 10s, network and `worktree add` 120s,
other writes 30s. `worktree` goes by its second word (`add` slow, `list` a read, the rest
a write), `branch` by its flags (bare or a listing flag reads; `-D`, `-m` or a new name
writes); an unlisted subcommand gets the write budget, so a write is never cut short at
the read budget. A killed `push` may have half landed, so a git call that outruns its
budget fails as a timeout, its own error kind, never as a plain failure. git's output
buffer is 16 MB (a large checkout's file listing); an overrun is not a timeout.

## 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 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 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.
- A branch of the project's repository holds the agents' data (tickets, the queue) the way
`gh-pages` holds a site; code branches hold only code. Every write that changes something
pushes, and a pull is a cycle of its own, so a machine that writes nothing still gets what
the others pushed.
- One branch for all skills, each with its own folder or file. Not one per skill: every
extra branch needs its own checkout and its own sync failure to report.
- Missing locally, it is adopted from origin's copy; missing there too, it is born an orphan
branch, so no code commit is ever in its history.
- The name is written once, in `names`, as `DATA_BRANCH`, and imported everywhere else.
`names` is its own entry point with no node imports, so browser code can import it.
- A read works from anywhere in the repository, an agent's worktree included: the checkout
is looked for beside the real `.git`. A file is looked for in that checkout when there is
one, then on the local branch, then on origin's copy; a directory listing always comes off
a ref. A read can ask for a fresh copy: fetch, then origin's ref instead of the checkout,
for a reader whose checkout may trail. A one-shot command opens the branch once: one fetch
when there is an origin, every read off origin's copy (the local branch when there is no
`origin/<branch>`), 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.

## Flow: a write
Fetch what others pushed → make the change → commit → push.

- 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.
`.branches/<branch>`, one write at a time per repository and branch, an in-memory lock:
two processes on one clone are not guarded. The pull is that same cycle with an empty
change, behind the same lock. 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),
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, and a failed write
resets it, new files included. Both try the push twice; the command's write then throws
with nothing left to retry, the process's never throws: its callers are background
ticks.
- A write is a re-runnable function, not a finished commit: a lost race winds the attempt's
commit back and runs the function again on the new files, so the change lands once. The
message is the caller's: fixed, or a function run after the change, since a batch only
knows what it did once done; the library's own are `create the <branch> branch` for the
birth and `sync` for the pull. Never a force push. After two failed pushes the process's
write reports the failure and the commit stays local in its checkout; the next write or
pull rebases it onto the remote and pushes it with the new one. When that rebase conflicts
the checkout is reset to origin's tip: the remote wins, every unpushed commit is dropped
unreported, only the current change runs again.
- An op is handed a directory and writes into it. `BranchFileFs` is the file seam an op can
take instead of the disk, for tests (type and node implementation ship here, the op
injects it); it creates parent directories: git keeps no empty directory, so a skill's
folder vanishes with its last file and is absent on a new branch.
- The remote is always `origin`; a repository without one is remote-less whatever other
remotes it has. Then the process's write commits locally and reports no error, a command's
write refuses (an outcome, not a throw), and the pull reports an error: nothing to
converge with.
Loading
Loading