Skip to content

The dashboard shows what the daemon finds wrong with a project; first emitter: the data-branch sync (fix #1599, towards #1500) - #1604

Open
suleimansh wants to merge 3 commits into
mainfrom
project-errors
Open

The dashboard shows what the daemon finds wrong with a project; first emitter: the data-branch sync (fix #1599, towards #1500)#1604
suleimansh wants to merge 3 commits into
mainfrom
project-errors

Conversation

@suleimansh

Copy link
Copy Markdown
Member

🤖 agent PR

Fix #1599. First slice of #1500 (the issue stays open for the agent-emitted half).

What the user sees. A project whose data branch cannot reach origin — the push is rejected (the live case: a daemon started without the ssh-agent, so every git push origin tf-data on an SSH-remote project fails with Permission denied (publickey)), or the repository has no remote at all — now shows it:

  • in the sidebar's Projects list, the project's dot turns red and the tooltip names the error;
  • on the project's page, a banner above the start form: "The data branch is not syncing · since 3h ago — the data branch could not be pushed: Permission denied (publickey)".

Both disappear on the first sync that converges. Nothing to dismiss: the way to make it go away is to fix the thing it names. Until now both states were one line on the daemon's stdout.

How. One new module, src/project-errors.ts: an in-memory per-project error state — one slot per (project, kind) holding the message and when it was first seen; a repeat report of the same kind refreshes the message and keeps since. The daemon's background jobs write it, the dashboard reads it through a new projectErrors capability on the dashboard context. No new RPC or stream: onProjects() attaches each project's errors to its ProjectSummary, and the shell's project list now polls (30s) instead of reading once, so the sidebar dot and the banner follow the daemon's minute cadence.

The first emitter is the data-sync tick (syncProjectData in daemon-services.ts): pullDataBranch now reports its outcome, and a no-remote repo counts as a failure to converge — the writer is right to treat it as fine (the commit is safe locally), but a sync's whole job is to meet the other machines, and #1595 removed no-remote as a supported mode. The stdout line stays.

Not in this PR. The agent-side "code block" emitter #1500's description asks for — agents reporting an error through a capability rather than a prompt instruction. Same store, a second emitter; #1500 tracks it.

Tests. project-errors.test.ts (the store), dashboard-rpc/projects.test.ts (onProjects carries the errors), a real-git case in daemon-services.test.ts (no remote → error; bare remote added → cleared), ProjectErrorBanner.test.tsx, and two sidebar cases in AgentHistory.test.tsx. Suite green.

Dogfood. Needs a daemon rebuilt from this branch and restarted from a shell with the ssh-agent (SSH_AUTH_SOCK) to see the healthy state, or without it to see the banner on every SSH-remote project.

@suleimansh

Copy link
Copy Markdown
Member Author

🤖 agent comment

SPEC walkthrough. Per modified SPEC file: where it sits (intro + TLDR as the file now reads), the diff, and what the diff means against the rest of the file. One sentence covers all of it: the daemon now records, per project, the states only the user can fix, and the dashboard shows them — starting with a data branch that cannot reach origin.


1. src/project-errors.SPEC.md + src/project-errors.test.SPEC.md — new, for the new module. The whole file is the story:

The daemon's per-project error state: when a background job finds a project in a state only the user can fix, it records the error here, and clears it the moment the state is good again — the dashboard shows whatever is recorded.

## TLDR

- One slot per project and kind of error, holding the detail and when it was first seen; a repeat report of the same kind refreshes the detail but keeps the first-seen time, so the dashboard can say how long the project has been in that state.
- The first emitter is the data-branch sync: a push that origin rejects, or a repository with no remote at all, is recorded every minute it persists and cleared on the first sync that converges.
- Held in memory only: every emitter re-evaluates on its own cadence, so a restarted daemon re-learns each error within a tick, and no stale record can outlive the condition that raised it.

## Rationales

- These states used to be console lines on the daemon's stdout, which nobody reads — swallowing an error is the worst way to handle it. A repository with no remote is not a supported mode but an error, since every other machine and cloud session converges through that remote.

The test SPEC: the store lists nothing for a clean project, a recorded error carries kind + detail + first-seen time, a repeat keeps the first-seen time, a clear removes it (and a later report starts a fresh clock), and projects never see each other's errors.


2. src/daemon-services.SPEC.md — the background jobs. Intro + the TLDR line that changed, in its context:

Everything the daemon runs in the background beside serving the dashboard: Discord notifications, automatic project management, CI watching, data-branch syncing, and disk reclamation.

## TLDR
- One clock runs every background job, each declaring how many ticks it wants between turns ...
- ...
- The data branch is pulled eagerly, so this machine reads what other machines and cloud sessions pushed without waiting for its own next write.

Diff:

-- The data branch is pulled eagerly, so this machine reads what other machines and cloud sessions pushed without waiting for its own next write.
+- The data branch is pulled eagerly, so this machine reads what other machines and cloud sessions pushed without waiting for its own next write. A project whose branch cannot converge — origin rejects the push, or there is no remote at all — is recorded as that project's error state for the dashboard to show, and the record is cleared by the first sync that converges.

Meaning: the eager pull is one of the jobs on the one clock (first TLDR line). It now has a second output besides the converged checkout — the per-project error state from file 1. "Cleared by the first sync that converges" is the same job on its next turn: set and clear are both the sync's own doing, no other job touches the record. The test SPEC gains the matching sentence (a repo with no remote → data-sync error; a bare remote added → cleared on the next turn).


3. src/data-branch.SPEC.md — the tf-data branch itself. Intro + the TLDR line that changed:

All the data The Framework writes — the tickets, the task queue, the session archives — lives on one dedicated branch, `tf-data`, so the code history stays 100% code and the data can be pushed and pulled eagerly without ever touching anyone's working tree.

## TLDR
- The branch is checked out at `.the-framework/branches/tf-data`, and a `tickets` symlink at the repo root points into it ...
- The daemon is the only local writer: every write is one serialized cycle — sync with origin, apply the change, commit, push ...
- Every machine (and every cloud session) converges on the same data by pulling the branch eagerly; a write that cannot reach the network stays committed locally and rides out on the next cycle.
- A project with the branch already on origin adopts it; a project without one births it with an empty history of its own ...

Diff:

-- Every machine (and every cloud session) converges on the same data by pulling the branch eagerly; a write that cannot reach the network stays committed locally and rides out on the next cycle.
+- Every machine (and every cloud session) converges on the same data by pulling the branch eagerly; a write that cannot reach the network stays committed locally and rides out on the next cycle. The eager pull says when it could not converge — origin rejected the push, or the repository has no remote to converge with — because a data branch nobody else can reach is an error for the user to fix, not a mode.

Meaning: the writer's rule (a failed push keeps the commit local, previous sentence) is unchanged — a write is never lost. What changes is the pull's answer: it now says whether it converged. The no-remote case is the deliberate part: for the writer a remote-less repo is fine (the commit is safe), for the pull it is a failure, because the pull's purpose — "every machine converges on the same data" — is impossible there. That is #1599's "no-remote is an error state, not a mode".


4. src/dashboard-rpc/projects.SPEC.md — the project-list call. Intro + TLDR as it now reads:

The project list and the launcher's pre-flight answers: which projects are registered, adding new ones, and whether an agent started now would get anywhere.

## TLDR

- Each listed project carries what the daemon's background jobs currently find wrong with it — a data branch that cannot reach origin, say — so the one list every project surface already polls is also how an error reaches the sidebar and the project's page.
- Adding a project (one repo, or every repo under a folder) goes through the daemon so it lands in the shared registry ...
- The pre-flight reads warn before a doomed start rather than after ...

Diff: the first TLDR line is new. Meaning: the error state from file 1 reaches the browser on the project list rather than through a call or stream of its own — the list is what the sidebar and every project surface already read. The new projects.test.SPEC.md: a healthy project lists no errors at all; a project with a recorded error lists it with kind, detail and first-seen time.


5. Dashboard — ProjectErrorBanner.SPEC.md (new), ProjectHome.SPEC.md, AgentHistory.SPEC.md, App.SPEC.md.

New banner component, whole file:

The banner at the top of a project's page naming what the daemon currently finds wrong with the project — a headline per kind of error, the failing command's own words underneath, and since when.

## TLDR

- Renders exactly what the project list carries and holds nothing of its own: there is no dismiss, because the daemon clears the error the moment the condition is fixed.

ProjectHome.SPEC.md (the project home / launcher), diff:

 - Never consumed by an agent: starting one adds its own view alongside, and this page stays put so you can launch again.
+- Anything the daemon finds wrong with the project — a data branch that cannot reach origin — sits as a banner above the start form, until the daemon sees it fixed.
 - Below the start form: every agent's open questions in one answerable place, then the project's docs ...

Meaning: the page's order is start form → open questions → docs; the banner goes above the start form, because an agent started on a project whose data branch cannot reach origin works from stale tickets and a queue nobody else will see.

AgentHistory.SPEC.md (the left sidebar), diff:

 - The Overview item carries the Human Queue count.
+- A project the daemon has found something wrong with — a data branch that cannot reach origin — gets a red dot in the Projects list, naming the error on hover.

Meaning: the Projects list already has a dot per project (activated or not); an error takes the dot over, red, with the error on the tooltip — the page carries the full banner. The test SPEC gains the two cases (red dot naming the error; a healthy project keeps its activated dot).

App.SPEC.md (the one page that owns what the views share), diff:

-- Owns what the views share: the agent list, project files, the cross-project needs-you queue, and the one live event stream the main view and right rail both read.
+- Owns what the views share: the agent list, the project list (each project carrying what the daemon currently finds wrong with it), project files, the cross-project needs-you queue, and the one live event stream the main view and right rail both read.

Meaning: the project list joins the shared reads the shell owns — it was read once for the tab title; now it is polled, because the error state it carries appears and clears on the daemon's cadence and both the sidebar dot and the banner follow it.


6. FEATURES-SPEC.md — one new row under Overview page, after "66 Projects sidebar":

+| 125 | 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 |

suleimansh and others added 3 commits August 20, 2026 13:43
… emitter: the data-branch sync (fix #1599, towards #1500)

A per-project error state the daemon's background jobs set and clear
(`project-errors.ts`): one slot per project and kind, with the detail and
since when. The project list carries it (`onProjects` attaches each
project's errors), the sidebar turns the project's dot red naming the
error on hover, and the project's page shows a banner above the start
form. The first emitter is the data-sync tick: a push origin rejects, or
a repository with no remote at all, sets the `data-sync` error; the first
sync that converges clears it. `pullDataBranch` now reports its outcome,
counting no-remote as a failure to converge, since a data branch nobody
else can reach is an error (#1595), not a mode.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ect test omits errors instead of passing undefined

CI's typecheck caught both: the prop was declared but never destructured (a
ReferenceError at runtime, which Vite does not check), and
exactOptionalPropertyTypes refuses an explicit undefined.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ctive was never defined here

Seen live: the sidebar dot vanished instead of turning red, and the banner
rendered without colour — Tailwind emits nothing for an unknown token.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@suleimansh

Copy link
Copy Markdown
Member Author

@brillout ready for your read — the first slice of #1500, as we agreed on #1599. Dogfooded live: it caught a real case on my machine the moment it ran (ssh-agent without identities → every SSH-remote project's tf-data push failing, invisible until now). SPEC walkthrough is in the agent comment above. #1500 stays open for the agent-side emitter.

@brillout

Copy link
Copy Markdown
Member

Didn't understand everything, but I guess it's okay for now.

It seems to be old-school hard-wired code, I wonder whether we can remove hard-wired code and just give agents the capability (via a code-block-macro). But I guess it's ok for now (we can refactor and remove code later).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Data-branch sync failures should surface in the dashboard

2 participants