Skip to content

fix(server): emit membership reconciliation summary from job path#130

Merged
Sootopolis merged 1 commit into
mainfrom
fix/129-membership-server-report
Jun 25, 2026
Merged

fix(server): emit membership reconciliation summary from job path#130
Sootopolis merged 1 commit into
mainfrom
fix/129-membership-server-report

Conversation

@Sootopolis

Copy link
Copy Markdown
Owner

What

Server-submitted membership jobs called reconcile() but never reported, so the per-job log (logs/jobs/<id>.log) was empty and the CLI's follow showed nothing after <slug> submitted: <id> — a silent success even though the DB reconciled correctly.

  • Add MembershipApp.reconcileAndReport, shared by the CLI ReconcileOnly path and the JobRoutes membership handler, so the reconciliation summary is logged (and streams to the CLI) on both paths. Sibling jobs (Ref/History/Stats) already log inline; membership was the lone silent one.
  • Thread clubId through ReconciliationResult so the post-reconcile invitation lookup uses the resolved identity instead of re-selecting by the input slug — which 404'd a club whose slug reconcile recovered through a rename (latent CLI bug, would have newly hit the server path too).
  • Invitation lookup is best-effort: a DB error there logs a warning and falls back to no annotations rather than failing an already-committed reconcile.
  • .gitignore logs/ — per-job server logs surfaced while testing this (runtime artifacts, never tracked).

Decision: server logs the summary only (no out/ file), matching every other server job.

Fixes

Closes #129

Testing

  • sbt compile clean.
  • Full sbt test via pre-push hook: 953 passed, 0 failed.
  • Targeted: membership Pure/Integration/NetworkOutage + TestRoutes + TestJobFollower (94) green before and after the clubId threading.

🤖 Generated with Claude Code

https://claude.ai/code/session_01ETHm6wCYdzFuB3kNEBayjF

Server-submitted membership jobs called reconcile() but never reported,
so the per-job log was empty and the CLI follow showed nothing after
"submitted:" — a silent success. Add MembershipApp.reconcileAndReport,
shared by the CLI ReconcileOnly path and the JobRoutes membership
handler, so the summary is logged (and streams to the CLI) on both.

Thread clubId through ReconciliationResult so the post-reconcile
invitation lookup uses the resolved identity instead of re-selecting by
the input slug, which 404'd a club whose slug reconcile recovered through
a rename (latent CLI bug, now also on the server path). The invitation
lookup is best-effort: a DB error there logs a warning and falls back to
no annotations rather than failing an already-committed reconcile.

Also gitignore logs/ (per-job server logs surfaced while testing this).

Closes #129

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ETHm6wCYdzFuB3kNEBayjF
@Sootopolis
Sootopolis merged commit b5193ac into main Jun 25, 2026
1 check passed
@Sootopolis
Sootopolis deleted the fix/129-membership-server-report branch June 25, 2026 23:44
Sootopolis added a commit that referenced this pull request Jun 26, 2026
JobRunner.submit forks each job with `.forkIn(layerScope)`, so the job
inherits `currentLoggers` from the submitting fiber. zio-http request
handlers (every `ccas <cmd>` CLI call) run outside ProgressDisplay's
`withLoggerScoped` tree, carrying only the default logger — so the job
logged via the default logger and `asZLogger` never wrote the per-job
FileSink. The file stayed empty, so the CLI's log follow showed nothing
after "<slug> submitted: <id>". Scheduler-submitted jobs were fine (their
fiber is inside the scope), which is why this only surfaced for membership
after #130 made it actually log.

Add ProgressDisplay.installLogger (removeDefaultLoggers +
withLoggerScoped(asZLogger)) and wrap every job with it in
JobRunner.submit, so a job logs through asZLogger into its per-job file
regardless of which fiber submitted it. HTTP- and scheduler-submitted jobs
now log identically.

TestJobRunner's shared layer switches from ProgressDisplay.live to
ProgressDisplay.make (no ambient logger), reproducing the HTTP
default-logger context; its three per-job-log assertions fail without the
fix and pass with it.

Closes #132


Claude-Session: https://claude.ai/code/session_01ETHm6wCYdzFuB3kNEBayjF

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sootopolis added a commit that referenced this pull request Jun 27, 2026
…+ stats

Server- and scheduler-submitted jobs called the core work fn and discarded the
result, so the completion summary — which the CLI follows via the per-job log —
only ever fired on the standalone-CLI path. Same silent-tail class as membership
#130; this closes it for the remaining apps.

- History: move logSummary into discoverClub's Phase 4 success path (shared by
  CLI, HTTP, and scheduler) instead of the CLI-only outputResult; the out/
  report file stays CLI-only. discover/discoverClub have no internal callers, so
  no double-log, and finalizeInterrupted's summary stays mutually exclusive.
- Stats: add memberStatsAndReport / playerOfPeriodAndReport wrappers (the pure
  core fns stay log-free); wire them into JobRoutes, JobScheduler, and the CLI.
  Period eligibility threshold (minGames) stays a wrapper arg; the HTTP path
  passes 1, mirroring the CLI default.
- Scheduler membership: point at the existing reconcileAndReport (#130 only
  patched the HTTP route, leaving scheduled membership runs silent).

Audited recruitment / matchref / clubdata — they already log their summary
inside the core fn, no change needed. A Phase-4 interrupt-safety gap surfaced
during review is filed separately as #137.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sootopolis added a commit that referenced this pull request Jun 27, 2026
…+ stats (#138)

Server- and scheduler-submitted jobs called the core work fn and discarded the
result, so the completion summary — which the CLI follows via the per-job log —
only ever fired on the standalone-CLI path. Same silent-tail class as membership
#130; this closes it for the remaining apps.

- History: move logSummary into discoverClub's Phase 4 success path (shared by
  CLI, HTTP, and scheduler) instead of the CLI-only outputResult; the out/
  report file stays CLI-only. discover/discoverClub have no internal callers, so
  no double-log, and finalizeInterrupted's summary stays mutually exclusive.
- Stats: add memberStatsAndReport / playerOfPeriodAndReport wrappers (the pure
  core fns stay log-free); wire them into JobRoutes, JobScheduler, and the CLI.
  Period eligibility threshold (minGames) stays a wrapper arg; the HTTP path
  passes 1, mirroring the CLI default.
- Scheduler membership: point at the existing reconcileAndReport (#130 only
  patched the HTTP route, leaving scheduled membership runs silent).

Audited recruitment / matchref / clubdata — they already log their summary
inside the core fn, no change needed. A Phase-4 interrupt-safety gap surfaced
during review is filed separately as #137.

Co-authored-by: Claude Opus 4.8 (1M context) <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.

Membership server jobs produce empty job logs (no streamed/out report) — reportReconciliation only runs on CLI path

1 participant