diff --git a/.the-framework/LAYOUT b/.the-framework/LAYOUT index b15f1b77c..425ef233b 100644 --- a/.the-framework/LAYOUT +++ b/.the-framework/LAYOUT @@ -1,5 +1,6 @@ framework-dir: .the-framework -data-branch: agents-data +tickets-branch: tickets +logs-branch: agents-logs archive-dir: agents events-file: events.jsonl meta-file: agent.json diff --git a/FEATURES-SPEC.md b/FEATURES-SPEC.md index 43bed1813..337fd63b4 100644 --- a/FEATURES-SPEC.md +++ b/FEATURES-SPEC.md @@ -89,10 +89,12 @@ happens while nobody is at the keyboard. - Hottest tickets - Projects sidebar - A project whose directory was renamed or deleted leaves the sidebar on the next refresh, and comes back when the directory does; the registration is kept -- Project errors: a project whose data branch cannot reach origin (push rejected, or no remote) is flagged with a red dot in the sidebar and a banner on its page, until a sync converges +- Project errors: a project whose bookkeeping branches cannot reach origin (push rejected, or no remote) — the `tickets` branch or the `agents-logs` branch — is flagged with a red dot in the sidebar and a banner on its page, until a sync converges ## Tickets +- Tickets and the queue live on the `tickets` branch, never on a code branch; every change is one commit pushed straight to it, so what one agent writes the next one sees without waiting for a pull request (#1748) +- A `tickets` link at your repository's root shows that branch's tickets from your own checkout, made only where nothing of yours sits at that path and kept out of git - Cross-project ticket list - Faceted filtering: text, priority/effort/uncertainty as buckets *or* ranges, topics, planning stage, project - Sorting and group-by-project toggle @@ -100,17 +102,21 @@ happens while nobody is at the keyboard. - Ticket detail page - A plan page when a plan exists; a button to start an agent writing one when it doesn't - "Resume agent" on the plan page: opens the session of the agent that wrote the plan, so the conversation continues with the plan already in its context -- Queue a ticket into the AI queue +- Queue a ticket into the AI queue — the entry is written onto the `tickets` branch, linked to the ticket and filed under the ticket's priority - Queue every ticket the filters show into the AI queue, in one click from the page heading - Queue a plan for every unplanned ticket the filters show, from the same heading +- A ticket whose work is done leaves the branch with its plan and its claim; a queue entry whose work is done is taken off the queue — done means deleted, never ticked off - Select tickets row by row (a checkbox per row) — while any are selected, the heading's queue buttons act on just the selected tickets - Tickets carry a GitHub issue link, so merging closes the issue +- A claimed ticket's row names the agent holding it — its session name, opening that agent's page — when the claim names one of the project's own agents; any other holder is shown as the claim writes it ## Handoff and what lands in git - Every agent gets its own git worktree under `.branches/` and its own branch (`agent-`), created before it starts; your checkout is never touched - 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 ` renames the agent's branch to `agent-` 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 `tickets` skill: every checkout The Framework creates carries the `@gemstack/skill-tickets` package's `SKILL.md` beside the `branches` skill, where the agent's harness looks for skills (`.claude/skills/tickets` for Claude Code, `.agents/skills/tickets` for Codex), and the presets tell the agent to use it — the tickets and the agent queue are on the `tickets` branch, read and changed with the command, claimed before they are planned or worked; an agent anywhere else (a terminal run in your checkout, an Actions runner, a cloud session) is told instead how to do the same with git, temporarily, until the skill is committed into the repository (#1748) +- `tickets` on every agent's PATH — the skill's command line: `list` and `show ` read the tickets with their plans and their holders, `queue` reads the queue in the order it is worked, `queue add ` puts an entry on it at a priority and linked to a ticket, `queue done ` takes an entry off, `put ` writes a ticket, a plan or the import stamp, `close ` removes a ticket with its plan and claim, `claim ` and `release ` are the claim; every one of them is one commit pushed to the `tickets` branch - The session name is the branch: an agent is labelled by its `agent-` 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 - A checkout whose work is not on the remote is kept — and a publish-nothing (`handoff: local`) agent's is kept until you publish or delete it @@ -122,7 +128,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 `agents-data` branch under per-user directories — pushed the moment a session settles +- Agent history archived on the `agents-logs` 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 @@ -134,13 +140,13 @@ happens while nobody is at the keyboard. - Every stand-down reported with its reason ("it is a setting, not a bug"); a stand-down at the concurrency cap, or a fan-out that came out short, names the runs holding the slots - Concurrency cap: how many unattended agents per project - Fan-out planning: several agents, one ticket each -- Cross-machine ticket claims so two agents never double-work; a claim whose agent ended with nothing to hand off is freed by the daemon +- Cross-machine ticket claims so two agents never double-work: a claim names the agent that holds it by its id, which the daemon mints before writing the claim and starts the agent with — so the dashboard names the holder's session on the ticket's row and opens that agent's page from it; a claim whose agent ended with nothing to hand off is freed by the daemon - CI watch: merge a PR once its checks pass - CI watch: one fix agent per red head commit, max two attempts - Reclaim the checkout of an agent whose work is on the remote — never by publishing what a `handoff: local` agent refused to - An agent that committed nothing leaves no branch behind: its empty branch goes with its checkout, never pushed — and the run-id branch it started on goes too, once the branch it moved to holds everything the run-id branch did - A directory under `.branches/` that git does not know as a worktree is never committed, pushed, linked or deleted through — it is reported and left alone, so a leftover can never stand in for your own checkout -- One triage at a time, across machines: a routine lock (`routines/.lock.md` on the data branch) taken by the daemon before the run starts and released when it ends, whatever the ending; a held lock stands the routine down naming the machine holding it, with no agent spent; a lock left by a dead machine expires after four hours, and a daemon frees its own on boot +- One triage at a time, across machines: a routine lock (`routines/.lock.md` on the `agents-logs` branch) taken by the daemon before the run starts and released when it ends, whatever the ending; a held lock stands the routine down naming the machine holding it, with no agent spent; a lock left by a dead machine expires after four hours, and a daemon frees its own on boot - The agent drains its own TODO backlog, one entry per turn ## Spending @@ -182,7 +188,7 @@ happens while nobody is at the keyboard. - Answer a cloud agent's question from the dashboard (typed back into claude.ai) — the same gate panel a local agent gets, multi-select and stop options included, listed with every other open question - Browser-bridge token setting - A cloud run's row follows the session's real branch and PR, with its armed draft PR opened when the session opens none -- Another machine's runs on the shared data branch are told apart: their rows carry a glyph naming the machine that started them (the Overview's working-now card spells it out), and a run is listed once even when two checkouts share its archive +- Another machine's runs on the shared `agents-logs` branch are told apart: their rows carry a glyph naming the machine that started them (the Overview's working-now card spells it out), and a run is listed once even when two checkouts share its archive ## Notifications diff --git a/SPEC.md b/SPEC.md index bc31db911..f8bef6a90 100644 --- a/SPEC.md +++ b/SPEC.md @@ -1,9 +1,10 @@ The Framework: autonomous AI programming. Humans make the important decisions; coding agents work the user's registered repos unattended and hand the result off as pull requests. The product never makes model calls of its own — it drives a coding-agent CLI the user already pays for (Claude Code or Codex) as a black box, on the user's own subscription. -Five top-level pieces, one product: +Six top-level pieces, one product: - `packages/framework` — the product itself, published as the npm package `framework`: one CLI (`the-framework`) that runs a foreground daemon, the agent lifecycle it orchestrates, and the browser dashboard it serves — the product's only user interface. -- `packages/skill-branches` — the git conventions and operations behind an agent's own checkout, as an API, as the `branches` command every agent the daemon starts on its machine gets on its PATH, and as the skill (`SKILL.md`) every agent's built-in system prompt carries, published as `@gemstack/skill-branches`: the first of the skills the product is being split into, and the only one so far. The product depends on it; it depends on nothing of the product. +- `packages/skill-branches` — the git conventions and operations behind an agent's own checkout, as an API, as the `branches` command every agent the daemon starts on its machine gets on its PATH, and as the skill (`SKILL.md`) every agent's built-in system prompt carries, published as `@gemstack/skill-branches`: the first of the skills the product is being split into. The product depends on it; it depends on nothing of the product. +- `packages/skill-tickets` — the project's tickets and its agent queue, on the `tickets` branch of the project's own repository, published as `@gemstack/skill-tickets`: the same three faces — an API, the `tickets` command every agent the daemon starts gets on its PATH, and the skill (`SKILL.md`) every such agent finds in its checkout — over reading tickets, writing them, claiming one so two agents never work the same, and keeping the queue. The second of the skills. The product depends on it, and it depends on skill-branches for the branch it stores everything on. - `packages/agent-driver` — the driver seam as its own package, published as `agent-driver`: one contract for driving a coding-agent CLI as a black box — a session in a directory, one full turn per prompt, a stream of what the agent did — and the implementations for Claude Code and Codex on this device, Claude Code on a GitHub Actions runner, and a scripted fake. The product depends on it and adds the one implementation that needs the product, the hand-off to a Claude Code cloud session; it depends on nothing of the product. - `packages/chrome-extension` — the Claude web bridge, a companion Chrome extension: when an agent's task was handed to a Claude Code cloud session on claude.ai, it carries the question that session is parked on into the local dashboard, and types the answer picked there back into the session. - `packages/the-framework.ai` — the marketing website at https://the-framework.ai. @@ -22,7 +23,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 `agents-data` branch, so the default branch stays 100% code. +- **Nothing bookkeeping-shaped on a code branch** - the tickets and the agent queue live on the `tickets` branch, which is the `tickets` skill's; The Framework's own records of its runs — the agent archives, the routine locks — live on the `agents-logs` branch. The default branch stays 100% code. ## Business logic @@ -44,7 +45,7 @@ See `## User story`: the daemon keeps working while nobody is around, within the #### Business logic -On a shared clock the daemon runs its background jobs: Auto PM works the agent queue down and refills it by triaging tickets and planning the ones without plans; the CI watch merges the framework's pull requests once their checks pass and starts a fix agent when checks go red; sweeps reclaim finished agents' checkouts (only what is already on the remote) and keep bookkeeping healthy, and a routine that must not run twice is guarded by a routine lock on the data branch. Each unattended start checks the quota boundary first. +On a shared clock the daemon runs its background jobs: Auto PM works the agent queue down and refills it by triaging tickets and planning the ones without plans; the CI watch merges the framework's pull requests once their checks pass and starts a fix agent when checks go red; sweeps reclaim finished agents' checkouts (only what is already on the remote) and keep bookkeeping healthy, and a routine that must not run twice is guarded by a routine lock on the `agents-logs` branch. Each unattended start checks the quota boundary first. ## Before modifying/creating SPEC.md files diff --git a/package.json b/package.json index a626ae978..0d66f42cf 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,8 @@ "preview": "pnpm -C packages/framework preview", "dev": "pnpm -C packages/framework dev", "typecheck": "pnpm -C packages/framework typecheck && pnpm -C packages/the-framework.ai typecheck", - "clean": "pnpm -C packages/skill-branches clean && pnpm -C packages/agent-driver clean && pnpm -C packages/framework clean", - "test": "pnpm -C packages/skill-branches test && pnpm -C packages/agent-driver test && pnpm -C packages/framework test", + "clean": "pnpm -C packages/skill-branches clean && pnpm -C packages/skill-tickets clean && pnpm -C packages/agent-driver clean && pnpm -C packages/framework clean", + "test": "pnpm -C packages/skill-branches test && pnpm -C packages/skill-tickets test && pnpm -C packages/agent-driver test && pnpm -C packages/framework test", "========= Website": "", "// Develop website https://the-framework.ai locally": "", "website": "cd packages/the-framework.ai/ && pnpm run dev", diff --git a/packages/SPEC.md b/packages/SPEC.md index 0a839b10d..977643441 100644 --- a/packages/SPEC.md +++ b/packages/SPEC.md @@ -1,12 +1,13 @@ The deliverables The Framework ships, one directory each: - `framework/` — the product: the `framework` npm package (CLI, daemon, agent lifecycle, dashboard). -- `skill-branches/` — the `@gemstack/skill-branches` npm package: the git conventions and operations behind an agent's own checkout, with the skill's instructions (`SKILL.md`) and the `branches` command an agent follows them with: the first skill of the skills-plus architecture (#1725). The product depends on it; nothing else does yet. +- `skill-branches/` — the `@gemstack/skill-branches` npm package: the git conventions and operations behind an agent's own checkout, with the skill's instructions (`SKILL.md`) and the `branches` command an agent follows them with: the first skill of the skills-plus architecture (#1725). The product depends on it, and so does the skill-tickets package. +- `skill-tickets/` — the `@gemstack/skill-tickets` npm package: the project's tickets and its agent queue on the `tickets` branch of its own repository, with the skill's instructions (`SKILL.md`) and the `tickets` command an agent reads, writes and claims them with: the second skill of the skills-plus architecture (#1748). The product depends on it; it depends on skill-branches for the branch it keeps everything on. - `agent-driver/` — the `agent-driver` npm package: the driver seam — start a coding-agent CLI in a directory, prompt it for one full turn, stream what it does, resume it later — with the Claude Code, Codex, GitHub Actions and fake implementations. The product depends on it and adds its own cloud-session implementation behind the same contract. - `chrome-extension/` — the Claude web bridge, a companion Chrome extension that connects Claude Code cloud sessions on claude.ai back to the local dashboard. - `the-framework.ai/` — the marketing website. -The product depends on the skill-branches and agent-driver packages in code; the extension and the website depend on the product only in what they present. See the root `SPEC.md` for how they relate as a product. +The product depends on the skill-branches, skill-tickets and agent-driver packages in code; the extension and the website depend on the product only in what they present. See the root `SPEC.md` for how they relate as a product. ## Before modifying/creating SPEC.md files diff --git a/packages/framework/SPEC.md b/packages/framework/SPEC.md index eb84cf1f9..4cd622e1b 100644 --- a/packages/framework/SPEC.md +++ b/packages/framework/SPEC.md @@ -2,7 +2,7 @@ The product: the `framework` npm package. One CLI, `the-framework`, runs a foreg How the package's pieces relate: -- `src/` — everything that runs in Node: the CLI, the daemon, the agent lifecycle (checkouts through the `skill-branches` package, gates, handoff), the drivers (the agent-driver package's, plus the product's own hand-off to a Claude Code cloud session), the data branch, autonomy (Auto PM, sweeps, CI watch), and the server side of the dashboard. +- `src/` — everything that runs in Node: the CLI, the daemon, the agent lifecycle (checkouts through the `skill-branches` package, gates, handoff), the drivers (the agent-driver package's, plus the product's own hand-off to a Claude Code cloud session), the `agents-logs` branch its own records live on (the tickets and the queue are the `skill-tickets` package's), autonomy (Auto PM, sweeps, CI watch), and the server side of the dashboard. - `dashboard/` — the browser app the daemon serves: a single-page app that is a pure projection of the files the daemon writes, reading over `POST /_rpc/` and a live event stream, and steering agents back through the same daemon. - `prompts/` — every prompt The Framework sends an agent, as markdown: the built-in system prompt, the protocols agents answer through, and the presets. The markdown is the only source of truth for agent-facing text. - `scripts/` — the build steps that compile the prompts into importable strings and run the package's test suites. diff --git a/packages/framework/dashboard/App.SPEC.md b/packages/framework/dashboard/App.SPEC.md index 7de5bb2ee..9eae1b06c 100644 --- a/packages/framework/dashboard/App.SPEC.md +++ b/packages/framework/dashboard/App.SPEC.md @@ -30,7 +30,7 @@ The user wants to paste an agent's address to a colleague, reload without losing #### Business logic -The address alone says what is on screen: the Overview at the root, a project's home and launcher under the project, one agent under that project, plus the settings page, the tickets list, one ticket's page, and that ticket's plan page. Every navigation the shell offers — picking a project, picking an agent, opening a ticket, going to settings — is an ordinary history entry, so Back and Forward work throughout, and reopening the dashboard returns to the project the user was last in. +The address alone says what is on screen: the Overview at the root, a project's home and launcher under the project, one agent under that project, plus the settings page, the tickets list, one ticket's page, and that ticket's plan page. Every navigation the shell offers — picking a project, picking an agent, opening a ticket, opening the agent that holds a ticket's claim (from the tickets list or from the ticket's own page, in that ticket's own project), going to settings — is an ordinary history entry, so Back and Forward work throughout, and reopening the dashboard returns to the project the user was last in. #### Rationale diff --git a/packages/framework/dashboard/App.tsx b/packages/framework/dashboard/App.tsx index b33ef40af..323e14d4e 100644 --- a/packages/framework/dashboard/App.tsx +++ b/packages/framework/dashboard/App.tsx @@ -283,7 +283,7 @@ export function App() { // A ticket's own page needs both a project and a slug; anything short of that (including the // bare cross-project route) is the list — every registered project, one section each. if (view === 'tickets' && projectId && ticketSlug) - return + return if (view === 'tickets') return ( ) if (!projectId) diff --git a/packages/framework/dashboard/components/AgentHistory.SPEC.md b/packages/framework/dashboard/components/AgentHistory.SPEC.md index d41cfc1f1..547e982b4 100644 --- a/packages/framework/dashboard/components/AgentHistory.SPEC.md +++ b/packages/framework/dashboard/components/AgentHistory.SPEC.md @@ -52,7 +52,7 @@ Scanning the list, the user needs to tell at a glance which agents need them, wh Each row shows a status word with a matching dot: a working agent pulses and reads its running status; an agent parked on the user reads "waiting" with a still dot, because it used to pulse identically whether it was mid-edit or had been idle for an hour; an agent that ended clean but has not yet reported its handoff reads "publishing…" with a pulsing dot, matching the wording on its own page; an agent that handed its task to a cloud session reads, once its local half is done, what the cloud side is doing per the cloud state rule (`cloud-run-state`): "waiting" with a still dot when the browser bridge holds a question its session is parked on, "in cloud" while the session is assumed to still be working, "merged" once the framework merged its pull request, and "done" once it has a pull request or the session can no longer be working — never "in cloud" forever. Otherwise the row shows the agent's final status. -Beside that: how long ago it started (preceded by its project name on the Overview), and, at the end of the line, a glyph naming the machine when another machine's daemon started the agent — the data branch is shared, so other machines' agents are listed here, and one that looked like this daemon's own was a mystery; a glyph rather than a word, since the rail's fixed width would cut a machine name short — a device glyph naming the device when the agent runs on another machine, a cloud glyph when it is a cloud session, and the logo of the driver that ran it. Underneath sits the agent's task; a task too long for the rail's fixed width is faded at its end and shows in full on hover, while one that fits is shown plainly with no hover at all. +Beside that: how long ago it started (preceded by its project name on the Overview), and, at the end of the line, a glyph naming the machine when another machine's daemon started the agent — the `agents-logs` branch the archives live on is shared, so other machines' agents are listed here, and one that looked like this daemon's own was a mystery; a glyph rather than a word, since the rail's fixed width would cut a machine name short — a device glyph naming the device when the agent runs on another machine, a cloud glyph when it is a cloud session, and the logo of the driver that ran it. Underneath sits the agent's task; a task too long for the rail's fixed width is faded at its end and shows in full on hover, while one that fits is shown plainly with no hover at all. ### Destinations and what is highlighted diff --git a/packages/framework/dashboard/components/AgentHistory.test.tsx b/packages/framework/dashboard/components/AgentHistory.test.tsx index e93c9f820..827a4ae1f 100644 --- a/packages/framework/dashboard/components/AgentHistory.test.tsx +++ b/packages/framework/dashboard/components/AgentHistory.test.tsx @@ -429,7 +429,7 @@ describe('project errors in the Projects list (#1500)', () => { expect(dot).toBeTruthy() expect(screen.getByText('Error:')).toBeTruthy() const tooltip = await hoverTooltip(dot!) - expect(tooltip.textContent).toContain('The data branch is not syncing') + expect(tooltip.textContent).toContain('Not syncing with the remote') expect(tooltip.textContent).toContain('Permission denied (publickey)') }) diff --git a/packages/framework/dashboard/components/AgentHistory.tsx b/packages/framework/dashboard/components/AgentHistory.tsx index e6198a1d0..2e5b28d20 100644 --- a/packages/framework/dashboard/components/AgentHistory.tsx +++ b/packages/framework/dashboard/components/AgentHistory.tsx @@ -626,7 +626,7 @@ function AgentHistoryRow({ being decorative. */} {(remote || cloud || picked || startedOn) && ( - {/* Another machine's daemon started this run (#1648): the shared data branch lists every + {/* Another machine's daemon started this run (#1648): the shared agents-logs branch lists every machine's runs here, and one that looked exactly like this daemon's own was a mystery solved only by reading the archive. A glyph, not a word in the meta line: the rail's fixed width truncated a hostname to "from…". */} diff --git a/packages/framework/dashboard/components/AiQueue.SPEC.md b/packages/framework/dashboard/components/AiQueue.SPEC.md index 21670712e..0d812b9f7 100644 --- a/packages/framework/dashboard/components/AiQueue.SPEC.md +++ b/packages/framework/dashboard/components/AiQueue.SPEC.md @@ -27,7 +27,7 @@ The user wants to see what the framework will work on next. #### Business logic -The card lists, per project, that project's open agent queue entries, with a count of open entries beside the project's name. Projects with nothing open are left out entirely; when no project has anything open the card says nothing is queued, and while the queue is still being fetched it says it is loading. Every open entry is shown — a collapsed plan is one the user cannot read. Checked-off entries are not listed. +The card lists, per project, that project's open agent queue entries, with a count of open entries beside the project's name. Projects with nothing open are left out entirely; when no project has anything open the card says nothing is queued, and while the queue is still being fetched it says it is loading. Every open entry is shown — a collapsed plan is one the user cannot read. Every entry on the queue is an open one: an entry whose work is done is taken off the queue rather than marked. Each entry's line is markdown, so the card prints its title rather than its source, with the whole raw line available on hover. @@ -53,7 +53,7 @@ The user starts a queued entry themselves and expects the same outcome the frame #### Business logic -The play button starts a single agent whose prompt narrows the drain preset's instruction to this one entry: open `TODO_AGENTS.md`, work on this one open entry only, then check it off, and start no other entry. The prompt quotes the entry's raw queue line rather than the tidied title, because the agent has to find exactly that line to check it off, and because the line's own link is how the agent opens the ticket. +The play button starts a single agent whose prompt narrows the drain preset's instruction to this one entry: work on this one open entry of the agent queue only, take it off the queue when the work is done, and start no other entry. The prompt quotes the entry's raw queue line rather than the tidied title, because the agent has to name exactly that line to take it off the queue, and because the line's own link is how the agent opens the ticket. The agent is started unattended, with the user's own agent preferences: its gates auto-answer, it ends when it settles, and its armed handoff fires — instead of parking in the stay-open chat loop with its PR never opened. diff --git a/packages/framework/dashboard/components/AiQueue.test.tsx b/packages/framework/dashboard/components/AiQueue.test.tsx index 028f72b37..000f57e46 100644 --- a/packages/framework/dashboard/components/AiQueue.test.tsx +++ b/packages/framework/dashboard/components/AiQueue.test.tsx @@ -115,7 +115,7 @@ describe('AiQueue', () => { await waitFor(() => expect(start).toHaveBeenCalled()) const [projectId, prompt, kind, options] = start.mock.calls[0]! expect(projectId).toBe('p1') - // The raw line, not the pretty label: the agent must find exactly this entry to check it off. + // The raw line, not the pretty label: the agent must find exactly this entry to take it off the queue. expect(prompt).toBe(workOnEntryPrompt(entry)) expect(prompt).toContain(entry) expect(kind).toBe('prompt') @@ -209,7 +209,7 @@ describe('AiQueue', () => { fireEvent.click(screen.getByRole('button', { name: fanOutLabel(3) })) await waitFor(() => expect(start).toHaveBeenCalledTimes(3)) // Each agent is pinned to its own entry, in queue order — the raw lines, since each agent must - // find exactly its entry to check it off. + // find exactly its entry to take it off the queue. expect(start.mock.calls.map(call => call[1])).toEqual([ workOnEntryPrompt('one'), workOnEntryPrompt('two'), diff --git a/packages/framework/dashboard/components/AiQueue.tsx b/packages/framework/dashboard/components/AiQueue.tsx index 1073e05ad..12480d174 100644 --- a/packages/framework/dashboard/components/AiQueue.tsx +++ b/packages/framework/dashboard/components/AiQueue.tsx @@ -9,7 +9,7 @@ import { Card, CardContent, CardHeader, CardTitle } from './ui/card.js' import { Tooltip, TooltipTrigger, TooltipContent } from './ui/tooltip.js' import { StartAgentButton } from './StartAgentButton.js' -// The Overview's AI Queue card (#1139): every project's open `TODO_AGENTS.md` items — the work the +// The Overview's AI Queue card (#1139): every project's open `TODO_AGENTS.md` entries — the work the // framework picks up on its own — grouped by project and shown in full. No "+N more": this is the // plan, and a collapsed plan is one you cannot read. // @@ -29,14 +29,14 @@ import { StartAgentButton } from './StartAgentButton.js' // DashboardPage has no test file, and opening tickets and starting runs are behaviour worth pinning. /** - * The prompt the play button starts an agent with: the drain preset's vocabulary ("work on … then - * check it off. Do not start any other entry.") narrowed from "the FIRST open entry" to the one - * entry the row shows. The raw `TODO_AGENTS.md` line, not the pretty label: the agent must find - * exactly this entry to check it off, and the line's link is how it opens the ticket (#1164). - * Exported so the test asserts against this and not a copy. + * The prompt the play button starts an agent with: the drain preset's vocabulary (work one entry + * through the `tickets` skill, then take it off the queue; do not start any other entry) narrowed + * from "the FIRST open entry" to the one entry the row shows. The raw `TODO_AGENTS.md` line, not + * the pretty label: the agent must name exactly this entry to take it off, and the line's link is + * how it opens the ticket (#1164). Exported so the test asserts against this and not a copy. */ export function workOnEntryPrompt(entry: string): string { - return `Open TODO_AGENTS.md and work on this one open entry only, then check it off. Do not start any other entry. The entry:\n\n${entry}` + return `Use the \`tickets\` skill: work on this one open queue entry only, and when the work is done and published run \`tickets queue done ""\`. Do not start any other entry. The entry:\n\n${entry}` } /** How many agents the fan-out button starts until its count says otherwise. */ diff --git a/packages/framework/dashboard/components/OnboardingChecklist.SPEC.md b/packages/framework/dashboard/components/OnboardingChecklist.SPEC.md index 4e0571583..c64ad0cd5 100644 --- a/packages/framework/dashboard/components/OnboardingChecklist.SPEC.md +++ b/packages/framework/dashboard/components/OnboardingChecklist.SPEC.md @@ -24,7 +24,7 @@ See `## User story`. **Add a project** — done once at least one project is registered. A project is described as a git repository The Framework may work in. While it is not done, the card offers to register the directory the daemon is running in (naming that directory on the button) when that directory is not already a project, and always offers the add-project dialog — the system folder picker — as the alternative. A failure to register is reported in place. -**Populate the queue of AI tasks** — done once at least one unchecked entry exists in a project's `TODO_AGENTS.md`. The card explains that each unchecked entry is work the agent picks up on its own, so a filled queue is what lets it keep going unattended. This step has no button: the queue is filled by the agent and by the user's own planning. +**Populate the queue of AI tasks** — done once a project's agent queue (`TODO_AGENTS.md`) holds at least one open entry. The card explains that every entry on the queue is work the agent picks up on its own, so a filled queue is what lets it keep going unattended. This step has no button: the queue is filled by the agent and by the user's own planning. **Populate `tickets/`** — optional; done once any project has tickets. The card explains that `tickets/` holds the bigger things to work on, that the agent researches and plans them, and that they are the input the queue is filled from. Its button, "Update from GitHub", starts an agent on the target project with the `update_tickets` preset — whose empty-`tickets/` behaviour is the first import — and the dashboard then lands on that agent so the user watches the import happen rather than arriving at an empty launcher. Beside it, "Configure first, then run" opens the target project's launcher carrying that same preset instead of starting anything, for the user who wants the import on a different model or somewhere other than this machine. With no project registered yet, both halves are disabled and the step says to add a project first — there is no project to start in, and none to open a launcher for. diff --git a/packages/framework/dashboard/components/OnboardingChecklist.tsx b/packages/framework/dashboard/components/OnboardingChecklist.tsx index dcd4f5bd3..d1e009dc7 100644 --- a/packages/framework/dashboard/components/OnboardingChecklist.tsx +++ b/packages/framework/dashboard/components/OnboardingChecklist.tsx @@ -139,7 +139,7 @@ export function OnboardingChecklist({ key: 'todos', label: 'Populate the queue of AI tasks', description: - 'TODO_AGENTS.md is the queue: each unchecked item is work the agent picks up on its own, so a filled queue is what lets it keep going without you.', + 'TODO_AGENTS.md is the queue: each entry is work the agent picks up on its own, so a filled queue is what lets it keep going without you.', done: (data?.totals.openTodos ?? 0) > 0, }, { diff --git a/packages/framework/dashboard/components/ProjectErrorBanner.SPEC.md b/packages/framework/dashboard/components/ProjectErrorBanner.SPEC.md index b98c03fa0..e0f9eef62 100644 --- a/packages/framework/dashboard/components/ProjectErrorBanner.SPEC.md +++ b/packages/framework/dashboard/components/ProjectErrorBanner.SPEC.md @@ -2,7 +2,7 @@ The warning banner at the top of a project's page, listing every project error t ## Glossary -- **project error** - a condition the daemon has found broken about a project and keeps recorded until the condition is gone. Today the only kind is a data branch that cannot sync, headlined "The data branch is not syncing". +- **project error** - a condition the daemon has found broken about a project and keeps recorded until the condition is gone. Today the only kind is one of the project's two bookkeeping branches failing to converge with origin — the `tickets` branch, holding the tickets and the agent queue, or the `agents-logs` branch, holding the agent archives and the routine locks — headlined "Not syncing with the remote". ## Business logic — TL;DR diff --git a/packages/framework/dashboard/components/ProjectErrorBanner.test.SPEC.md b/packages/framework/dashboard/components/ProjectErrorBanner.test.SPEC.md index f1474274f..e151fc559 100644 --- a/packages/framework/dashboard/components/ProjectErrorBanner.test.SPEC.md +++ b/packages/framework/dashboard/components/ProjectErrorBanner.test.SPEC.md @@ -1,4 +1,4 @@ -What the tests cover: a project the daemon finds nothing wrong with gets no banner at all; a data branch that cannot sync is announced as an alert carrying its headline ("The data branch is not syncing"), the underlying failure message word for word, and how long it has been failing. +What the tests cover: a project the daemon finds nothing wrong with gets no banner at all; a bookkeeping branch that cannot sync with origin is announced as an alert carrying its headline ("Not syncing with the remote"), the underlying failure message word for word, and how long it has been failing. ## Before modifying/creating SPEC.md files diff --git a/packages/framework/dashboard/components/ProjectErrorBanner.test.tsx b/packages/framework/dashboard/components/ProjectErrorBanner.test.tsx index 4da8d61fc..7c398d8a7 100644 --- a/packages/framework/dashboard/components/ProjectErrorBanner.test.tsx +++ b/packages/framework/dashboard/components/ProjectErrorBanner.test.tsx @@ -22,7 +22,7 @@ describe('ProjectErrorBanner (#1500)', () => { test('a data-sync error reads as an alert: headline, the emitter’s own words, and since when (#1599)', () => { render() const alert = screen.getByRole('alert') - expect(alert.textContent).toContain('The data branch is not syncing') + expect(alert.textContent).toContain('Not syncing with the remote') expect(alert.textContent).toContain('Permission denied (publickey)') expect(alert.textContent).toContain('since 3h ago') }) diff --git a/packages/framework/dashboard/components/ProjectErrorBanner.tsx b/packages/framework/dashboard/components/ProjectErrorBanner.tsx index e98c6a9aa..51a695576 100644 --- a/packages/framework/dashboard/components/ProjectErrorBanner.tsx +++ b/packages/framework/dashboard/components/ProjectErrorBanner.tsx @@ -3,7 +3,7 @@ import type { ProjectError, ProjectErrorCode } from '../../src/index.js' import { formatAge } from '../lib/format-date.js' // What the daemon currently finds wrong with the project (#1500), at the top of its page. The -// daemon records the state and clears it when the condition is gone (#1599: a data branch that +// daemon records the state and clears it when the condition is gone (#1599: a tickets or agents-logs branch that // cannot reach origin), so this renders exactly what the project list carries — no state of its // own, and nothing to dismiss: the way to make it go away is to fix the thing it names. @@ -11,7 +11,7 @@ import { formatAge } from '../lib/format-date.js' export function projectErrorTitle(code: ProjectErrorCode): string { switch (code) { case 'data-sync': - return 'The data branch is not syncing' + return 'Not syncing with the remote' } } diff --git a/packages/framework/dashboard/components/ProjectHome.SPEC.md b/packages/framework/dashboard/components/ProjectHome.SPEC.md index 36a80348f..d50ae7297 100644 --- a/packages/framework/dashboard/components/ProjectHome.SPEC.md +++ b/packages/framework/dashboard/components/ProjectHome.SPEC.md @@ -23,7 +23,7 @@ The project error banner sits between the project's action bar and the start for #### Rationale -An agent started on a project whose data branch cannot reach origin works from stale tickets and writes into an agent queue nobody else will ever see. Naming that failure after the start form would be too late. +An agent started on a project whose `tickets` branch cannot reach origin works from stale tickets and writes into an agent queue nobody else will ever see. Naming that failure after the start form would be too late. ### Every parked agent, answerable here diff --git a/packages/framework/dashboard/components/ProjectHome.tsx b/packages/framework/dashboard/components/ProjectHome.tsx index a70a88749..2ffe38125 100644 --- a/packages/framework/dashboard/components/ProjectHome.tsx +++ b/packages/framework/dashboard/components/ProjectHome.tsx @@ -49,7 +49,7 @@ export function ProjectHome({ return ( - {/* Above the start form, because an agent started on a project whose data branch cannot + {/* Above the start form, because an agent started on a project whose tickets or agents-logs branch cannot reach origin (#1599) works from stale tickets and a queue nobody else will see. */}