Releases: heggria/taskflow
Release list
v0.1.5
[0.1.5] — 2026-07-03
Added
- Scoring gates (
scoreongatephases). Deterministic, composable,
auditable quality checks: six pure scorers (exact-match,contains,
regex,json-schema,length-range,code-compiles) run against a target
string at zero tokens and combine viaall/any/weighted
(+threshold). When the deterministic combination passes and the judge
cannot veto it, the gate auto-passes with no LLM call (mirrors theeval
fast-path). When it fails, an optionaljudge(LLM-as-judge) decides, or the
gatetaskruns with the scorer report appended, or — with no fallback — the
gate blocks explicitly. The structured result is the gate's.json
({steps.<gate>.json.combined},.json.results), so downstream phases can
route on quality, not just pass/fail. - Reflexion memory in loops (
reflexion: true). Each iteration after the
first receives a structured failure summary of the prior one via the
{reflexion}placeholder (auto-appended when absent, capped at 2000 chars):
expect-contract diagnostics, the (sanitized) error, or the unmetuntil,
plus a truncated output snippet. Body failures become feedback instead of
termination — timeout/abort/over-budget still hard-stop, and exhausting
maxIterationson a failure still fails the phase.PhaseState.loop.failures
records every failed iteration for audit. Default off = byte-for-byte the
historical behavior. - Side-effect classification (
idempotent: false). Marks a phase with
irreversible side effects (webhook POSTs, deploys, DB writes): transient
provider errors are not auto-retried (explicitretry{}is still
honored) and the result is never cached in any scope (within-run resume,
cross-run,incremental). The phase state recordssideEffect: true
(rendered as ⚡), and a re-execution on resume surfaces a warning. - Single-source skills. The pi and codex skill docs are now authored once
inskills-src/taskflow/and compiled per host byscripts/build-skills.mjs
(npm run build:skills); a drift guard (skills-build.test.ts) fails CI if a
generated file is edited directly. Codex reaches feature parity with pi
automatically.
Security
- Scoring-gate hardening for LLM-generated dynamic sub-flows.
validateTaskflowrejectscode-compilesscorers (compiler execution — the
npx tscpath could resolve a repo-plantednode_modules/.bin/tsc) and
regexscorers (catastrophic-backtracking ReDoS) insideflow{def}
definitions produced at runtime — the same hardening class as the existing
script-phase block. Author-written flows keep both (a human reviewed them). code-compilesruns in an isolated temp directory.mkdtempSynccloses
the predictable-temp-name symlink/TOCTOU race, and running the compiler with
that dir as its cwd stopsnpxresolving a repo-plantedtsceven in
author-written flows (defense-in-depth).- Judge-prompt injection guard. A scoring gate's judge embeds the
model-produced target in a fenced evidence block; fences in the target are
now neutralized so crafted output cannot close the block and inject
instructions at prompt level. - Reflexion prompt-injection surface reduced. Provider error noise is run
throughsanitizeErrorMessagebefore it is injected into the next
iteration's prompt (HTML gateway pages no longer leak in verbatim). - ReDoS fixes in
safeParse. The fenced-block extractor and the stray-key
diagnostic regexes are now linear-time (removed ambiguous adjacent whitespace
quantifiers) — closes twojs/polynomial-redoscode-scanning alerts.
Fixed
- Detached (background) runs load the host runner correctly. The host now
self-reports its runner module path viaimport.meta.urlinstead of
resolving a relative.tsspecifier thatrewriteRelativeImportExtensions
left pointing at a non-existentdist/runner.ts— every detached phase
previously failed with "No subagent runner injected" in the published build
while dev checkouts worked. The detached runner now also fails fast (exits
non-zero, persisting the real import error on a__detach__phase) instead
of burying the cause under N no-runner stubs. - Loop
untilself-references no longer error.{steps.<thisId>.json.done}
in a loop'suntil(the documented stop-condition pattern) was flagged as a
self-reference bug; loop phases are now exempt. - Per-scorer field validation. Fields not applicable to a scorer's type
(e.g.negateoncontains) are rejected instead of silently ignored. - Loop-only fields on non-loop phases warn.
until/maxIterations/
convergenceon a non-loop phase now surface a warning instead of being
silently dropped.
Docs
- The interpolation reference now documents
{reflexion},{loop.iteration},
{loop.lastOutput},{loop.maxIterations}, and thescore.target/
score.judge.taskinterpolation sites. examples/quality-pipeline.jsoncomposes all three new features (scoring
gate → reflexion loop → non-idempotent notify).
v0.1.4
[0.1.4] — 2026-07-02
Security
- Dynamic sub-flows can no longer smuggle
scriptphases (RCE guard).
validateTaskflowrejectsscriptphases in LLM-authored dynamic flow
definitions (flowphases with an inline definition produced at runtime),
closing the path where a subagent's output could inject arbitrary shell
commands into the host. - Dependency audit clean.
npm audit fixbumps transitiveprotobufjs
to 7.6.4 (GHSA schema-derived name shadowing, moderate) — 0 open alerts.
Fixed (adversarial review of the features below)
- Peek
--itemnow keys by positional label, not section order. A
budget-skipped map item has no section in the merged output, so section-order
indexing silently returned the WRONG item's content for every position after
the gap.splitItemsnow parses each### [k/N]label and keys byk; a
missing item returns "not found (budget-skipped items have no section)" with
the available indices. /tf peekrejects non-numeric--item/--limitwith a usage message
instead of passingNaNthrough ("Item NaN out of range").- Contract
enumcomparison is now key-order-insensitive for object
literals (structuraldeepEqualinstead ofJSON.stringifyequality). - Tournament phases propagate
timedOutwhen all variants fail by
phase-timeout (the custom all-failed return path missed the marker). - Codex MCP
taskflow_runpersists terminal run state even if the runtime
throws (finally-wrapped saveRun), and bothtaskflow_run/
taskflow_peekdescriptions cross-reference the runId so LLM callers chain
them. verifyTaskflow's contract pass scans more ref sources —context,
input,judge,withvalues, and array-formrun— closing false-negative
gaps for{steps.X.json.field}typos.
Added
- Peek — post-hoc inspection of intermediate phase outputs.
/tf peek <runId> [phaseId](pi) and thetaskflow_peekMCP tool (Codex) read one
phase's output from a stored run: omitphaseIdfor a phase listing
(status + output size),--jsonfor the parsed JSON,--item <n>for one
section of a map/parallel fan-out,--limit <chars>to adjust truncation.
Output is hard-truncated (default 4000 chars, ceiling 32000) and the
operation is read-only + explicitly human/tool-invoked, so the
context-isolation contract (only the final output enters the conversation)
is preserved — peek is the debugging escape hatch for "phase 4 of 12
produced garbage" without re-running the whole flow. The Codex MCP
taskflow_runnow persists run state (throttled + terminal, same contract
as the pi adapter) and reports the runId, so MCP runs are peekable too. - Per-phase
timeoutfor agent-running phases. Previously onlyscript
phases had a time cap; every other phase type could run unboundedly (the
idle watchdog only catches silent stalls, not busy-but-never-finishing
subagents).timeout(ms, >= 1000) now caps EACH subagent call of an
agent/gate/reduce/map/parallel/loop/tournament phase: on expiry the
subagent is aborted, the phase fails with atimedOut: truemarker
(rendered as ⏱ in the pi TUI), and the failure is deterministic — never
retried (neither explicitretrynor the transient fallback), so a capped
call can't double-spend. Not valid on approval/flow phases (validation
error). Script phases keep their existing child-process semantics and now
also recordtimedOuton the phase state. - Output schema contracts (
expect). A JSON-emitting phase
(agent/gate/reduce/loop withoutput: "json") can declare the shape its
output must satisfy — a small JSON-Schema-like contract (type,
properties,required,items,enum, nested). The runtime validates
the parsed output the moment the subagent finishes; a violation fails the
phase with per-path diagnostics (e.g.$.score: required key is missing)
and is retryable under the phase's explicitretrypolicy — turning
"phase completed but the shape is wrong and downstream silently
mis-parses" into an immediate, precise failure at the source. Statically,
validateTaskflowrejects malformed contracts andverifyTaskflowgains a
contractpass that warns when a{steps.X.json.field}ref names a field
absent from X's declared contract — catching ref typos before a single
token is spent. Zero new dependencies (hand-rolled total validator in
taskflow-core/src/contract.ts). - Script phase test coverage — validation, execution, security, and
robustness suites for thescriptphase type (952 tests total).
Changed
- Each published package now ships its README (
scripts/copy-readme.mjs),
fixing the empty npm package pages. - Docs sync: README/README.zh-CN/AGENTS/CONTRIBUTING/SECURITY refreshed for
the script phase, brand/version drift fixed; CI actions bumped
(checkout v7, setup-node v6) and dev-dependencies updated.
v0.1.3
[0.1.3] — 2026-07-02
Added
- Codex MCP
taskflow_compilerenders an inline SVG diagram. It now emits a
hand-rendered SVG of the flow DAG so the Codex desktop app shows a real diagram
instead of a bare<image content>placeholder; a layered text outline rides
along as the caption/fallback for the CLI/TUI and vision-less models. Oversized
graphs skip the image and fall back to text. Injection-safe: all rendered text
is XML-escaped and the renderer is total (never throws on malformed input).
Isolated to thecodex-taskflowadapter — core's Mermaidcompileartifact is
unchanged (taskflow_run/taskflow_verifyreturn text only).
Fixed
- Eval gates could silently auto-PASS on an unresolved ref or parse error. A
containscheck with a missing{steps.*}LHS, or any eval with a parse
error, used to skip the LLM gate (evaluateConditionfails open withtrue).
Both now fail-safe — a missing ref or unparseable eval falls through to the LLM
gate instead of bypassing the safety check. - A
mapphase with a literal-arrayovercrashed the whole run with
over.match is not a function. The map runtime assumedoverwas always a
string interpolation ref (e.g.{steps.scan.json}) and called.match()on
it. Two-layer fix:validateTaskflownow rejects a non-stringoverup front
with an actionable message (emit the list from an upstream phase and reference
its.json), anddirectRefguards against non-string input so the runtime
fails a phase gracefully instead of throwing even if validation is bypassed. - Codex MCP tools threw or false-passed on malformed input.
taskflow_verify
crashed (phases is not iterable) on a missingphases;taskflow_compile
false-passed an empty flow (✓ PASS) and crashed on a non-stringmap.overor
a phase missing itsid. Both tools now validate first and return a structured
✗ FAIL(still rendering a diagram for a renderable-but-invalid flow so it can
be debugged), and the SVGtruncatehelper coerces non-string fields so the
renderer can never throw. Hardening was extended to the full class of
JSON-valid-but-malformed inputs: non-stringid/task/agent/when,null
or non-object phase elements, non-string gateevalentries, malformed
cache/cache.fingerprint, and non-objectbranchesentries all return a
structured validation error instead of throwing, and every diagram renderer
(Mermaid + SVG + text outline) is total against a non-arrayphases. - Static verification ignored
reduce.fromedges.verify.tsbuilt its
successor / terminal / connectivity graphs fromdependsOnonly, so a phase
feeding a reduce solely viafromwas falsely flagged terminal/dead-end and
the reduce falsely flagged unreachable — contradicting the runtime and the
compile outline, which usedependenciesOf(dependsOn ∪ from). All graph
helpers now usedependenciesOf. - Broken install contract on Node 22.0–22.18.
engines.nodewas>=22on
all packages, but the locked Pi SDK requires>=22.19.0; with
engine-strict=truethat meant a hard install failure once the Pi deps were
reached. Bumped all fourengines.nodeto>=22.19.0to match the real floor.
Changed
- The Codex plugin pins the MCP package version it launches.
.mcp.jsonnow
runsnpx -y -p codex-taskflow@<version>(was unpinned), so the installed
plugin version binds the exact code executed. The publish workflow verifies the
pin (andplugin.json's version) equals the release tag. - CI matured for the multi-host monorepo. Node
22/24test matrix,
cross-platformnpm install, abuild(dist-emit) job, the network-free Codex
MCP e2e suites (stdio handshake + comprehensive rendering/injection), CodeQL,
Dependabot, and least-privilege permissions. - Rebrand:
pi-taskflow→ taskflow. The project is now presented as a
host-neutral, multi-host orchestration runtime (Pi and Codex), not a
Pi-only extension. GitHub repo renamedheggria/pi-taskflow→
heggria/taskflow; all docs, badges, hero/social images, and the 6 i18n
READMEs updated. npm package names are unchanged (taskflow-core,
pi-taskflow,codex-taskflow) and thepi-taskflowpackage keeps its
pi-*keywords +pimanifest field, so Pi package indexing and
pi install npm:pi-taskfloware unaffected. - MCP
serverInfonow reportstaskflow/0.1.3(waspi-taskflow/
0.0.28).
Added
- Codex plugin (
packages/codex-taskflow/plugin/) for zero-config,
plug-and-play install:codex plugin marketplace add heggria/taskflowthen
codex plugin add taskflow@taskflow. Ships a.codex-plugin/plugin.json
manifest, a.mcp.jsonthat launches the MCP server vianpx codex-taskflow
(no separate global install), and a routingSKILL.mdso Codex reaches for the
taskflow_*tools on multi-phase / fan-out work automatically. A repo-root
.claude-plugin/marketplace.jsonmakes the plugin discoverable. tool_timeout_sec: 1800in the Codex plugin's.mcp.json. Codex applies a
per-server MCP tool-call timeout; when unset the plugin inherited Codex's
(short) default, so a long multi-phasetaskflow_run— which returns only
after the whole DAG finishes — could be abandoned client-side while the run
kept executing server-side. The plugin now ships a 30-minute default so large
flows aren't cut off. Override per machine in~/.codex/config.tomlunder
[mcp_servers.taskflow]if you need more or less.
v0.1.2
[0.1.2] — 2026-06-30
Fixed
- codex-taskflow MCP server failed every phase with
Model metadata for {{fast}} not found(same refactor-omission class as issue #3). The MCP
server calleddiscoverAgents(cwd, "both")without the model-roles map, so
the built-in agents'{{fast}}/{{strong}}/… placeholders were never
resolved. Fixed: it now callsreadSubagentSettings()and passes
modelRolesthrough, exactly like the pi adapter. - codex-runner passed pi-provider model ids to
codex exec, which rejected
them. Once the placeholders resolved, the resulting ids
(openrouter/deepseek/...,anthropic/glm-5.2:xhigh) are pi's provider
namespacing — Codex model ids are flat (gpt-5.5,claude-sonnet-4-6) and
it errors withModel metadata for <id> not found. Fixed:runCodexAgentTask
now drops a model that still looks like a pi-provider path (contains/) or an
unresolved{{placeholder}}, socodex execfalls back to its own configured
default model. A user who sets a real Codex model id (no/) still gets it
passed through. Verified end-to-end: a single-agent flow now runs to
completion on Codex and returns the model's output.
v0.1.1
[0.1.1] — Unreleased
Fixed
- Foreground taskflow runs silently executed no phase (issue #3, broadest
impact — found during the regression sweep). The same default-runner change
that broke detached runs also brokerunFlow(every foreground run) and both
recomputeTaskflowpaths inpi-taskflow/src/index.ts: they constructed
RuntimeDepswithout arunTask, so every phase hit thenoRunnerInjected
stub. Pre-refactor this worked only because the engine's defaultrunTask
wasrunAgentTask(same package). Fixed by explicitly injecting
piSubagentRunner.runTaskat all three call sites. Added a structural
regression test that scans the production source and fails if any
executeTaskflow/recomputeTaskflowdeps omitsrunTask. - Detached (background) runs crashed on launch (issue #3). The detached
runner specifier resolved todist/detached-runner.js.js(double.js) under
taskflow-core's"./*"export rewrite, so the spawned child died at import
withCannot find module. Now resolved with a suffix-less specifier
(taskflow-core/detached-runner) →dist/detached-runner.js. The stale
--experimental-strip-typesflag (the runner ships compiled) is dropped. - Detached runs could never execute any phase (issue #3, deeper). The
detached-runner calledexecuteTaskflowwith norunTask(see the default
above), so every detached phase failed with "No subagent runner injected" even
after the module loaded. Fixed: the host serializes arunnerModule/
runnerExport(resolved from its own package, works under both workspaces and
npm installs) into the detached context file, and the detached-runner
dynamically imports it and injectsrunTask. - A crashed detached runner no longer leaves the run stuck at
running
forever (issue #3, secondary). The host now pipes stderr and attaches
exit/errorhandlers that, when the child dies before reaching a terminal
state, persiststatus: "failed"with the captured stderr recorded in a
pollable synthetic phase (__detach__). Race-safe: guarded by pid + status so
a genuine terminal state the runner persisted is never clobbered.
v0.1.0
[0.1.0] — 2026-06-27
Monorepo split + first multi-host release. pi-taskflow is now three
independently published packages built on a host-neutral engine, and the
taskflow engine runs on both Pi and Codex.
Added
taskflow-core— the host-neutral engine (DSL, runtime, cache, verify,
FlowIR, shared context tree, agent discovery, persistence, the
SubagentRunnercontract). Zero host-SDK dependency (depends only on
typebox). Vendors the three small pi helpers it used (StringEnum,
parseFrontmatter,getAgentDir) so it is fully standalone.codex-taskflow— run taskflow on OpenAI Codex: acodex exec-backed
subagent runner, plus a dependency-free MCP server (codex-taskflow-mcp) that
exposestaskflow_run/list/show/verify/compileto Codex users. Register with
codex mcp add taskflow -- codex-taskflow-mcp.- Host-neutral
SubagentRunnerseam — the engine drives any host via an
injectedrunTask;piSubagentRunnerandcodexSubagentRunnerare the two
implementations.
Changed
pi-taskflowis now the Pi adapter package (same published name — existing
pi install npm:pi-taskflowusers are unaffected). It depends on
taskflow-core.- Repo restructured to npm workspaces under
packages/*. Each package now
builds todist/(tsc→.js+.d.ts) and publishes the compiled
output — required because Node refuses to type-strip.tsundernode_modules.
Dev still runs the TypeScript sources directly (adevelopmentexport
condition +--conditions=developmentresolvestaskflow-coretosrc, so a
fresh clone runstypecheck/testwith no build step). CI builds and
publishes the three packages in dependency order (core → pi → codex) on a
v*tag.
Notes
- All 864 tests pass across the three packages (713 core + 135 pi + 16 codex).
- See
RELEASE.mdfor the publish flow.
pi-taskflow 0.0.28
Granular-reuse release. v0.0.27 proved the incremental-recompute cost win — v0.0.28 makes it far larger and trivial to opt into. Invalidation drops from whole-flow to per-phase and per-item, and a single flag flips an entire flow into cross-run reuse.
⚡ Smarter invalidation
- Per-phase fingerprint (
v3:phasefp) — edit one phase, only it and its dependents re-run. An independent sibling keeps its cache hit. - Per-item
mapcaching — change 1 of N items, only that item re-executes. The other N−1 are served for $0.
🎛️ One-flag opt-in
incrementalflag (flow-level andrunarg) — defaults every phase to cross-run reuse, no per-phase annotation needed. Per-phasecacheand blocked types (gate/approval/loop/tournament) still win; default stays the saferun-only.
🔍 See what got reused
- Reuse reporting — the end-of-run report and
/tf recomputenow show reused-vs-executed counts plus a per-phase Why trace:▲ rerun · ✂ cutoff · ✓ reused · ✗ failed, with← causedBy.
Implementation details & soundness
v3:phasefphashes the phase plus its transitivedependsOn ∪ fromclosure, replacing the whole-flowv2:flowdefhash.cacheKeysemits a 4-tier read ladder (v3:phasefpwrite →v2:flowdef→ bare flowdef → legacy, all read-only) so the upgrade is additive — no miss-storm for unchanged flows. Fail-open: any per-phase error degrades that phase to the whole-flow hash. Falls back to whole-flow when per-phase invalidation can't be statically guaranteed (flow-widecontextSharing, anyshareContextphase in the closure,join: "any", or sub-flow inner phases). —extensions/flowir/phasefp.ts- Per-item keys omit the structural fingerprint (which hashes the whole
oversource) so changing one item no longer moves every key; they fold[phase.id, it.agent, model, it.task]+ the world-state tail. Disabled (whole-map only) underrun-only/offscope,shareContext/ flow-widecontextSharing, or inside a runtime-generated sub-flow. phaseFingerprintnow stripscache,retry,concurrency,final— none changes a phase's output, so a no-op config tweak no longer falsely invalidates.- Reuse reporting reports a dollar figure only for within-run reuse (where prior usage is preserved); cross-run hits are counted without an invented saving. —
summarizeReuse/RecomputeDecisioninextensions/runtime.ts
Tests: 804 → 846 (+42) across 46 files — new cache-phasefp, cache-peritem, incremental-flag, reuse-summary suites. Typecheck clean.
Upgrade: drop-in. The 4-tier cache ladder reads old entries; nothing to migrate.
pi-taskflow 0.0.27
[0.0.27] — 2026-06-25
Evidence release: the incremental-recompute cost win is now proven, not
asserted. v0.0.25 made/tf recomputetrustworthy and v0.0.26 made the
dependency contract under it real — but the only cascade test re-ran every
phase, so "rerun only what changed" had no regression proof. This release pins
the two ways recompute actually saves money, closing the flagship's open
acceptance criterion (the prerequisite for ever flipping recompute on by
default).
Added
- Flagship cost-win tests (
test/recompute.test.ts):- Partial cascade —
rerun < full. A diamond where one branch shares no
edge with the changed seed proves the unrelated phase is reused (0 tokens),
never re-run, and the rerun set is strictly smaller than the full flow. - Early-cutoff propagation. Re-seeding a phase whose output is unchanged
cuts off its entire transitive downstream — only the seed spends a token,
every descendant hits its cache. This is the "changed a file that didn't
actually affect the result ⇒ near-zero rerun" guarantee.
- Partial cascade —
- Tests: 802 → 804 (+2).
Changed
- README test count and feature line refreshed (was stale at 702/34 files):
now 804 tests across 42 files, withincremental recomputeand
FlowIR compile seamlisted among the headline capabilities.
Notes
- Scope held deliberately. Two further H2 ideas — flipping
runto
auto-recompute by default, and preciseir-changed/ map item-level reuse —
are not in this release. The first changes every user'srunbehavior
(a kernel-level, post-M5 decision); the latter two are scoped-out in the
roadmap (§6) as later RFCs. Shipping the proof first keeps each step
independently releasable.
pi-taskflow 0.0.26
[0.0.26] — 2026-06-25
Foundation release: the convergence roadmap's H1 lands — a real FlowIR
compile seam (M1), a declared dependency plane (M2), and a
backward-compatible cache-key migration. v0.0.25 made incremental recompute
trustworthy; this release makes the contract underneath it real: the
recompute frontier now reasons over observed ∪ declared dependencies, the
flow definition compiles through a typed IR surface instead of an inlined
hash, and folding the definition into the cache key no longer evicts every
pre-existing cross-run entry.
Added
- FlowIR compile seam (M1). New
extensions/flowir/{index,translate,meta}.ts
exposescompileTaskflowToIR(def) → { ir, meta, hash, usedFallbackHash, warnings, errors }— a typed, never-throwing projection of a desugared flow
into a content-addressed IR. The runtime now routesflowDefHashthrough this
seam instead of inlining it.translateis currently a 1:1 stub projection
(sousedFallbackHashistrueand the hash equals the vendored
flowDefHash); it becomes the genuine overstory compiler once that kernel is
vendored, at which point the cache-key version advancesv2: → v3:. /tf ir <flow>command +irtool action. Renders the compiled IR plus
its hash and any structuredCompileError[]— zero tokens, no LLM.- Declared dependency plane (M2).
compileTaskflowToIRsynthesizes per-phase
DeclaredDeps { reads, writes }from interpolation refs
(task/over/when/until/eval/branches/with/context) and
dependsOn, attaches them toir.meta.declaredDeps, and persists them to
RunState./tf recomputenow computes its stale frontier over
union(observed ∪ declared) rather than observed-only — a dependency that
was declared but never interpolated at runtime is no longer missed. - Tests: 753 → 802 (+49) across new suites:
flowir.test.ts,
flowir-declared.test.ts,stale-union.test.ts(incl. a 500-iteration
property test proving the union frontier is never narrower than observed-only),
recompute-union.test.ts,cache-migration.test.ts, pluse2e-flowir.mts
ande2e-cache-migration.mts.
Fixed
- Cache-key migration no longer evicts existing cross-run entries. Folding
flowdef:into the key previously invalidated every pre-existing cross-run
cache entry on upgrade (a one-time miss-storm).cacheKeyis now versioned
(v2:flowdef:) with a 3-tier lookup: new key → bareflowdef:key →
legacy (no-flowdef) key. Old entries still hit for one release cycle; there is
no write-through on a fallback hit (legacy entries age out naturally), and
every tier still includesflow:${name}so two different flows can never
collide. - Declared plane and recompute guard now see
loop.untilandgate.eval.
collectRefsskippeduntil(loop convergence) andeval[](gate zero-token
checks), so a dependency expressed only in those fields was absent from the
declared plane and from thedryRun:falseunobserved-dependency guard. Both
are now scanned. (Closes the two MEDIUM findings from the H1 risk review.)
Compatibility
- Backward compatible.
RunState.flowDefHashandRunState.declaredDeps
are optional — pre-0.0.26 run states load unchanged. A compile/hash failure
fails open:usedFallbackHashstays set, cross-run cache is disabled for that
run, and the key degrades to a flow-scoped (collision-free) form. The one
observable change on upgrade is a single re-execution of in-flight phases
whose storedinputHashpredates thev2:prefix.
pi-taskflow 0.0.25
Release v0.0.25: incremental recompute is now trustworthy Closes three silent correctness holes in /tf recompute: when/eval readSet capture, loop self-read scheduler deadlock, and the unobserved-dependency guard for real recomputation. The difference between looks-incremental and provably-incremental.