Stacked on #38 → #37. Retarget down the chain as each lands.
What
The board's Steeping and In-review columns were always empty (v0 could only place three columns honestly). This reads the real gitea issue timeline and fills all five — and derives the estimate-vs-actual working time the calibration fit (D3) will learn from.
Core (@commitea/core)
inferLifecycle(issue, events, asOf) — five-column inference: closed → done; open PR ref → review; commit ref → steeping; any triage signal (label/milestone/assign, or a current label) → triage; else diagnosis. Earliest event of each kind fixes the stage timestamp.
Derives actualWorkingDays (work-start → close — the actual calibration learns from) and steepingDays (first commit → now) for the age badge.
workingDaysBetween() — whole Mon–Fri days in [start, end), day-granular.
normalizeTimeline() + client.getIssueTimeline() — map gitea's raw timeline (label/milestone→triage, commit_ref→commit, pull_ref→pull, close, reopen), drop the rest. Paginated.
App
reconcile fetches every issue's timeline, returns it keyed by number; threaded through the bridge → useBacklog → board/focus.
issuesToBoardColumns + scheduleFocus run inferLifecycle: real Steeping/In-review columns, steeping-age days badge, focus-card steeping badge.
Live result
Against the real repo: Diagnosis 0 · Triage 17 · Steeping 0 · In-review 8 · Done 9. Done holds merged work; In-review holds issues with open PRs (#9 scheduler, #10 Monte Carlo — the live PRs).
Known refinements (later)
gitea's pull_ref fires on any PR mention, so an issue merely referenced in a PR body can read as In-review; distinguishing a closing ref from a mention needs the PR link's state.
63 core tests green (15 lifecycle: workingDaysBetween + the five transitions + earliest-event + fallback) · desktop typecheck clean · 14 fixture e2e green · live spec asserts Done is populated from real events.
Stacked on #38 → #37. Retarget down the chain as each lands.
## What
The board's **Steeping** and **In-review** columns were always empty (v0 could only place three columns honestly). This reads the real **gitea issue timeline** and fills all five — and derives the estimate-vs-actual working time the calibration fit (D3) will learn from.
## Core (`@commitea/core`)
- **`inferLifecycle(issue, events, asOf)`** — five-column inference: closed → *done*; open PR ref → *review*; commit ref → *steeping*; any triage signal (label/milestone/assign, or a current label) → *triage*; else *diagnosis*. Earliest event of each kind fixes the stage timestamp.
- Derives **`actualWorkingDays`** (work-start → close — the actual calibration learns from) and **`steepingDays`** (first commit → now) for the age badge.
- **`workingDaysBetween()`** — whole Mon–Fri days in [start, end), day-granular.
- **`normalizeTimeline()`** + **`client.getIssueTimeline()`** — map gitea's raw timeline (label/milestone→triage, commit_ref→commit, pull_ref→pull, close, reopen), drop the rest. Paginated.
## App
- `reconcile` fetches every issue's timeline, returns it keyed by number; threaded through the bridge → `useBacklog` → board/focus.
- `issuesToBoardColumns` + `scheduleFocus` run `inferLifecycle`: real Steeping/In-review columns, steeping-age `days` badge, focus-card steeping badge.
## Live result
Against the real repo: **Diagnosis 0 · Triage 17 · Steeping 0 · In-review 8 · Done 9**. Done holds merged work; In-review holds issues with open PRs (#9 scheduler, #10 Monte Carlo — the live PRs).
## Known refinements (later)
- gitea's `pull_ref` fires on **any** PR mention, so an issue merely referenced in a PR body can read as In-review; distinguishing a *closing* ref from a mention needs the PR link's state.
- Re-opening → multi-segment actuals deferred (v0 takes earliest work-start → close).
## Verified
63 core tests green (15 lifecycle: `workingDaysBetween` + the five transitions + earliest-event + fallback) · desktop typecheck clean · 14 fixture e2e green · live spec asserts Done is populated from real events.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
christian
added 1 commit 2026-07-08 23:32:24 +00:00
Fill the board's Steeping / In-review columns (and the calibration actuals)
from real gitea timeline events, replacing the three-column-only v0.
core (@commitea/core):
- inferLifecycle(issue, events, asOf): five-column inference — closed → done;
open PR ref → review; commit ref → steeping; any triage signal → triage;
else diagnosis. Earliest event of each kind fixes the stage timestamp.
- Derives actualWorkingDays (work-start → close) — the estimate-vs-actual the
calibration fit (D3) learns from — and steepingDays (first commit → now) for
the board age badge.
- workingDaysBetween(): whole Mon–Fri days in [start, end), day-granular.
- normalizeTimeline() + client.getIssueTimeline(): map gitea's raw timeline
(label/milestone → triage, commit_ref → commit, pull_ref → pull, close,
reopen), drop the rest. Paginated.
app:
- reconcile now fetches every issue's timeline and returns it keyed by number;
threaded through the bridge → useBacklog → board/focus.
- issuesToBoardColumns + scheduleFocus run inferLifecycle: real Steeping/In-review
columns, steeping-age `days` badge, focus-card steeping badge.
Known refinement: gitea's pull_ref fires on any PR mention, so an issue merely
referenced in a PR body can read as In-review; distinguishing closing refs from
mentions needs the PR link's state (later). Re-opening multi-segment actuals
also deferred.
Verified: 63 core tests green (15 lifecycle, incl. workingDaysBetween + the five
transitions), desktop typecheck clean, 14 fixture e2e green, live spec asserts
the board's Done column is populated from real events.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
christian
added 1 commit 2026-07-08 23:35:37 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Stacked on #38 → #37. Retarget down the chain as each lands.
What
The board's Steeping and In-review columns were always empty (v0 could only place three columns honestly). This reads the real gitea issue timeline and fills all five — and derives the estimate-vs-actual working time the calibration fit (D3) will learn from.
Core (
@commitea/core)inferLifecycle(issue, events, asOf)— five-column inference: closed → done; open PR ref → review; commit ref → steeping; any triage signal (label/milestone/assign, or a current label) → triage; else diagnosis. Earliest event of each kind fixes the stage timestamp.actualWorkingDays(work-start → close — the actual calibration learns from) andsteepingDays(first commit → now) for the age badge.workingDaysBetween()— whole Mon–Fri days in [start, end), day-granular.normalizeTimeline()+client.getIssueTimeline()— map gitea's raw timeline (label/milestone→triage, commit_ref→commit, pull_ref→pull, close, reopen), drop the rest. Paginated.App
reconcilefetches every issue's timeline, returns it keyed by number; threaded through the bridge →useBacklog→ board/focus.issuesToBoardColumns+scheduleFocusruninferLifecycle: real Steeping/In-review columns, steeping-agedaysbadge, focus-card steeping badge.Live result
Against the real repo: Diagnosis 0 · Triage 17 · Steeping 0 · In-review 8 · Done 9. Done holds merged work; In-review holds issues with open PRs (#9 scheduler, #10 Monte Carlo — the live PRs).
Known refinements (later)
pull_reffires on any PR mention, so an issue merely referenced in a PR body can read as In-review; distinguishing a closing ref from a mention needs the PR link's state.Verified
63 core tests green (15 lifecycle:
workingDaysBetween+ the five transitions + earliest-event + fallback) · desktop typecheck clean · 14 fixture e2e green · live spec asserts Done is populated from real events.🤖 Generated with Claude Code