Files
commitea/packages/core/src/index.ts
Croissant Le Doux 1636d6bada feat: capacity-aware scheduling (#8) — real focus factors drive every forecast
Turns the single-serial-worker scheduler into a capacity-aware, multi-lane one.
Configured team members become lanes; an issue runs on its assignee's lane (or the
earliest-free lane), its duration scaled by that lane's throughput
(focusFactor × allocation). Every forecast — Focus cone, Runway, milestone
drill-in — is now capacity-aware.

core (@commitea/core):
- capacity/capacity-v0: CapacityMember + capacityPerWorkday + parseCapacityConfig
  (clamps, drops invalid; degrades to []).
- scheduler/scheduler-capacity-v0: scheduleWithCapacity reuses the v0 topo order +
  critical path, re-lays work across lanes (layoutOnLanes, resolveLanes, makespan).
  Empty workers → the single serial plan verbatim.
- forecast() gains options.workers: each MC trial lays sampled durations across the
  lanes and takes the makespan; serial path unchanged. SchedulableIssue gains
  assignee; ScheduledItem gains worker.
- 11 new tests (parse/clamp, parallelism halves makespan, speed scaling, assignee
  routing, cross-lane deps, forecast makespan shrinks with lanes).

app:
- pm-state capacity/members.json read (readCapacity + pmstate:capacity bridge);
  useCapacity hook → workers; forecastBacklog/runwayView/milestoneView pass workers.
- Runway Capacity card shows the real config (person · focus · alloc · pd/day).

Config lives in pm-state (D4); seeded christian(0.8)/stephen(0.6×0.5). Degrades to
the fixture/serial when absent.

Verified: 128 core tests green, desktop typecheck clean, 14 fixture e2e green. Live:
the capacity card is real, and the P2 forecast shifts 32d→37d — honest, since real
focus factors (<1) replace the v0 focus-1.0 assumption.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 00:59:03 -04:00

121 lines
3.4 KiB
TypeScript

export {
ESTIMATE_LABELS,
PRIORITY_LABELS,
HARD_DEADLINE_LABEL,
extractLabelFacts,
isCommiteaLabel,
parseEstimateLabel,
parsePriorityLabel,
} from './labels/label-schema.js'
export type { EstimateLabel, LabelFacts, PriorityLabel } from './labels/label-schema.js'
export { createGiteaClient, normalizeIssue, normalizeMilestone, normalizeTimeline } from './gitea/client.js'
export type { GiteaClient, ListIssuesOptions } from './gitea/client.js'
export { GiteaApiError } from './gitea/types.js'
export type {
FetchLike,
GiteaConfig,
GiteaHttpResponse,
GiteaIssue,
GiteaLabel,
GiteaMilestone,
GiteaMilestoneRef,
GiteaRequestInit,
} from './gitea/types.js'
export { describeChange, planIssueChange, proposalsFor } from './changes/apply-changes-v0.js'
export type { ChangeProposal, IssueChange, LabelPlan, ProposeChangeArgs } from './changes/apply-changes-v0.js'
export {
inferColumnV0,
inferLifecycle,
LIFECYCLE_COLUMNS,
workingDaysBetween,
} from './lifecycle/lifecycle-v0.js'
export type {
LifecycleColumn,
LifecycleEvent,
LifecycleEventType,
LifecycleInference,
LifecycleStages,
} from './lifecycle/lifecycle-v0.js'
export { DEFAULT_ESTIMATE_DAYS, schedule, selectFocus } from './scheduler/scheduler-v0.js'
export type {
DependencyEdge,
Focus,
SchedulableIssue,
ScheduledItem,
SchedulePlan,
} from './scheduler/scheduler-v0.js'
export { makespan, scheduleWithCapacity } from './scheduler/scheduler-capacity-v0.js'
export type { Worker } from './scheduler/scheduler-capacity-v0.js'
export { capacityPerWorkday, parseCapacityConfig } from './capacity/capacity-v0.js'
export type { CapacityMember } from './capacity/capacity-v0.js'
export {
COLD_START_PRIORS,
durationParams,
forecast,
nearestBucket,
PRIOR_BUCKETS,
priorForEstimate,
} from './forecast/forecast-v0.js'
export type {
BurnUpPoint,
DurationModel,
Forecast,
ForecastOptions,
LognormalPrior,
} from './forecast/forecast-v0.js'
export {
CALIBRATION_BUCKET_FLOOR,
calibrationSamples,
COLD_START_THRESHOLD,
fitCalibration,
toDurationModel,
} from './calibration/calibration-v0.js'
export type {
BucketFit,
CalibrationModel,
CalibrationSample,
PersonBias,
} from './calibration/calibration-v0.js'
export { createChatClient } from './agent/chat-client.js'
export type { ChatClient, ChatMessage, CompletionResult, ModelConfig, OnToken, ToolCall, ToolDecl } from './agent/chat-client.js'
export { pickModel } from './agent/model-router.js'
export type { ModelRouter, TaskKind } from './agent/model-router.js'
export { runAgentTurn } from './agent/agent-loop.js'
export type { AgentStep, AgentTurn, ToolExecutor } from './agent/agent-loop.js'
export {
PROPOSE_CHANGE_TOOL,
QUERY_PROJECT_TOOL,
RECORD_DIRECTIVE_TOOL,
REGINALD_SYSTEM,
REGINALD_TOOLS,
} from './agent/agent-tools.js'
export { buildProjectView } from './agent/query-project.js'
export type { ProjectSnapshot, ProjectView, QueryFilters } from './agent/query-project.js'
export { CAPTURE_SYSTEM, captureWork, parseCaptureArgs, PROPOSE_ISSUES_TOOL } from './agent/capture-work.js'
export type { CaptureProposal, ProposedIssue } from './agent/capture-work.js'
export {
appendDirective,
makeDirectiveEntry,
parseDirectiveLog,
serializeDirective,
toDirectiveInput,
} from './directives/record-directive-v0.js'
export type {
DirectiveEntry,
DirectiveInput,
DirectiveKind,
DirectiveRecord,
DirectiveStatus,
DirectiveTarget,
} from './directives/record-directive-v0.js'