Skip to Content

← All archived runs

Run: customer-activity-feed

run.md

Run: customer-activity-feed

  • branch: claude/optimistic-wright-l5ms86
  • pr: #500

00_intake/stub.md

Stub: Customer dashboard — activity feed

  • feature-slug: customer-activity-feed
  • epic: customer-dashboard-data
  • personas: Customer
  • initiative: Build the Bridge / objective: Q2 2026 Objective 3 — Validate Technical Infrastructure & Payout Flow
  • depends-on: customer-project-foundation
  • sequence: 6 of 9

Problem

The Value section's activity block (latestSummary, latestTime, entries[{event, detail, time, type, impactTag}]) and its "View all activity" link to /customer/activity are dummy. A real per-lead audit log already exists — the activity model (packages/services/src/db/models/activity.ts) records changeType, message, performedBy, status transitions per leadId — but it is not mapped into the customer-facing shape, and the /customer/activity page does not exist.

Proposed change

  • Build a read that pulls the active project's Activity entries and maps them to the customer view: event, detail, time, type (start | delivery | update | …), impactTag (e.g. no-impact).
  • Decide the customer-facing filter — which audit entries a customer should see (delivery and status-change events, not internal field edits) — and the type / impactTag derivation from changeType + action.
  • Wire the activity block (latest summary + recent entries) in ValueSection, and build the /customer/activity full-feed page the "View all activity" link targets.

Acceptance criteria (rough)

  • The activity block shows real recent project events, newest first, with derived type + impact.
  • Internal-only audit entries are filtered out of the customer view.
  • /customer/activity renders the full, paginated feed for the active project.
  • Empty state renders cleanly when a project has no customer-visible activity yet.

Out of scope (this feature)

  • Writing new activity entries — this consumes the existing audit log only.
  • Real-time push of activity (the messaging thread covers live updates).

Notes for Define

  • The mapping from changeType → customer type/impactTag is the design crux; agree the visible event set and tag rules with the user.
  • touches: packages/services/src/db/models/activity.ts (read/map), packages/services/src/server, apps/web/components/dashboard/customer/value-section.tsx, apps/web/app/(app)/customer/activity/ (new page).

01_define/output/spec.md

Spec: Customer activity feed

  • slug: customer-activity-feed
  • personas: Customer
  • touches: packages/services/src/server, packages/services/src/db/models/activity.ts (read/map), apps/web/components/dashboard/customer/value-section.tsx, apps/web/app/(app)/customer/dashboard/page.tsx (wire), apps/web/app/(app)/customer/activity/ (new page)
  • complexity: standard

Problem

The customer dashboard's Value section shows a "Latest update" activity block (latestSummary, latestTime, entries[{ event, detail, time, type, impactTag }]) and a "View all activity" link to /customer/activity — but both are dummy, fed from apps/web/lib/mock/customer.ts. A real per-lead audit log already exists: the Activity model records changeType, message, performedBy, oldStatus/newStatus and timestamps per leadId. It is simply never mapped into the customer-facing shape, and /customer/activity does not exist. This is feature 6 of the customer-dashboard-data epic (Build the Bridge / Q2 2026 Objective 3 — Validate Technical Infrastructure & Payout Flow): replacing the last mocked sections of the customer delivery window with real, tenant- and project-scoped data so the customer can trust what they see.

Proposed change

  • Add a server read (in @sustentus/services/server) that pulls the signed-in customer's active project's Activity entries and maps them into the customer view shape (event, detail, time, type, impactTag), newest first.
  • Visible-event filter (decided): include only STATUS_CHANGE and CREATE entries — status transitions and creation events (project/milestone start, status reached). Exclude UPDATE, DELETE, ASSIGNMENT, COMMENT, OTHER (internal field edits and chatter never reach the customer view).
  • type derivation (decided default): CREATEstart; STATUS_CHANGEdelivery when the new status denotes a completion/delivery/go-live milestone, otherwise update. These three map to the icons the ValueSection already renders (start/delivery/update, fallback update).
  • impactTag derivation (decided): always no-impact for this feature. The audit log carries no impact signal; go-live impact is the health-engine's job (a separate epic feature). The field is kept so the badge renders, but it is constant for now.
  • Build latestSummary + latestTime from the most recent customer-visible entry.
  • Wire the activity block in ValueSection (via the dashboard page's server fetch, replacing d.activity) and build the /customer/activity full-feed page the "View all activity" link targets, paginated, scoped to the active project.

Acceptance criteria

  • The Value section activity block shows real STATUS_CHANGE + CREATE events for the customer's active project, newest first, with derived type and impactTag of "No impact".
  • UPDATE, DELETE, ASSIGNMENT, COMMENT and OTHER audit entries are excluded from the customer view.
  • latestSummary and latestTime reflect the most recent customer-visible event (no mock).
  • /customer/activity renders the full, paginated feed for the active project.
  • The empty state renders cleanly when the active project has no customer-visible activity yet.
  • Activity is tenant- and project-scoped: a customer only ever sees their own active project's entries (reuses the foundation's active-project resolution).

Out of scope

  • Writing new Activity entries — this consumes the existing audit log only.
  • Real-time push of activity (the project-messaging thread covers live updates).
  • Impact tagging beyond constant no-impact — real go-live impact is the health-engine feature.
  • An action-type "customer-visible" allowlist flag — the changeType filter is sufficient this round.
  • Multi-project switching — the single active project only, per the epic.

Open questions

  • none

02_build/output/notes.md

Build notes: customer-activity-feed

  • commits: feat(customer-activity-feed): customer activity feed from audit log

What changed

  • packages/services/src/db/services/activity/index.ts: added the customer-facing mapping (CustomerActivityEntry/CustomerActivityType), the visible-event filter (STATUS_CHANGE + CREATE only), the type/impactTag derivation, and a paginated findCustomerVisibleByLead(tenantId, leadId, opts) read that filters at the query level so totals stay accurate.
  • packages/services/src/db/services/customer-project/index.ts: added CustomerActivityFeed and getCustomerActivity(tenantId, customerUserId, opts) — resolves the signed-in customer's active project lead (tenant + customer scoped), delegates to the activity read, and derives latestSummary/latestTime from the newest entry. Returns null when there's no active project.
  • packages/services/src/db/services/index.ts: exported the new types.
  • apps/web/lib/dashboard-utils.ts: added formatRelativeTime (ISO → "2 hours ago" … short date).
  • apps/web/app/(app)/customer/dashboard/page.tsx: resolves project + activity together, maps the feed into the ValueSection shape (replacing d.activity mock), formatting times for display.
  • apps/web/app/(app)/customer/activity/page.tsx: new full, paginated feed page the "View all activity" link targets, with empty state and prev/next pagination.

Acceptance criteria status

  • The Value section activity block shows real STATUS_CHANGE + CREATE events for the customer's active project, newest first, with derived type and impactTag of "No impact" — getCustomerActivity (limit 3) feeds ValueSection via toActivityView.
  • UPDATE, DELETE, ASSIGNMENT, COMMENT and OTHER entries are excluded — filtered in findCustomerVisibleByLead via changeType: { $in: ["CREATE","STATUS_CHANGE"] }.
  • latestSummary/latestTime reflect the most recent customer-visible event — derived from the newest entry in getCustomerActivity.
  • /customer/activity renders the full, paginated feed for the active project — new page, 25/page.
  • Empty state renders cleanly when the project has no customer-visible activity — both the dashboard block placeholder and the activity page's empty card.
  • Tenant- and project-scoped — the active-project lookup is keyed on tenantId + customer + isActiveProject, reusing the foundation's scoping; activity reads are tenant + leadId scoped.

Verify result

  • mechanical checks (format · lint · typecheck · build) run in CI + the Vercel preview, not here. No check expected to fail.

Notes for review

  • impactTag is intentionally constant no-impact (per the approved spec) — real go-live impact is the health-engine feature, not this one.
  • type derivation: CREATEstart; STATUS_CHANGEdelivery when the new status/action name matches a completion/go-live hint (deliver|complete|done|live|launch|closed|paid), else update.
  • Time formatting lives in the web layer (formatRelativeTime); the service returns ISO strings so the data layer stays presentation-free.

03_release/output/changelog.md

Changelog: customer-activity-feed

Live entry: apps/help/app/changelog/2026-06-22-customer-activity-feed/page.mdx


See real activity on your project

You can now see what is happening on your project as it happens. Your dashboard shows the latest updates — status changes and milestones reached — and a new activity page gives you the full history, newest first. Internal working notes stay private; you only see the events that matter to you.

03_release/output/investor-update.md

Customers can now track their project's real activity

Who it's for: Customers What shipped: The customer dashboard's activity feed now shows real status and milestone events from the project audit log, plus a full activity page. Why it matters: Another customer dashboard section runs on live data, advancing Build the Bridge / Q2 objective 3, Validate Technical Infrastructure & Payout Flow.

Dig deeper: <merged-PR URL> · <changelog entry URL>