Skip to content

feat(aggregator): honesty + freshness — no silent zeros, primed health, per-source chips (#1139, #1267, #1138) - #1318

Merged
dezren39 merged 1 commit into
mainfrom
pm/honesty-freshness
Oct 27, 2026
Merged

dezren39 merged 1 commit into
mainfrom
pm/honesty-freshness

Conversation

@dezren39

@dezren39 dezren39 commented Oct 27, 2026 •

Copy link
Copy Markdown
Member

Honesty + freshness slice (epic #1184)

One PR closing the three honesty issues from the PM-stack coherence review, plus the aggregator half of #1148's dead-man's switch.

#1139 — no silent zeros

  • New diagnosis primitive: diagnoseOrchestrationSection() in src/feed.ts classifies the raw section value as live / stale (>90 min, the design's threshold) / absent / invalid (invalid names the first failing validation check). Source.diagnostics() carries it from both sources; the live source recomputes it on every orchestration() read.
  • /attention: an absent tier source renders Tier source missing — showing session-derived items only; the orchestrator's published queues are NOT reflected in the counts below.; invalid names the failed checks (Tier source invalid — … failed validation (provenance); …); stale names the run and its age. When the source is absent/invalid the tier headings and chips read "Needs user (unknown)" — never a confident 0 — and the "nothing needs you" empty copy is suppressed (it was the false-reassurance failure mode). When present, tier headings carry a section stamp (· section <runId> · <age>).
  • /today: the derived-view banner names the section's true state — invalid renders "present but failed validation (tiers.needsDrew)…" instead of the old blanket "has not published yet"; a stale-but-served section gets a stale warning with run + age.
  • This banner is impl(fleet): staleness watchdog - surface dead/stale stack inputs as NEEDS ORCHESTRATOR instead of silent decay #1148's aggregator-side dead-man's switch: if the orchestration job dies, the surfaces say so within one staleness threshold. The pass-side watchdog checks remain impl(fleet): staleness watchdog - surface dead/stale stack inputs as NEEDS ORCHESTRATOR instead of silent decay #1148's remainder (noted there, not closed).

#1267 — /api/health must not lie by request order

Provenance facets populate lazily, one per method call, so health used to report fixture defaults for facets nobody had exercised yet. /api/health now primes every facet first (60 s-cached, failure-tolerant — a failed facet keeps its honest fallback string) and returns the structured sections diagnosis beside the provenance strings. Regression test (test/health-priming.test.ts) boots a live-source server against a controlled feed and asserts on the first response the process ever serves: quota/orchestration/registry facets report live, registry counts 1 session records, sections.orchestration.state === "live". The live items() facet also reports pool-backed truthfully on an empty-refs prime call.

#1138 — per-source freshness chips

  • Source.provenanceDetail() returns structured per-facet detail (text, state, ageMs, count, countLabel); state derives from the facet's own maintained string plus the orchestration diagnosis and feed age (a "live" string over a >15 min-old feed renders stale, impl(fleet): staleness watchdog - surface dead/stale stack inputs as NEEDS ORCHESTRATOR instead of silent decay #1148's threshold).
  • Pages render chips for exactly the sources they use — board (board/items/orchestration), roadmap (roadmap/items), quota + service detail (quota/usage), attention (orchestration/attention/registry), today (orchestration/quota/attention), dns (dns), sessions (registry), projects (items), program hub + project pages (orchestration/items/roadmap). The footer's ten-string line moves into a collapsed <details> "source detail" disclosure; the tailnet link stays visible.
  • Counts always travel with their noun, and the registry facet now counts the merged store total — live (221 session records · 61 from the latest feed · …) — the same number the /sessions header renders, ending the unexplained 221-vs-61 split.

Role-based wording (Drew's direction, 2026-10-27)

All user-visible "Needs Drew" copy becomes "Needs user" across the aggregator — /attention headings, chips and intro, the session tier badge, the /today alert, the page-manifest blurb, the /program editorial record, project-config prose, and the README. This is Drew's explicit direction, extending the fleet-wide 2026-10-25 rename of Drew-named labels to role-based ones: the NEEDS USER queue is an intake the orchestrator works first — it resolves what it can and escalates only what genuinely requires the user (user/escalated) — so the copy no longer implies every queued item is waiting on the user personally. Internal field names, JSON keys, and the orchestration contract (needsDrew, drewReason, …) are unchanged; contract naming is #1290's open decision.

Verification

  • bun test: 181 pass / 0 fail (baseline 167; +14 new: diagnosis states, attention banner states, today invalid banner, chips incl. per-page selection, live-source diagnostics/detail, health priming).
  • bunx tsc --noEmit: clean. Formatted with schema-pinned biome 2.3.2 + prettier.

Closes #1139, closes #1267, closes #1138. Refs #1148 (aggregator half), #1184.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

…h, per-source chips (#1139, #1267, #1138)

#1139 — the orchestration section is diagnosed on every read
(diagnoseOrchestrationSection: live/stale>90m/absent/invalid with the
failing check named) and Source.diagnostics() carries it to the
surfaces. /attention banners an absent/invalid/stale tier source at
the point of display, renders tier counts as 'unknown' (never a
confident 0) when the source is missing, stamps tier headings with
the section run id + age when present, and suppresses the
false-reassurance empty copy. /today's derived banner names the
section's true state (invalid names the failed checks); a stale
section behind the served view warns. This banner is #1148's
aggregator-side dead-man's switch.

#1267 — /api/health primes every provenance facet (60s-cached,
failure-tolerant) before answering, so its report no longer depends
on request order; the response also carries the structured sections
diagnosis. Regression test boots a live-source server and asserts on
the FIRST response it ever serves. The live items() facet also
reports pool-backed truthfully on an empty-refs prime call.

#1138 — per-source freshness chips (source · state · age; counts
always with their noun) on the pages that use each source, from
Source.provenanceDetail(); the footer provenance line moves into a
collapsed 'source detail' disclosure. The registry facet now counts
the MERGED store total (the number /sessions renders) instead of
the per-read incoming batch, ending the 221-vs-61 disagreement.

Role-based wording (Drew, 2026-10-27): all user-visible 'Needs Drew'
copy becomes 'Needs user' (headings, chips, session badge, /today
alert, page blurb, program editorial, README) — the NEEDS USER queue
is an intake the orchestrator works first; only items it cannot
resolve escalate to the user. Internal field names, JSON keys, and
the orchestration contract (needsDrew etc.) are unchanged — contract
naming is #1290's decision.

Tests: 181 pass (baseline 167), tsc clean, biome 2.3.2 + prettier.
@dezren39
dezren39 requested a review from a team as a code owner October 27, 2026 06:22
@coderabbitai

coderabbitai Bot commented Oct 27, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 4 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 6edfb73f-ab02-484f-83bb-74ca791bc4b6
Commits

Reviewing files that changed from the base of the PR and between 621b8fe and fc571cf.

Files selected for processing: 17
  • pm-stack/aggregator/README.md
  • pm-stack/aggregator/src/derive.ts
  • pm-stack/aggregator/src/feed.ts
  • pm-stack/aggregator/src/model.ts
  • pm-stack/aggregator/src/pages.ts
  • pm-stack/aggregator/src/program.ts
  • pm-stack/aggregator/src/project-config.ts
  • pm-stack/aggregator/src/render.ts
  • pm-stack/aggregator/src/server.ts
  • pm-stack/aggregator/src/source.ts
  • pm-stack/aggregator/src/sources/fixtures.ts
  • pm-stack/aggregator/src/sources/live.ts
  • pm-stack/aggregator/test/boot.test.ts
  • pm-stack/aggregator/test/health-priming.test.ts
  • pm-stack/aggregator/test/orchestration.test.ts
  • pm-stack/aggregator/test/render.test.ts
  • pm-stack/aggregator/test/slice2.test.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-27T06:27:18.334625Z fc571cf PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fc571cff35

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if (path === "/attention") {
const { view, coverage } = await attentionContext();
return html(renderAttention(view, coverage, prov(), await commentsStore.counts()));
return html(renderAttention(view, coverage, prov(), await commentsStore.counts(), provDetail()));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Prime displayed facets before rendering freshness chips

On a fresh live-source process, /attention renders a registry freshness chip without calling source.registry(): attentionContext() only reads attention, orchestration, and coverage. With a valid registry in the feed, I reproduced registry · fixtures on the first /attention request, changing to registry · live · 1 session records · fresh after visiting /sessions. The board's orchestration chip and projects page's items chip have the same unprimed-facet problem. Exercise the displayed facets or maintain their metadata when their underlying data is read, so these new indicators do not depend on which unrelated pages were visited first.

Useful? React with 👍 / 👎.

: "");
} else if (projectorServed) {
provenance.items = "live (projector)";
} else if (pool.length > 0 && feed) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Limit pool-only provenance to empty-reference priming

When the feed contains a nonempty project pool but none of the requested refs match it, and the projector is disabled or unavailable, items() returns fixture items through fix.items(remaining). This new branch nevertheless labels the result live (project pool…), so the freshness chip presents hand-written fixture issue/PR states as live data. The branch also matches nonempty unresolved requests, not just the empty-reference priming case described in its comment. Restrict that special case to empty refs and report fixture provenance when all requested items came from the fallback.

Useful? React with 👍 / 👎.

This was referenced Oct 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment