@open-agent-toolkit/control-plane
v0.3.22
Published
Read-only OAT control-plane library for structured project state
Downloads
6,069
Readme
@open-agent-toolkit/control-plane
Read-only OAT control-plane library for parsing project artifacts into structured state.
Purpose
@open-agent-toolkit/control-plane is the typed "read" layer behind OAT project inspection.
It is responsible for:
- parsing OAT project artifacts from disk
- aggregating task progress, artifact status, and review state
- recommending the next workflow skill from parsed project state
- returning stable typed objects for CLI and future UI consumers
The package is intentionally pure and read-only. It has no CLI, UI, or server dependencies beyond Node.js filesystem/path builtins and yaml for frontmatter parsing.
Public API
import {
evaluateQuickPlanReadiness,
getProjectState,
isBriefMode,
listProjects,
parsePhaseMarkers,
parseProvisionalPhaseIds,
parseTaskProgress,
quickPlanNotReadyReason,
recommendSkill,
selectPhaseUnits,
WORKFLOW_MODES,
type JournalPhaseStatus,
type PhaseMarkers,
type TaskProgressOptions,
} from '@open-agent-toolkit/control-plane';WORKFLOW_MODES is the ordered runtime list of supported modes:
spec-driven, quick, import, lite, brief, and goal. The exported
WorkflowMode type is derived from this constant. isBriefMode(mode) reports
whether a mode belongs to the brief family (BRIEF_MODES: brief and
goal), whose plans count progress in phase-units rather than tasks. goal
is the brief-family mode that delivers in milestones.
The phase-unit parser surface is public so plan validation and promotion share
one marker grammar: parseTaskProgress(plan, journal, { phaseUnits }) counts
plan tasks by default and phase-units when phaseUnits is true;
parsePhaseMarkers(journal) reads the strict **Status:**, **Base:**,
**Head:** and **Extracted to:** journal markers (PhaseMarkers,
JournalPhaseStatus); parseProvisionalPhaseIds(plan) returns phases marked
**Provisional:** yes; and selectPhaseUnits(progress, scope) returns the
phases in an active scope in plan document order (null selects every phase).
Goal projects pass extractedUnits: true so a journal phase marked
**Status:** extracted with **Extracted to:** <slug> reads as an extracted
unit (extracted: { to }, counted zero).
Goal mode adds two shared readers. readMilestoneMembership(plan, journal) is
the one reader of the brief's canonical ## Milestones table
(MILESTONES_TABLE_HEADER); it returns MilestoneMembership with every
malformed table, unknown or duplicate phase, duplicate or empty milestone and
unowned phase as a MilestoneMembershipError, and milestonePhaseScope
yields a milestone's ordered phase set only when membership has no errors.
validateGoalOutcomeAudit(audit, fullPhaseInventory, outcomes) is the single
terminal-audit predicate: it enumerates every brief phase (including extracted
and revision phases) and every numbered outcome (parseBriefOutcomes(plan))
and returns a GoalOutcomeAuditValidation. For goal projects ProjectState
also carries milestone, milestoneStatus, goalStatus, goalOutcomeAudit
with goalOutcomeAuditValidation, goalRecovery, deliveries,
milestoneGates, milestoneMembership and planParallelGroups; every other
mode omits them. recommendSkill routes goal projects through a goal priority
block (terminal declaration, review feedback and recovery records, membership,
then the milestone status table) that reads only this persisted state.
getProjectState(projectPath)
Reads one OAT project directory and returns a full ProjectState snapshot, including:
- phase and lifecycle status
- task progress and current task
- artifact and review status
- blocker and HiLL metadata
- PR/docs timestamps and recommendation output
ProjectState.blockers uses the exported ProjectBlocker union: a legacy
reason string or { task_id: string, reason: string, since: string }. Structured
records retain their literal fields in parsed state and JSON. Empty lists stay
empty; mixed lists retain entries in order. Records missing a nonempty string
field and other malformed entries become diagnostic legacy strings:
Malformed blocker entry: <JSON>. They remain visible to blocker checks and
human output without [object Object] coercion. Legacy string normalization
remains unchanged.
Review ledger entries in ProjectState.reviews expose the stable five-column
event identity (scope, type, status, date, and artifact) plus optional
provenance:
reviewedHead: validated full 40-character commit SHA at the head of the reviewed rangeinvocation: invocation kind recorded by the review writergateTarget: configured target for gate-originated review lineage
Legacy five-column review rows remain supported. Missing, empty, placeholder, or invalid provenance is omitted from the parsed object.
listProjects(projectsRoot)
Reads all projects under a configured projects root and returns lightweight ProjectSummary records suitable for list or dashboard surfaces.
ProjectSummary.scope is an additive optional field (shared, local, or
synced). The CLI's cross-scope list surface returns ProjectListRow[], a
discriminated union:
materializedrows carry normal lifecycle state.recorded-absentandremoterows usenulllifecycle fields and an explicit pull recommendation instead of inventing project state.recorded-terminalrows represent legacy or authoritative completion. They carrylifecycle: 'complete', aterminalState, a nullablearchiveSnapshot, and no pull recommendation; their checkout may still be present when a completed ref supersedes stale local state.terminal-invalidrows represent differing active and completed ref SHAs. They carry the conflicting refs and SHAs, usecheckout: 'invalid', and return a repair diagnosis rather than workflow guidance.
ArtifactStatus for the plan artifact also carries quickPlanReadiness, the
executable equivalent of the named quick plan readiness predicate that
oat-project-quick-start defines and that oat-project-plan,
oat-project-progress, and oat-project-next route by: ready frontmatter
(oat_status: complete, oat_ready_for: oat-project-implement, oat_template
absent or false), a durable ## Reviews disposition read fence-aware, a
substantive ### Task pNN-tNN: under a ## Phase heading, and an absent or
empty oat_promotion_pending_phases, so a brief or goal project promoted to
quick is not ready (promotion-pending-phases) while it lists phases. The field reports
ready plus the first unmet clause in failure, and is omitted on every other
artifact type.
recommendSkill(projectState)
Pure function that maps parsed project state to the next recommended OAT workflow skill.
In quick mode at the plan phase, the route is decided by quickPlanReadiness:
a ready plan keeps its ordinary route to oat-project-implement, and a plan
that is not ready routes to oat-project-quick-start to be finished in place.
Every other workflow mode and phase is unaffected by the predicate.
evaluateQuickPlanReadiness(planContent) and quickPlanNotReadyReason(readiness)
evaluateQuickPlanReadiness evaluates the quick plan readiness predicate
against plan.md content (null when the file is missing) and returns the same
QuickPlanReadiness value quickPlanReadiness carries.
quickPlanNotReadyReason returns the reason recommendSkill gives when a quick
plan is not ready. The CLI dashboard (oat state refresh) routes a quick plan in
progress through both, so it reports the router's route and wording.
Current Consumers
packages/cli/src/commands/project/status.tspackages/cli/src/commands/project/list.tspackages/cli/src/commands/state/generate.ts
The CLI also uses adjacent config-resolution code for oat config dump, but the control plane remains focused on project artifact parsing rather than config ownership.
Development
pnpm --filter @open-agent-toolkit/control-plane test
pnpm --filter @open-agent-toolkit/control-plane lint
pnpm --filter @open-agent-toolkit/control-plane type-check
pnpm --filter @open-agent-toolkit/control-plane build