v6.0.0
Major release. Two themes ship together:
Theme A — Extractor removal (the v5 → v6 migration story): the
build-time + runtime extractor pattern is removed entirely and replaced
by two phase-specific recorders that were already shipping under L7:
StructureRecorder (build phase) and the existing FlowRecorder
(runtime phase). No deprecation period. See MIGRATION-6.md for the
recipes.
Theme B — Observer-architecture polish (3 follow-up proposals): new
helpers (splitStageId, walkSubflowSpec) on footprintjs/trace; new
fields on StructureSubflowMountedEvent (subflowSpec, subflowPath);
uniform onStageExecuted for ALL stage kinds with a new stageType
discriminator on FlowStageEvent.
Theme B — Observer-architecture polish
Added
StructureSubflowMountedEvent.subflowSpec— the mounted subflow's
complete spec (reference-equal tosubflow.buildTimeStructure), set on
every EAGER mount, undefined on lazy mounts. Lets a parent-attached
recorder walk the subflow's full inner structure without needing
inner-builder attachment (proposal #1).StructureSubflowMountedEvent.subflowPath— local mount id within
the parent ('auth'for top-level, composed'auth/verify'for nested
observations). Matches runtimetraversalContext.subflowPathsemantics
(proposal #1).walkSubflowSpecexported fromfootprintjs/trace— a generator
that yields the structural shape of a subflow spec as a flat ordered
stream (subflow-startmarkers,stage/edge/loop/subflowitems,
auto-recurse with composed paths). Replaces the consumer-side
connected-component "tag inner stages" workaround (proposal #1).WalkerItem,WalkerOptionstypes exported fromfootprintjs/trace.splitStageId(prefixedStageId)exported fromfootprintjs/trace—
decomposes a prefixed stage id ('sf-tools/execute-tool-calls') into
{ localStageId, subflowPath }. MirrorsparseRuntimeStageId's
decomposition and applies uniformly tospec.id,CommitBundle.stageId,
any prefixed id without the#Nsuffix (proposal #2).StageTypeunion exported from the engine narrative types:
'linear' | 'decider' | 'fork' | 'selector' | 'subflow-mount'
(proposal #3).FlowStageEvent.stageType— REQUIRED discriminator field on every
onStageExecutedevent. Lets consumers route by stage kind without a
side-table lookup into the chart spec (proposal #3).StructureRecorder+ 6 event payload types
(StructureDeciderCompleteEvent,StructureEdgeAddedEvent,
StructureEdgeKind,StructureLoopEdgeAddedEvent,
StructureStageAddedEvent,StructureSubflowMountedEvent) now exported
from the mainfootprintjsbarrel, not onlyfootprintjs/advanced.
Changed (behavior)
-
onStageExecutednow fires for ALL stage kinds, not just linear
stages. Previously, decider / fork / selector / subflow-mount stages
fired only their specialized event (onDecision/onFork/
onSelected/onSubflowEntry) and the engine returned without firing
onStageExecuted. As of this release, the engine fires
onStageExecutedAFTER the specialized event for these stages — every
stage that runs produces exactly oneonStageExecutedevent, carrying
the newstageTypediscriminator (proposal #3).Migration: consumers that used
onStageExecutedto track "did this
stage run?" no longer need separateonDecision/onFork/onSelected
handlers for the same purpose. Consumers that usedonStageExecutedas
a LINEAR-ONLY signal must filter:if (event.stageType !== 'linear') return;. SeeMIGRATION-6.mdfor the recipe.
Changed (docs / JSDoc)
parseRuntimeStageIdJSDoc now warns explicitly about the LOCAL-vs-FULL
naming collision between its returnedstageIdfield andspec.id/
node.idfor subflow-nested stages. Points consumers atsplitStageId
for safe decomposition (proposal #2).StructureStageAddedEvent.stageIdJSDoc clarifies that the field
carries the builder's LOCAL form at event-fire time, butspec.idis a
LIVE reference that may be rewritten to the FULL prefixed form when
this builder is mounted as a subflow (proposal #2).StructureSubflowMountedEventJSDoc updated to describe the new
subflowSpecandsubflowPathfields, including the reference-equality
guarantee and the lazy-mount caveat (proposal #1).
Engine internals
IControlFlowNarrative.onStageExecutedsignature gained a required
stageType: StageTypeargument. CustomNarrativeGenerator
implementations must update their method signature.
NullControlFlowNarrativeGeneratorand the built-in
NarrativeFlowRecorder/CombinedNarrativeRecorderwere updated.CombinedNarrativeRecorder.onStageExecutedand
NarrativeFlowRecorder.onStageExecutedgate tostageType === 'linear'
(or undefined for back-compat) — narrative output is byte-stable across
the v6 transition because the specialized handlers
(onDecision/onFork/onSelected/onSubflowEntry) keep emitting
their narrative entries unchanged.
Tests
- +9 unit tests for uniform
onStageExecuted(per-kind fire-order +
literalstageTypeassertions + pause/error negative cases). - +3 unit tests for
CombinedNarrativeRecorderbyte-stability (no
double-emission for decider / fork / subflow-mount stages). - +5 wiring tests for
subflowSpecreference equality at all 4 eager
mount sites +subflowSpec: undefinedon lazy mounts. - +8 unit tests for
walkSubflowSpec(subflow-start markers, recursion,
composed paths,{recurse: false}). - +5 unit tests for
splitStageIdround-trip parity with
parseRuntimeStageId.
Theme A — Extractor rip-out
Breaking changes
- Removed all extractor types:
BuildTimeExtractor,
BuildTimeNodeMetadata,TraversalExtractor,ChartExtractor, and
ExtractorError. These were the v5.x build-time + runtime extraction
surfaces. - Removed
FlowChartOptions.extractorfield.FlowChartOptionsis no
longer generic — the new shape is{ structureRecorders?, description? }. - Removed the legacy 5-positional
flowChart()signature
((name, fn, id, buildTimeExtractor?, description?)). The factory now
accepts only(name, fn, id, options?). - Removed builder methods:
addBuildTimeExtractor(),
addTraversalExtractor(),getBuildTimeExtractorErrors(). - Removed executor method
getExtractorErrors(). Use
builder.getStructureBuildErrors()(call on the BUILDER, before/after
.build()). - Removed
FlowChartExecutorOptions.enrichSnapshotsfield — it was
only consumed by the extractor system and is now dead code. - Removed internal:
FlowChartBuilder_flush(),
_drainCursorIfPending(),_invokeOnBuildAdHoc(),
_drainPendingFlush(),_chartExtractor,_buildTimeExtractorfields;
engineExtractorRunner.tsdeleted;FlowchartTraverserextractor
wiring (extractorRunner,callExtractor,getStagePath,
getExtractedResults,getExtractorErrors) removed.
Replacement
- Build-phase observation:
StructureRecorder— 5 events
(onStageAdded,onEdgeAdded,onLoopEdgeAdded,onDeciderComplete,
onSubflowMounted). Register via the options bag
(flowChart('seed', fn, 'seed', { structureRecorders: [rec] })) or the
fluent chain (.attachStructureRecorder(rec)). - Runtime per-stage observation: already-shipping
FlowRecorderwith
hooks likeonStageExecuted,onDecision,onSubflowEntry,onError.
Attach viaexecutor.attachFlowRecorder(rec).
Migration
See MIGRATION-6.md for diff-by-diff recipes covering
the four most common v5.x → v6.0 patterns.