Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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/.gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# The Framework: agent state is transient; the lasting records live on the tf-data branch.
# The Framework: agent state is transient; the lasting records live on the agents-data branch.
*
!.gitignore
!LAYOUT
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
data-branch: tf-data
data-branch: agents-data
archive-dir: agents
events-file: events.jsonl
meta-file: agent.json
Expand Down
4 changes: 2 additions & 2 deletions FEATURES-SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ happens while nobody is at the keyboard.
## Handoff and what lands in git

- Every agent gets its own git worktree under `.branches/` and its own branch (`agent-<id>`), created before it starts; your checkout is never touched
- The `branches` skill: the system prompt of every agent started in its own checkout carries the `@gemstack/skill-branches` package's `SKILL.md` — that checkout is your whole workspace, name the session with the command, commit as you go, leave a clean tree, never push or open the PR yourself; an agent anywhere else (a terminal run in your checkout, an Actions runner, a cloud session) is told to branch with git itself
- The `branches` skill: every checkout The Framework creates carries the `@gemstack/skill-branches` package's `SKILL.md` where the agent's harness looks for skills (`.claude/skills/branches` for Claude Code, `.agents/skills/branches` for Codex), and the system prompt tells the agent to use it — that checkout is your whole workspace, name the session with the command, commit as you go, leave a clean tree, never push or open the PR yourself; an agent anywhere else (a terminal run in your checkout, an Actions runner, a cloud session) is told to branch with git itself
- `branches` on every agent's PATH — the skill's command line: `name <name>` renames the agent's branch to `agent-<name>` and prints the name it got (suffixed when taken); `status` says whether the tree is clean and the branch on the remote; `create`, `attach`, `list`, `remove`, `prune` are the same operations the dashboard runs
- The session name is the branch: an agent is labelled by its `agent-<name>` branch, read from git after every turn — nothing to signal, nothing to record twice
- Dependency directories shared from the parent checkout instead of reinstalled — as directories of links, so an agent's own install stays in its checkout and never rewrites or purges the parent's
Expand All @@ -122,7 +122,7 @@ happens while nobody is at the keyboard.
- Empty agents publish nothing
- Handoff panel: push / open PR / merge, as buttons
- A withheld merge is reported with its reason
- Agent history archived on the `tf-data` branch under per-user directories — pushed the moment a session settles
- Agent history archived on the `agents-data` branch under per-user directories — pushed the moment a session settles
- Post-merge quality follow-ups queued (maintainability / security)
- Knowledge folded back into `DECISIONS.md` / `FACTS.md` / `INSIGHTS.md` at merge

Expand Down
2 changes: 1 addition & 1 deletion SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Every user-facing feature is enumerated in `FEATURES-SPEC.md`.
- **The agent is the unit of work** - one task, in its own git worktree on its own branch, streaming everything it does as events; finished work is pushed and leaves as a pull request.
- **Black-box driving** - the framework prompts the wrapped coding-agent CLI, lets the CLI's own loop run a full turn, and learns everything from the turn's final message: the session name the agent invented, the questions it stops to ask, and the ready-for-merge signal.
- **Autonomy bounded by the account's own quota** - unattended work runs only while the account is under its pro-rated quota boundary; work a human asks for is never blocked, and a running agent is never interrupted over quota.
- **Framework data on its own branch** - everything The Framework itself writes (tickets, the agent queue, agent archives) lives on the `tf-data` branch, so the default branch stays 100% code.
- **Framework data on its own branch** - everything The Framework itself writes (tickets, the agent queue, agent archives) lives on the `agents-data` branch, so the default branch stays 100% code.

## Business logic

Expand Down
2 changes: 1 addition & 1 deletion packages/framework/prompts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ is written in TypeScript any more, so prompting can change without touching the
| `system_prompt.md` | The built-in system prompt (#326). Rom's doc. Its `# User prompt` slot is where the user's own text is rendered, for a build and a prompt session alike (#1691). |
| `ticketing_format.md` | The ticket file format: how a ticket, its plan and its lock are written under `tickets/`. Travels in every agent's context. |
| `todo_format.md` | The agent-queue format: how `TODO_AGENTS.md` is banded by priority. Travels in every agent's context. |
| `data_branch_protocol.md` | The data-branch protocol: tickets, the queue and the session archives live on `tf-data`, read and written there directly, never on a code branch. Travels in every agent's context. |
| `data_branch_protocol.md` | The data-branch protocol: tickets, the queue and the session archives live on `agents-data`, read and written there directly, never on a code branch. Travels in every agent's context. |
| `branch_yourself.md` | The "Branch management" section for an agent that runs outside a checkout The Framework created (#1725): the command is not there, so it branches with git itself. Agents in their own checkout get the `skill-branches` package's `SKILL.md` instead. |
| `triage_scope.md` | The queue-only rule appended to both triage presets: a triage writes `TODO_AGENTS.md`, never a ticket's code (#1641). |
| `on_before_mergeable_prompt.md` | The optional extra turn an agent gets when it signals ready for merge: queue quality follow-ups, fold what it learned into the knowledge base. |
Expand Down
4 changes: 2 additions & 2 deletions packages/framework/prompts/SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Every prompt The Framework sends an agent lives here as markdown. Nothing agent-
- **Markdown is the only source of truth** - the prompts are compiled into the package at build time and the code uses the compiled copies; a prompt is edited in exactly one place.
- **The built-in system prompt** - the standing instructions every agent starts with: analyze the prompt, park at a gate when the user should decide, name the session before the first change, declare ready for merge only when finished.
- **The file formats** - the shapes agents must follow for tickets and for the agent queue, carried in the agent's own context rather than pointed at.
- **Branch management** - the section after the built-in prompt saying how the agent names its branch: the `skill-branches` package's own skill for an agent in a checkout The Framework created, else the fallback that has it branch with git itself.
- **Branch management** - the section after the built-in prompt for an agent outside a checkout The Framework created: the fallback that has it branch with git itself. An agent in such a checkout has the `branches` skill in the checkout instead, and the prompt sends it there.
- **The data-branch protocol** - where the framework's own data lives and how to read and write it without putting it on a code branch.
- **The protocols** - how an agent signals: awaited choices and ready for merge, plus the sections added only when it has a browser, when it runs hands-off, and when nothing can answer its questions.
- **The presets** - one file per launcher button and per routine prompt.
Expand Down Expand Up @@ -43,7 +43,7 @@ See `## User story`.

- **The system prompt** — The Framework's built-in standing instructions for every agent, and the slot the user's own prompt is rendered into. The user's own additions live in their repo instead, not here.
- **The file formats** — the ticketing format (how a ticket, its lock and its plan are written) and the agent-queue format (how `TODO_AGENTS.md` is banded by priority). They travel with the agent's context, so the agent has already read the format rather than having to find the file that describes it.
- **The data-branch protocol** — that tickets, the agent queue and the agent archives live on `tf-data`, are read off that branch, and are written straight to it instead of riding a pull request.
- **The data-branch protocol** — that tickets, the agent queue and the agent archives live on `agents-data`, are read off that branch, and are written straight to it instead of riding a pull request.
- **The triage scope rule** — the one-paragraph rule appended to both triage presets, that a triage only queues work and never implements it.
- **The protocols** — how an agent signals to The Framework, and what this particular agent can do.
- **The presets** — one file per preset: the launcher's buttons and the daemon's routine prompts.
Expand Down
14 changes: 7 additions & 7 deletions packages/framework/prompts/data_branch_protocol.SPEC.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
Tells the agent where The Framework's own data lives and how to touch it: tickets, the agent queue and the agent archives sit on the data branch `tf-data`, are read off that branch without ever checking it out, and are written as commits pushed straight to it rather than riding the agent's pull request.
Tells the agent where The Framework's own data lives and how to touch it: tickets, the agent queue and the agent archives sit on the data branch `agents-data`, are read off that branch without ever checking it out, and are written as commits pushed straight to it rather than riding the agent's pull request.

## Business logic — TL;DR

- **The data is not in the checkout** - `tickets/**.md` (plans and locks included), `TODO_AGENTS.md` and the agent archives exist only on `tf-data`, so the agent reads them off the branch after fetching, falling back to the remote copy when there is no local branch.
- **Writes go straight to the data branch** - only when the agent's instructions say to edit tickets or the queue, and then as a commit on `tf-data` pushed immediately, rebasing and retrying if the push is rejected.
- **Data never mixes with code** - the agent never switches its checkout to `tf-data` and never places these files on its own agent branch, so a data change is published on its own instead of arriving inside the pull request.
- **The data is not in the checkout** - `tickets/**.md` (plans and locks included), `TODO_AGENTS.md` and the agent archives exist only on `agents-data`, so the agent reads them off the branch after fetching, falling back to the remote copy when there is no local branch.
- **Writes go straight to the data branch** - only when the agent's instructions say to edit tickets or the queue, and then as a commit on `agents-data` pushed immediately, rebasing and retrying if the push is rejected.
- **Data never mixes with code** - the agent never switches its checkout to `agents-data` and never places these files on its own agent branch, so a data change is published on its own instead of arriving inside the pull request.

## Business logic

Expand All @@ -16,7 +16,7 @@ The user wants the project's default branch to stay 100% code. Everything The Fr

#### Business logic

The agent is told that this data lives on the dedicated branch `tf-data` and never on code branches, so its own checkout does not contain those files at all. To read one it fetches first and then reads the file directly out of the branch, using the remote copy when the local branch does not exist.
The agent is told that this data lives on the dedicated branch `agents-data` and never on code branches, so its own checkout does not contain those files at all. To read one it fetches first and then reads the file directly out of the branch, using the remote copy when the local branch does not exist.

### Writes go straight to the data branch

Expand All @@ -26,7 +26,7 @@ A triage or planning agent's whole output is a change to tickets or to the agent

#### Business logic

The agent writes these files only when its instructions tell it to edit tickets or the queue. Such a write is a commit made on `tf-data` and pushed immediately; a rejected push is rebased and retried rather than abandoned.
The agent writes these files only when its instructions tell it to edit tickets or the queue. Such a write is a commit made on `agents-data` and pushed immediately; a rejected push is rebased and retried rather than abandoned.

#### Rationale

Expand All @@ -40,7 +40,7 @@ The user reviews an agent's pull request as a diff of code. Bookkeeping files ap

#### Business logic

Two prohibitions carry this: the agent never switches its own checkout over to `tf-data`, and never puts these files on its agent branch. A data change is pushed directly and does not ride the pull request.
Two prohibitions carry this: the agent never switches its own checkout over to `agents-data`, and never puts these files on its agent branch. A data change is pushed directly and does not ride the pull request.

## Before modifying/creating SPEC.md files

Expand Down
6 changes: 3 additions & 3 deletions packages/framework/prompts/data_branch_protocol.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# The data branch

The framework's own data — `tickets/**.md` (plans and locks included), `TODO_AGENTS.md`, the session archives — lives on the dedicated branch `tf-data`, never on code branches. Your checkout does not contain these files.
The framework's own data — `tickets/**.md` (plans and locks included), `TODO_AGENTS.md`, the session archives — lives on the dedicated branch `agents-data`, never on code branches. Your checkout does not contain these files.

Read them off the branch (fetch first): `git show tf-data:<FILE>` — `origin/tf-data:` when the local branch doesn't exist.
Read them off the branch (fetch first): `git show agents-data:<FILE>` — `origin/agents-data:` when the local branch doesn't exist.

Write them, only when the instructions say to edit tickets or the queue, as a commit on `tf-data` pushed immediately — rebase and retry on a rejected push. Never switch your checkout to the branch, and never put these files on your session branch: a data change is pushed directly, it does not ride your PR.
Write them, only when the instructions say to edit tickets or the queue, as a commit on `agents-data` pushed immediately — rebase and retry on a rejected push. Never switch your checkout to the branch, and never put these files on your session branch: a data change is pushed directly, it does not ride your PR.
4 changes: 2 additions & 2 deletions packages/framework/prompts/system_prompt.SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ The Framework's built-in system prompt: the standing instructions every agent st

- **Analyze the prompt before working** - an unclear prompt becomes a plausibility-ranked list of interpretations offered as a choice, and the agent waits for the answer.
- **Large scope gets approved first** - large work is written up as a plan file shown to the user and awaits approval; very large work also seeds follow-up entries onto the agent queue.
- **Name the session** - before the first change the agent names the session and its branch after it, as the "Branch management" section says; the name it ends up with is the session name.
- **Name the session** - before the first change the agent names the session and its branch after it with the `branches` skill — or, where the skill is not available, as the "Branch management" section says; the name it ends up with is the session name.
- **Rate variability, offer alternatives** - each problem about to be solved is scored on how obviously optimal its solution is; low scorers are explored and their alternatives offered as a choice.
- **Ready for merge is explicit** - the agent signals it only when the task is finished; otherwise it states what remains.
- **The user's prompt is the last section** - the built-in instructions frame the system channel, and the user's own prompt is delivered as its own half.
Expand Down Expand Up @@ -51,7 +51,7 @@ See `## User story`: work must be reviewable as a pull request on a branch named

#### Business logic

Before applying its first change the agent picks a session name — an `[a-z0-9-]+` string that succinctly captures the intent of the user's prompt — and names its branch after it the way the "Branch management" section appended after the prompt says: the `branches` skill's command for an agent in a checkout The Framework created, git itself anywhere else. When the name the agent ends up with differs (the skill's command suffixes a taken name), that is the session name from then on.
Before applying its first change the agent picks a session name — an `[a-z0-9-]+` string that succinctly captures the intent of the user's prompt — and names its branch after it with the `branches` skill, which an agent in a checkout The Framework created finds in that checkout; where the skill is not available, the "Branch management" section appended after the prompt has it branch with git itself. When the name the agent ends up with differs (the skill's command suffixes a taken name), that is the session name from then on.

#### Rationale

Expand Down
2 changes: 1 addition & 1 deletion packages/framework/prompts/system_prompt.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Do the following before applying your first change.
### Session name

1. Create a <SESSION_NAME> as a string [a-z0-9-]+ that succinctly represents the intention of the user prompt
2. Name your branch after it, as the "Branch management" section below says — and if the name you end up with differs, that is your <SESSION_NAME> from now on
2. Name your branch after it: use the `branches` skill, which says how. Where that skill is not available, the "Branch management" section below says how instead. If the name you end up with differs, that is your <SESSION_NAME> from now on


## Before applying changes
Expand Down
20 changes: 5 additions & 15 deletions packages/framework/scripts/gen-prompts.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { readdir, readFile, writeFile } from 'node:fs/promises'
import { createRequire } from 'node:module'
import { dirname, join, relative } from 'node:path'
import { fileURLToPath } from 'node:url'

Expand Down Expand Up @@ -42,25 +41,16 @@ function constName(relPath) {
.toUpperCase()
}

// The `branches` skill (#1725) rides in the system channel the way the prompts above do,
// but its text is the package's, not this directory's: read from wherever the package is
// installed, so the instructions and the command they name can never come from two versions.
// Its front matter is the skill catalogue's metadata, not instructions, and is dropped.
const skillPath = createRequire(import.meta.url).resolve('@gemstack/skill-branches/SKILL.md')
const sources = [
...(await findMarkdown(promptsDir)).map(path => {
const relPath = relative(promptsDir, path).split('\\').join('/')
return { label: `prompts/${relPath}`, name: constName(relPath), path, frontMatter: false }
}),
{ label: '@gemstack/skill-branches/SKILL.md', name: 'BRANCHES_SKILL', path: skillPath, frontMatter: true },
]
const sources = (await findMarkdown(promptsDir)).map(path => {
const relPath = relative(promptsDir, path).split('\\').join('/')
return { label: `prompts/${relPath}`, name: constName(relPath), path }
})
const entries = await Promise.all(
sources.map(async source => {
const raw = await readFile(source.path, 'utf8')
const text = source.frontMatter ? raw.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n+/, '') : raw
// Strip exactly one trailing newline: the files end with one so they are well-formed on
// disk, the prompts they carry do not.
return { ...source, text: text.replace(/\n$/, '') }
return { ...source, text: raw.replace(/\n$/, '') }
}),
)

Expand Down
Loading
Loading