Skip to content

Mark tasks carrying an artifact or a link on the dashboard - #410

Draft
tildesrc wants to merge 1 commit into
mainfrom
panopticon/dashboard-mark-column
Draft

Mark tasks carrying an artifact or a link on the dashboard#410
tildesrc wants to merge 1 commit into
mainfrom
panopticon/dashboard-mark-column

Conversation

@tildesrc

Copy link
Copy Markdown
Contributor

Scanning the task table told you a task's state and whose turn it was, but not whether it had a plan to read or a PR to open — you had to press a or p on each row to find out.

This adds a two-slot marks column, left of the name it annotates: when the task has an unhidden artifact, when it has a url. The header doubles as the legend.

  state          turn       container   repo       ❏➚  slug[memo]
  ITERATING      agent      live        web-api    ❏➚  add-oauth[Add OAuth login]
  PLANNING       user       live        web-api    ❏   fix-upload[Flaky S3 upload]
  MERGING        agent      starting    dashboard   ➚  dark-mode[Dark-mode theme]
  ORCHESTRATING  agent      live        infra          q3-cleanup[Q3 tech-debt]

Getting the bit to the dashboard

Artifacts are files rather than task columns, so presence has to ride along on the list response or the dashboard would need a request per row on every refresh. GET /tasks now reports has_artifacts, resolved once per response through a new bulk ArtifactStore.tasks_with_artifacts. It's concrete on the ABC — defaulting to list() per id, the way link_slug defaults to a no-op — so no adapter breaks by not knowing about it, and the filesystem store overrides it with a single-pass os.scandir that stops at the first unhidden entry.

Writing an artifact now bumps the change feed too. An artifact stores no row of its own, so without that a newly written plan.md wouldn't wake a parked long-poll and the mark would lag until some unrelated mutation came along.

The dotfile rule that decides "unhidden" moves to core.artifacts.is_hidden, shared with the dashboard's existing "Show hidden" toggle rather than spelled out at each surface.

Why these glyphs

(U+274F) and (U+279A) rather than 📁/🔗: both are East_Asian_Width=Neutral with no emoji presentation form, so they occupy exactly one cell in every terminal. Emoji are Width=Wide, which makes the rendered column width depend on the viewer's terminal and font. A test pins equal cell width across all four mark combinations, so a later edit can't quietly regress it.

Notes for the reviewer

  • has_artifacts is on the summary shape only, not TaskOut. _task_out is sync and called from ~20 mutation handlers; making it async to add a listdir per mutation response isn't worth it for a field only the table needs, and GET /tasks/{id}/artifacts already answers it exactly for one task.
  • Tests that read row cells positionally now resolve the index by column label, so inserting a column doesn't renumber assertions across the file.
  • Pre-existing and left alone: the table already renders Ambiguous-width glyphs (the │ ├─ └─ connectors, the status placeholder, the snoozed ·), which do drift in a CJK-configured terminal. Independent of this change and worth its own decision.

Plan: the task's plan.md artifact.

Scanning the task table told you a task's state and whose turn it was, but not
whether it had a plan to read or a PR to open — you had to press `a` or `p` on
each row to find out. Add a two-slot marks column, left of the name it
annotates: `❏` when the task has an unhidden artifact, `➚` when it has a url.
The header doubles as the legend.

Artifacts are files rather than task columns, so presence has to ride along on
the list response or the dashboard would need a request per row on every
refresh. `GET /tasks` now reports `has_artifacts`, resolved once per response
through a new bulk `ArtifactStore.tasks_with_artifacts`. It is concrete on the
ABC (defaulting to `list()` per id, like `link_slug` defaults to a no-op), with
the filesystem store overriding it with a single-pass `os.scandir` that stops
at the first unhidden entry. Writing an artifact now bumps the change feed too
— it stores no row of its own, so without that a newly written plan would not
wake a parked long-poll and the mark would lag until an unrelated mutation.

The dotfile rule that decides "unhidden" moves to `core.artifacts.is_hidden`,
shared with the dashboard's existing "Show hidden" toggle.

`❏` (U+274F) and `➚` (U+279A) rather than 📁/🔗 because both are
East_Asian_Width=Neutral with no emoji presentation form: exactly one cell in
every terminal. Emoji are Width=Wide, which would make the column's rendered
width depend on the viewer's terminal and font. A test pins equal cell width
across all four mark combinations to keep a later edit from regressing that.

Tests that read row cells positionally now resolve the index by column label,
so inserting a column doesn't renumber assertions across the file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.

1 participant