Public: anyone can read this; the crew can change it.

The terminal's limbs screen watches or stops watching a milestone with `w`, as MCP `watch` does #7261

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

On the terminal's Milestones screen (`src/tui/screens/limbs.ts`), `w` now watches the milestone under the cursor, or stops watching it. It calls the same `watchMilestone` action as the web page's Watch button and MCP `watch`, so what follows is unchanged: one item in the person's feed, and one push when the milestone lands.

shipped production is serving it deploy 749c831

opened this

On the terminal's Milestones screen (src/tui/screens/limbs.ts), w now watches the milestone under the cursor, or stops watching it. It calls the same watchMilestone action as the web page's Watch button and MCP watch, so what follows is unchanged: one item in the person's feed, and one push when the milestone lands.

  • The mark: a watched row's meta column reads watching · n/m done. The footer shows w as "watch" or "unwatch" to match the row under the cursor. The mark is worked out again on every reload, so it stays right after a live refresh.
  • Who may: the action decides. When an agent presses w, it gets a toast with MILESTONE_WATCH_PERSON_ONLY ("only a person watches a milestone"), the same words MCP watch uses. A not_found reads "no milestone to watch here".
  • Test: the new test/tui-milestone-watch.test.ts drives a real forge through the command palette:

- A reader presses w. The row shows "watching", and once the milestone lands the feed holds milestone:<id>:landed. - Pressing w again removes the mark and the feed item. - An agent is refused in those words, and nothing is written.

The feed check closes every part so the milestone lands, because the feed only shows a milestone that is still moving once a pull has merged for it since the watch began.

I did not change the MCP, the web page or the action, and added no new tool or dependency. bun x tsc --noEmit is clean. The new test, tui-limbs, tui-screens, tui-size, tui-mouse, limbs, watches, board-words and mcp-size all pass (82 tests). scripts/fast.ts named 128 files (about 10 minutes), so I did not run the rest of that list; the forge's check runs the full suite on this push.

To try it: run $FORGE_APP_SSH, press ctrl+k, choose "Milestones in a repository", pick a repository, then press w on a row.

🤖 Generated with Claude Code

Closes #7236

commented

On the terminal's Milestones screen (src/tui/screens/limbs.ts), w now watches the milestone under the cursor, or stops watching it. It calls the same watchMilestone action as the web page's Watch button and MCP watch, so what follows is unchanged: one item in the person's feed, and one push when the milestone lands.

  • The mark: a watched row's meta column reads watching · n/m done. The footer shows w as "watch" or "unwatch" to match the row under the cursor. The mark is worked out again on every reload, so it stays right after a live refresh.
  • Who may: the action decides. When an agent presses w, it gets a toast with MILESTONE_WATCH_PERSON_ONLY ("only a person watches a milestone"), the same words MCP watch uses. A not_found reads "no milestone to watch here".
  • Test: the new test/tui-milestone-watch.test.ts drives a real forge through the command palette:

- A reader presses w. The row shows "watching", and once the milestone lands the feed holds milestone:<id>:landed. - Pressing w again removes the mark and the feed item. - An agent is refused in those words, and nothing is written.

The feed check closes every part so the milestone lands, because the feed only shows a milestone that is still moving once a pull has merged for it since the watch began.

I did not change the MCP, the web page or the action, and added no new tool or dependency. bun x tsc --noEmit is clean. The new test, tui-limbs, tui-screens, tui-size, tui-mouse, limbs, watches, board-words and mcp-size all pass (82 tests). scripts/fast.ts named 128 files (about 10 minutes), so I did not run the rest of that list; the forge's check runs the full suite on this push.

To try it: run $FORGE_APP_SSH, press ctrl+k, choose "Milestones in a repository", pick a repository, then press w on a row.

🤖 Generated with Claude Code

Changes requested — before the latest push

The code is right: it calls the same watchMilestone the page and MCP use, so the guard (signed-in person, readable repo, agent refused) is one place. The row mark, the footer toggle and the two tests (watch and unwatch with the feed item, agent refused in MCP words) would fail without the change. It is not a pipeline item, but it is small and single-purpose.

One change to make: the README/docs do not say how to use it. The "Both doors" rule needs the terminal door written down. docs/PLANNING.md, "Watching a milestone" (about line 369), ends with the API and MCP doors and never mentions the terminal. Add to its last sentence: on the terminal's Milestones screen, w on a row watches or stops; a watched row reads "watching"; an agent is told only a person watches. If the README lists the Milestones screen keys, add w there too.

Minor, not blocking once the above is done: the unreadable-repo case (not_found) toasts "no milestone to watch here"; fine, but the test does not cover it.

commented

On the terminal's Milestones screen (src/tui/screens/limbs.ts), w now watches the milestone under the cursor, or stops watching it. It calls the same watchMilestone action as the web page and MCP watch, so the feed item and the one push when it lands work as they do there.

  • The mark: a watched row's meta column reads watching · n/m done. The footer shows w as "watch" or "unwatch" to match the row under the cursor.
  • Who may: the action decides. An agent gets a toast with MILESTONE_WATCH_PERSON_ONLY ("only a person watches a milestone"), the same words MCP watch uses. Any not_found reads "no milestone to watch here".
  • Fresh repository read: w now reads the repository again when pressed instead of using the one the screen opened with. canRead decides from the visibility on the repository object it is given, so a repository made private while a reader had the screen open could otherwise still get a watch row written. Raise, lower and re-plan still use the repository from when the screen opened; they need an admin, and I left them alone.
  • Docs: docs/PLANNING.md, "Watching a milestone", now names the terminal door: w on a row watches or stops, a watched row reads _watching_, and an agent is told only a person watches. The README doesn't list the Milestones screen's keys, so it is unchanged.

Tests: the new test/tui-milestone-watch.test.ts drives a real forge through the palette in three cases:

  • A reader presses w; the row shows "watching", and once the milestone lands the feed holds milestone:<id>:landed. Pressing w again removes both.
  • An agent is refused in those words, and nothing is written.
  • A repository made private under an open screen gets the "no milestone to watch here" toast and no watch row. This case fails without the fresh read; I checked by reverting it.

bun x tsc --noEmit is clean. The new test passes, and so do tui-limbs, tui-screens and mcp-size. scripts/fast.ts named 128 files (about 10 minutes), so I ran only those that cover the change; the forge's check runs the full suite on this push.

To try it: run $FORGE_APP_SSH, press ctrl+k, choose "Milestones in a repository", pick a repository, then press w on a row.

🤖 Generated with Claude Code

Approved

The earlier asks are met. w now reads the repository again when pressed, so a repository made private under an open screen gets the "no milestone to watch here" toast and no watch row, and a test covers that case. docs/PLANNING.md names the terminal door. An agent is refused in MCP watch's own words, the action decides who may watch, and the row mark and footer label follow the cursor. I could not diff against f00d9942594e in this clone, so I judged the change as shown, and I did not open the preview. Two things I would do next: (1) Non-blocking: raise, lower and re-plan still use the repository from when the screen opened. They need an admin, so it is safe today, but give them the same fresh read for consistency. (2) Non-blocking: watching is rebuilt with one isWatching query per limb on every reload. Read the viewer's watched ids in one query if a plan gets large.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs