feat: deterministic scheduler → real Now/Next/Later (P2 thin slice)

CommiTea now recommends its own next unit of work from the live backlog.

- @commitea/core: `schedule()` — dependency topo-sort with priority +
  estimate tie-breaks, single serial capacity, cycle detection, and
  critical-path marking; `selectFocus()` takes the top three. Pure,
  deterministic; the LLM does none of this. +11 tests (39 in core).
  Client gains `getIssueDependencies`.
- main: reconcile also fetches native issue dependencies for the open
  scope and returns edges.
- renderer: `scheduleFocus()` maps real issues+deps→Now/Next/Later;
  Focus renders scheduler output (fixture fallback when unconfigured).

v0 scope (each a later slice): single serial worker (per-person
capacity #8), point durations (Monte Carlo cone #10), estimate-only
(calibration #5). Verified: 14 e2e green (fixtures) + gated live spec —
the board shows the real 25 open + 9 closed, and Focus picks #2
ChangeSource (critical path) as Now. Screenshots confirmed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Croissant Le Doux
2026-07-08 17:12:22 -04:00
parent 94199639f2
commit 68a93de098
11 changed files with 387 additions and 26 deletions

View File

@@ -66,6 +66,8 @@ export interface GiteaClient {
listIssues(opts?: ListIssuesOptions): Promise<GiteaIssue[]>
/** Fetch every milestone (all pages). */
listMilestones(): Promise<GiteaMilestone[]>
/** The issue indices this issue depends on (its blockers). */
getIssueDependencies(index: number): Promise<number[]>
}
/** Map raw gitea issue JSON to the normalized domain shape. Pure. */
@@ -156,5 +158,10 @@ export function createGiteaClient(config: GiteaConfig, fetchImpl: FetchLike): Gi
)
return raw.map(normalizeMilestone)
},
async getIssueDependencies(index) {
const raw = (await request(`/issues/${index}/dependencies`)) as { number: number }[]
return raw.map((d) => d.number)
},
}
}