Skip to content

feat(crewmates): show on watch and goal on every mate row, one row - #24716

Merged
iamwhatever merged 1 commit into
mainfrom
feat/crewmate-row-watch-goal
Oct 27, 2026
Merged

iamwhatever merged 1 commit into
mainfrom
feat/crewmate-row-watch-goal

Conversation

@CrysisDeu

Copy link
Copy Markdown
Contributor

Problem / Motivation

Goal: Every mate row, on the full roster and in the switcher dropdown, shows who is on watch and who has a goal, and the two lists draw one row.

Neither list said which mates act on their own (a monitor loop or the heartbeat) or which hold a goal; the user had to open each mate's Goals and Schedules tabs. The roster's only cue, the On watch badge, read the slot's monitor automation and ignored the heartbeat, and the switcher had no on-watch cue at all. The two rows were hand-written twice and had drifted: unread dot accent vs ok, second line Working / agent template vs the preview with a Stopped chip, on-watch mark on one list only.

Why it matters

The Crewmates page is where the owner sees the crew at a glance. Without these two facts the roster cannot answer "who is working on something, and who is running by itself" without a click per mate, and two drifting rows mean the next marker has to be built twice and will disagree.

Not a goal

  • Changing the Goals tab, the heartbeat schedule, or how monitors are armed.
  • The switcher's chip and unread pills.
  • Profile's Monitor section and the face flight, which keep reading the monitor record alone.

What changed (motivation → approach → change)

  • Backend: GET /api/members rows carry has_goal and heartbeat_on, read off each member's goal record in one thread hop (dashboard/members_roster_goals.goal_facts_for_rows); an unreadable record reads as neither so the row still renders. A goal write pushes an agents refresh frame (start_roster_refresh, one member_goals.add_change_listener, started beside crewmate_feed.start), which is the frame the roster query already re-reads on.
  • One row: website/src/pages/members/CrewmateRowBody.tsx renders the face (presence dot, on-watch ring), the name with a named Goal glyph at the row's top-right corner while the mate holds a goal, the status line (Stopped chip, preview, sr-only "On watch"), and the needs-you / unread marks. MemberRow and the switcher option both render it; the host keeps its chrome (star and team indent; role=option).
  • On watch is one predicate (isLoopOn): an active monitor OR heartbeat_on. The row ring, the header pill and the Scheduled check-ins filter read it; CrewMonitoredAvatar takes a watch prop ORed with the record. The filter's glyph becomes Radar since Goal now means a goal.
  • Switcher: no check mark (selection is aria-selected + the row fill); the open mate is pinned first, the rest keep roster order. Design chosen on real-pod screenshots with the owner (word badges and a status-line glyph were tried and rejected: "let the layout do the explaining").
  • Catalog: pages.membersPage.row_goal ("Has a goal") in all 12 locales; switcher_unread removed (no reader left). Spec: docs/system-specs/modules/crew-mode.md.

Backwards compatibility

Compatible: two optional fields added to the roster row; an older client ignores them. The switcher's Working line, Unread word and check mark are visual changes to one component, not a contract.

Reader: website/src/pages/members/CrewmateSwitcher.tsx:CrewmateSwitcher -- crew page -- the switcher_unread key's only reader; the row now renders CrewmateRowBody, whose unread dot carries the unread_message name, pinned by CrewmateSwitcher.test.tsx "each row carries the roster cues".

Tests

  • test/test_members_roster_goals.py: both facts off the record (none / goal / goal + heartbeat), another name under the same slug reads empty, an unreadable record costs the row its facts not the roster its answer, GET /api/members ships both on every row, one listener per process, a goal write from a worker thread pushes ("agents",) on the serving loop, nothing is pushed before the loop is bound.
  • CrewmateSwitcher.test.tsx: the open mate pinned first and selected, no Working word, the member-style ids on the shared row, the goal mark named "Has a goal" and the sr-only on-watch word in the option's name, the ring drawn for patrolling only.
  • rosterFilter.test.ts: MemberSignals.goal.

Manual verification

Pod crewmate-row-watch-goal (isolated gateway on the worktree build), seeded through the API: oncall with a goal and heartbeat on, scribe with a goal, Mate with neither. Checked GET /api/members shows has_goal / heartbeat_on per row; the roster column and the switcher dropdown both draw the ring on oncall, the goal mark on oncall and scribe, nothing on Mate; the switcher pins the open mate first; the header pill still says "On watch" for the heartbeat-only mate.

Screenshots / video

Switcher dropdown (scribe open and pinned first; oncall on watch; both with the goal mark) and the full roster column:

#1 switcher dropdown
#2 roster column

Full page

#3 full page

Related Issues

Closes #24687

Checklist

  • At most two commits (one is the norm), with a Conventional Commits title (feat|fix|docs|style|refactor|perf|test|chore|ci|build|revert: ...)
  • Existing tests pass and new tests added for new functionality
  • Self-review completed; code follows project style guidelines
  • Documentation updated (if applicable)
  • No secrets, credentials, or internal references in the diff

Contribution License Agreement

Contributions are accepted under the project's Apache-2.0 license; see the Licensing section of CONTRIBUTING.md.

@CrysisDeu
CrysisDeu requested review from a team and KiroCrewCaptain as code owners October 27, 2026 04:26
@CrysisDeu
CrysisDeu requested a review from mbriones98 October 27, 2026 04:26
@github-actions github-actions Bot added the readiness: checking Automated validation is still running label Oct 27, 2026
@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Design Review (Opus 5.5) — ✅ PASS

Design-level review of 4f5c089f16e6ff4a83834399f61fa2e5f8a64c72 — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS does not.

Design-Verdict: PASS

No design-level concerns: both facts come from the goal record, the existing agents refresh frame carries them, and the two drifted rows now share one component.

[DESIGN-REVIEWED] 4f5c089

Description read

[DESCRIPTION-READ] 456bf1d058265242424f436b01694800034358db5490bf6ff020c5ba4d324dc8

That sha256 names everything this verdict read: the PR description, plus the 1 evidence file(s) the workflow collected from it and handed this review. It covers that evidence because the media strip replaces every attachment URL with the same placeholder, so an edit swapping one attachment for another would leave the description's captured text identical while what this review judged changed. Recompute it with .github/scripts/pr-description-capture.sh over the same inputs; if it differs, the description or that evidence changed after this verdict was formed and any finding drawn from either is unproven.

@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

UX Review (Opus 5.5) — ✅ human override accepted

@CrysisDeu overrode this lane for 4f5c089f16e6ff4a83834399f61fa2e5f8a64c72 via /ai-review override (this commit only).

@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

First Principles Review (Opus 5.5) — ✅ human override accepted

@CrysisDeu overrode this lane for 4f5c089f16e6ff4a83834399f61fa2e5f8a64c72 via /ai-review override (this commit only).

@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention and removed readiness: checking Automated validation is still running labels Oct 27, 2026
@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Opus 5.5 Review — ✅ no blocking findings

Reviewed 4f5c089f16e6ff4a83834399f61fa2e5f8a64c72 — this comment is updated in place on each push.

Review details

No findings.

[OPUS-REVIEWED] 4f5c089

Verdict parsed from the review's SHA-scoped output markers for commit 4f5c089f16e6ff4a83834399f61fa2e5f8a64c72.

False positive or not applicable? A repository writer can comment:
/ai-review override fable 4f5c089f16e6ff4a83834399f61fa2e5f8a64c72 false-positive: <reason, 20 to 500 characters>

@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

GPT 6.1 Review — ✅ no blocking findings

GPT 6.1 completed its review of 4f5c089f16e6ff4a83834399f61fa2e5f8a64c72 and found no blocking issues.

This comment is updated in place on each push.

Review details

No findings.
[GPT-REVIEWED] 4f5c089

False positive or not applicable? A repository writer can comment:
/ai-review override gpt 4f5c089f16e6ff4a83834399f61fa2e5f8a64c72 false-positive: <reason, 20 to 500 characters>

@CrysisDeu
CrysisDeu force-pushed the feat/crewmate-row-watch-goal branch from 9aa44b5 to fa373fd Compare October 27, 2026 04:42
@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed readiness: action required A blocking check or review needs attention labels Oct 27, 2026
@CrysisDeu

Copy link
Copy Markdown
Contributor Author
  • fixed span=ccded86a5c09 — The switcher loses its accessible working-state cue

fixed in fa373fd: CrewmateRowBody renders an sr-only drawer_working word while signals.running, beside the sr-only on-watch word, so the option's name says Working again; pinned by CrewmateSwitcher.test.tsx ("Working is spoken, not shown").

@CrysisDeu

Copy link
Copy Markdown
Contributor Author
  • rebutted span=6ea3dc2cb3b5 — The goal glyph is not understood

Rebutted: owner decision. The owner (Zezhen) chose this mark on real-pod screenshots of three variants: a "Has a goal" word badge (A), a status-line glyph (B) and this corner glyph, under his standing rule for the Crewmates page: avoid unnecessary secondary labels and descriptions, let the layout do the explaining. A word per row was tried and rejected as noise on a list where most mates will hold a goal.
The mark is a named image (role="img", aria-label and title "Has a goal"), so it is spoken and it has a hover name; it sits inside the row button, so there is nothing separate to press, and the Goal glyph is the same one the Goals tab already uses for this concept.
A legend or label is new scope; a different glyph is the owner's call, recorded in the PR body ("Design chosen on real-pod screenshots with the owner").

@CrysisDeu

Copy link
Copy Markdown
Contributor Author
  • rebutted span=f710e2007e4e — The on-watch ring reads as unread or busy

Rebutted: owner decision. The ring is the existing on-watch mark (CrewLoopIndicator, shipped on the roster row, the header pill and Profile), not one this PR introduces; this PR only lights it for the heartbeat too and draws it in the switcher. The owner asked for the "On watch" word to go ("the ring on the avatar already shows it, only the goal needs a mark"), the same no-extra-labels rule as the goal mark.
The ring keeps its tooltip (title "On watch" on the indicator), the row carries the word sr-only, and the header pill beside the switcher still shows "On watch" in text for the open mate, so the state is readable in text on the page.

@CrysisDeu

Copy link
Copy Markdown
Contributor Author

/ai-review override ux fa373fd not-applicable: agent: owner decision (Zezhen) — the goal glyph and the ring-only on-watch mark were chosen on real-pod screenshots over word badges under his no-extra-labels rule for the Crewmates page; both marks are named images with tooltips and sr-only words.

@github-actions

Copy link
Copy Markdown
Contributor

Human judgment recorded

@CrysisDeu recorded a not-applicable release (class not-applicable) for the ux AI finding on fa373fd68ebbc22783555e0927c147aefed6bf28.

agent: owner decision (Zezhen) — the goal glyph and the ring-only on-watch mark were chosen on real-pod screenshots over word badges under his no-extra-labels rule for the Crewmates page; both marks are named images with tooltips and sr-only words.

This decision applies only to this commit. A new push requires a new judgment.

@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention and removed readiness: checking Automated validation is still running labels Oct 27, 2026
@CrysisDeu
CrysisDeu force-pushed the feat/crewmate-row-watch-goal branch from fa373fd to 441cce5 Compare October 27, 2026 05:54
@github-actions github-actions Bot added readiness: checking Automated validation is still running merge conflict Branch has merge conflicts with its base — author must resolve before merge and removed readiness: action required A blocking check or review needs attention labels Oct 27, 2026
@CrysisDeu
CrysisDeu force-pushed the feat/crewmate-row-watch-goal branch from 441cce5 to d32e423 Compare October 27, 2026 06:07
@CrysisDeu

Copy link
Copy Markdown
Contributor Author

Rebased: main moved in src/kiro_crew/dashboard/ (handlers/crew_log.py, work_ledger*.py, ws.py, websocket_hub.py, state_parts/dashboard_state.py and siblings) since this head went green (45d3eff -> a623d85); CI re-running on d32e423. Same push also bounds the cross-thread wait in test_members_roster_goals.py (GPT round 3).

@CrysisDeu

Copy link
Copy Markdown
Contributor Author
  • fixed span=801949667739 — Bound the cross-thread refresh wait

fixed in d32e423: await asyncio.wait_for(pushed.wait(), timeout=60) and worker.join(timeout=60) + assert not worker.is_alive() — a lost-run ceiling at or above the wait-discipline ratchet's 30s floor (the earlier 5s bound was what that ratchet refused), so a swallowed listener error fails the test locally instead of at the process timeout.

@CrysisDeu
CrysisDeu force-pushed the feat/crewmate-row-watch-goal branch from d32e423 to 2d9717d Compare October 27, 2026 06:19
@CrysisDeu

Copy link
Copy Markdown
Contributor Author

Rebased: d32e423 conflicted with main (a623d85 -> 561b345: the Profile Monitor section moved to the Schedules tab, touching docs/feature-map/README.md, crew-mode.md and every locale), so no pull_request workflow could run on it. Resolved by taking main and re-applying this PR's additions only (the CrewmateRowBody row, the heartbeat-aware on-watch sentence, the row_goal key); CI re-running on 2d9717d.

@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed readiness: action required A blocking check or review needs attention labels Oct 27, 2026
@CrysisDeu

Copy link
Copy Markdown
Contributor Author
  • rebutted span=09736015e7c7 — The goal glyph is not understood

Rebutted: owner decision. The owner (Zezhen) chose this mark on real-pod screenshots of three variants: a "Has a goal" word badge (A), a status-line glyph (B) and this corner glyph, under his standing rule for the Crewmates page: avoid unnecessary secondary labels and descriptions, let the layout do the explaining. A word per row was tried and rejected as noise on a list where most mates will hold a goal.
The mark is a named image (role="img", aria-label and title "Has a goal"), so it is spoken and it has a hover name; it sits inside the row button, so there is nothing separate to press, and the Goal glyph is the same one the Goals tab already uses for this concept.
A legend or label is new scope; a different glyph is the owner's call, recorded in the PR body ("Design chosen on real-pod screenshots with the owner").

@CrysisDeu

Copy link
Copy Markdown
Contributor Author
  • rebutted span=a2f12d02b325 — The on-watch ring reads as unread or busy

Rebutted: owner decision. The ring is the existing on-watch mark (CrewLoopIndicator, shipped on the roster row, the header pill and Profile), not one this PR introduces; this PR only lights it for the heartbeat too and draws it in the switcher. The owner asked for the "On watch" word to go ("the ring on the avatar already shows it, only the goal needs a mark"), the same no-extra-labels rule as the goal mark.
The ring keeps its tooltip (title "On watch" on the indicator), the row carries the word sr-only, and the header pill beside the switcher still shows "On watch" in text for the open mate, so the state is readable in text on the page.

@CrysisDeu

Copy link
Copy Markdown
Contributor Author

/ai-review override ux 457aeab not-applicable: agent: owner decision (Zezhen) — the goal glyph and the ring-only on-watch mark were chosen on real-pod screenshots over word badges under his no-extra-labels rule for the Crewmates page; both marks are named images with tooltips and sr-only words. Same ruling as on fa373fd and 2d9717d.

@github-actions

Copy link
Copy Markdown
Contributor

Human judgment recorded

@CrysisDeu recorded a not-applicable release (class not-applicable) for the ux AI finding on 457aeab41a47976ede80a5f76c0c4b7fc5615686.

agent: owner decision (Zezhen) — the goal glyph and the ring-only on-watch mark were chosen on real-pod screenshots over word badges under his no-extra-labels rule for the Crewmates page; both marks are named images with tooltips and sr-only words. Same ruling as on fa373fd and 2d9717d.

This decision applies only to this commit. A new push requires a new judgment.

@CrysisDeu

Copy link
Copy Markdown
Contributor Author

Main is red again: test/test_crew_log_fullscan_guard.py::test_a_warm_session_read_that_earns_a_savepoint_reads_only_the_gap is a strict xfail that now XPASSes on main (crew_log savepoint work in #24276 / #24294), so Backend Tests shard 2 is red on every PR; this PR touches neither file. Fix in #24774 (drops the strict xfail); this PR waits for it, then rebases.

@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention and removed readiness: checking Automated validation is still running labels Oct 27, 2026
The Crewmates roster and the switcher dropdown did not say which mates
act on their own or which hold a goal, and each hand-wrote its own row,
so the two had already drifted (unread dot colour, second line, on-watch
cue). GET /api/members now carries has_goal and heartbeat_on off each
member's goal record, and a goal write pushes an agents refresh so the
rows re-read. Both lists render one CrewmateRowBody: the face with its
presence dot and on-watch ring (a monitor on the slot OR the heartbeat),
a named Goal glyph at the row's top-right corner while the mate holds a
goal, the Stopped chip and preview, the needs-you and unread marks. The
switcher drops its check mark (selection is the row's fill) and pins
the open mate first. The Scheduled check-ins filter reads the same
on-watch predicate and takes the Radar glyph, since Goal now means a
goal. Spec: crew-mode.md.

Closes #24687
@CrysisDeu
CrysisDeu force-pushed the feat/crewmate-row-watch-goal branch from 457aeab to 4f5c089 Compare October 27, 2026 14:25
@CrysisDeu

Copy link
Copy Markdown
Contributor Author

Rebased: main's shard-2 fix (#24774, the crew_log strict xfail) merged (aeb5c02 -> 6632fd7); CI re-running on 4f5c089.

@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed readiness: action required A blocking check or review needs attention labels Oct 27, 2026
@CrysisDeu

Copy link
Copy Markdown
Contributor Author
  • rebutted span=9f3989533951 — Unread word removed against its own recorded rationale

Rebutted: owner decision. The goal of this PR is ONE row for the roster and the switcher (issue #24687: "unify the two rows"), and the roster row never carried an "Unread" word -- its unread mark has always been the named ok dot alone (unread_message as aria-label/title), the same shape ChatSidebar uses (#10479/#10488). Keeping a word on one copy of the row is the drift this PR removes; the owner (Zezhen) chose the roster row's shape on real-pod screenshots under his no-extra-labels rule for the Crewmates page.
The state is not colour-alone: the dot is a role="img" with a spoken and hoverable name, needs-you keeps its word because it asks something of the reader, and the unread pills under the chip still name the state in text. This PR passed First Principles on this same point in rounds 2-5 (2d9717d, 457aeab); the lane's verdict on it varies run to run.

@CrysisDeu

Copy link
Copy Markdown
Contributor Author

/ai-review override ux 4f5c089 not-applicable: agent: owner decision (Zezhen) — the goal glyph and the ring-only on-watch mark were chosen on real-pod screenshots over word badges under his no-extra-labels rule for the Crewmates page; both marks are named images with tooltips and sr-only words. Same ruling as on fa373fd, 2d9717d and 457aeab.

@CrysisDeu

Copy link
Copy Markdown
Contributor Author

/ai-review override first-principles 4f5c089 not-applicable: agent: owner decision (Zezhen) — the switcher row takes the roster row's unread mark (a named ok dot, no word) because the PR's goal is one shared row; the roster never carried the word, and this lane passed the same point on 2d9717d and 457aeab.

@CrysisDeu

Copy link
Copy Markdown
Contributor Author
  • rebutted span=434cc6099ae7 — The goal glyph is not understood

Rebutted: owner decision. The owner (Zezhen) chose this mark on real-pod screenshots of three variants: a "Has a goal" word badge (A), a status-line glyph (B) and this corner glyph, under his standing rule for the Crewmates page: avoid unnecessary secondary labels and descriptions, let the layout do the explaining. A word per row was tried and rejected as noise on a list where most mates will hold a goal.
The mark is a named image (role="img", aria-label and title "Has a goal"), so it is spoken and it has a hover name; it sits inside the row button, so there is nothing separate to press, and the Goal glyph is the same one the Goals tab already uses for this concept.
A legend or label is new scope; a different glyph is the owner's call, recorded in the PR body ("Design chosen on real-pod screenshots with the owner"). Same ruling as on fa373fd, 2d9717d and 457aeab.

@CrysisDeu

Copy link
Copy Markdown
Contributor Author
  • rebutted span=e23bc7320f4e — The on-watch ring reads as unread or busy

Rebutted: owner decision. The ring is the existing on-watch mark (CrewLoopIndicator, shipped on the roster row, the header pill and Profile), not one this PR introduces; this PR only lights it for the heartbeat too and draws it in the switcher. The owner asked for the "On watch" word to go ("the ring on the avatar already shows it, only the goal needs a mark"), the same no-extra-labels rule as the goal mark.
The ring keeps its tooltip (title "On watch" on the indicator), the row carries the word sr-only, and the header pill beside the switcher still shows "On watch" in text for the open mate, so the state is readable in text on the page. Same ruling as on fa373fd, 2d9717d and 457aeab.

@github-actions

Copy link
Copy Markdown
Contributor

Human judgment recorded

@CrysisDeu recorded a not-applicable release (class not-applicable) for the ux AI finding on 4f5c089f16e6ff4a83834399f61fa2e5f8a64c72.

agent: owner decision (Zezhen) — the goal glyph and the ring-only on-watch mark were chosen on real-pod screenshots over word badges under his no-extra-labels rule for the Crewmates page; both marks are named images with tooltips and sr-only words. Same ruling as on fa373fd, 2d9717d and 457aeab.

This decision applies only to this commit. A new push requires a new judgment.

@CrysisDeu

Copy link
Copy Markdown
Contributor Author

/ai-review override first-principles 4f5c089 not-applicable: agent: owner decision (Zezhen) — the switcher row takes the roster row's unread mark (a named ok dot, no word) because the PR's goal is one shared row; the roster never carried the word, and this lane passed the same point on 2d9717d and 457aeab. (Re-posted: the first copy's handler run was cancelled by concurrency.)

@github-actions

Copy link
Copy Markdown
Contributor

Human judgment recorded

@CrysisDeu recorded a not-applicable release (class not-applicable) for the first-principles AI finding on 4f5c089f16e6ff4a83834399f61fa2e5f8a64c72.

agent: owner decision (Zezhen) — the switcher row takes the roster row's unread mark (a named ok dot, no word) because the PR's goal is one shared row; the roster never carried the word, and this lane passed the same point on 2d9717d and 457aeab. (Re-posted: the first copy's handler run was cancelled by concurrency.)

This decision applies only to this commit. A new push requires a new judgment.

@github-actions github-actions Bot added readiness: passed Eligible automated validation passed for the current revision and removed readiness: checking Automated validation is still running labels Oct 27, 2026

@iamwhatever iamwhatever left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve at 4f5c089.

The roster rows get has_goal and heartbeat_on from each member's goal record in one thread hop, and an unreadable record costs only that row its facts (members_roster_goals.py#L39). A goal write pushes the existing agents refresh from the writing thread through call_soon_threadsafe (L81). On watch is one predicate, monitor or heartbeat (useMembersPageRoster.ts#L213), and both lists render CrewmateRowBody.

The UX and First Principles BLOCKs on this head are product choices: the goal glyph, the ring-only on-watch mark, and dropping the switcher's Unread word. Each has a reasoned disposition and an override that cites the owner's decision. Each state keeps a spoken and hoverable name. Opus and GPT report no findings, Design passes, and every check on this head is green or skipped. One note for later, not blocking: agents is not a narrow refresh kind, so every goal write re-reads all dashboard caches.

— crew-pr-reviewer (r129)

@iamwhatever
iamwhatever merged commit 5e691ae into main Oct 27, 2026
109 of 111 checks passed
@iamwhatever
iamwhatever deleted the feat/crewmate-row-watch-goal branch October 27, 2026 19:15
@github-actions github-actions Bot removed the readiness: passed Eligible automated validation passed for the current revision label Oct 27, 2026

@buluoray buluoray left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving 4f5c089.

One shared CrewmateRowBody for the roster and the switcher removes the drift cleanly, and each side it picks matches the roster's established shape: the --ok unread dot, the preview with the Stopped chip, and the on-watch ring. No entry in docs/decisions covers these marks, and the spec is rewritten in the same commit.

Backend: has_goal/heartbeat_on cost one thread hop and one capped goal-record read per mate, with no crew-log access (the full-scan guard suite stays green). They ride the existing owner-gated GET /api/members and add only two booleans to the explicit allowlist. An unreadable record degrades to neither fact. The refresh listener registers once and hands its push to the serving loop.

Verified locally on this head: the touched backend tests (272 passed) and the members/i18n vitest files (1185 passed). The same subset also passes merged onto current main, which merges cleanly. Mutations: forcing heartbeat_on false, dropping the goal mark's accessible name, and dropping the avatar's watch OR each turn a named test red.

Non-blocking follow-ups:

  • Removing || !!m.heartbeat_on from isLoopOn (useMembersPageRoster.ts) leaves every members/crew test green, so the headline behaviour (a heartbeat-only mate reads as on watch in the roster ring, the header pill and the Scheduled check-ins filter) is not pinned on the real resolver. The switcher tests only exercise rowOnlySignals.
  • Every goal-record write, including key-result progress and report stamps, now triggers a full roster re-read. Pushing the refresh only when one of the two facts actually changes would keep that cheap as mates write more often.

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.

Crewmates roster: show who is on watch and who has a goal

3 participants