Skip to content

Releases: heggria/taskflow

v0.1.5

Choose a tag to compare

@github-actions github-actions released this 03 Jul 06:45
70846c4

[0.1.5] — 2026-07-03

Added

  • Scoring gates (score on gate phases). 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 via all / any / weighted
    (+ threshold). When the deterministic combination passes and the judge
    cannot veto it, the gate auto-passes with no LLM call (mirrors the eval
    fast-path). When it fails, an optional judge (LLM-as-judge) decides, or the
    gate task runs 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 unmet until,
    plus a truncated output snippet. Body failures become feedback instead of
    termination
    — timeout/abort/over-budget still hard-stop, and exhausting
    maxIterations on 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 (explicit retry{} is still
    honored) and the result is never cached in any scope (within-run resume,
    cross-run, incremental). The phase state records sideEffect: 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
    in skills-src/taskflow/ and compiled per host by scripts/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.
    validateTaskflow rejects code-compiles scorers (compiler execution — the
    npx tsc path could resolve a repo-planted node_modules/.bin/tsc) and
    regex scorers (catastrophic-backtracking ReDoS) inside flow{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-compiles runs in an isolated temp directory. mkdtempSync closes
    the predictable-temp-name symlink/TOCTOU race, and running the compiler with
    that dir as its cwd stops npx resolving a repo-planted tsc even 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
    through sanitizeErrorMessage before 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 two js/polynomial-redos code-scanning alerts.

Fixed

  • Detached (background) runs load the host runner correctly. The host now
    self-reports its runner module path via import.meta.url instead of
    resolving a relative .ts specifier that rewriteRelativeImportExtensions
    left pointing at a non-existent dist/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 until self-references no longer error. {steps.<thisId>.json.done}
    in a loop's until (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. negate on contains) are rejected instead of silently ignored.
  • Loop-only fields on non-loop phases warn. until / maxIterations /
    convergence on 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 the score.target /
    score.judge.task interpolation sites.
  • examples/quality-pipeline.json composes all three new features (scoring
    gate → reflexion loop → non-idempotent notify).

v0.1.4

Choose a tag to compare

@github-actions github-actions released this 02 Jul 10:37
a376c96

[0.1.4] — 2026-07-02

Security

  • Dynamic sub-flows can no longer smuggle script phases (RCE guard).
    validateTaskflow rejects script phases in LLM-authored dynamic flow
    definitions (flow phases 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 fix bumps transitive protobufjs
    to 7.6.4 (GHSA schema-derived name shadowing, moderate) — 0 open alerts.

Fixed (adversarial review of the features below)

  • Peek --item now 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. splitItems now parses each ### [k/N] label and keys by k; a
    missing item returns "not found (budget-skipped items have no section)" with
    the available indices.
  • /tf peek rejects non-numeric --item/--limit with a usage message
    instead of passing NaN through ("Item NaN out of range").
  • Contract enum comparison is now key-order-insensitive for object
    literals (structural deepEqual instead of JSON.stringify equality).
  • Tournament phases propagate timedOut when all variants fail by
    phase-timeout (the custom all-failed return path missed the marker).
  • Codex MCP taskflow_run persists terminal run state even if the runtime
    throws
    (finally-wrapped saveRun), and both taskflow_run /
    taskflow_peek descriptions cross-reference the runId so LLM callers chain
    them.
  • verifyTaskflow's contract pass scans more ref sourcescontext,
    input, judge, with values, and array-form run — 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 the taskflow_peek MCP tool (Codex) read one
    phase's output from a stored run: omit phaseId for a phase listing
    (status + output size), --json for 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_run now persists run state (throttled + terminal, same contract
    as the pi adapter) and reports the runId, so MCP runs are peekable too.
  • Per-phase timeout for agent-running phases. Previously only script
    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 a timedOut: true marker
    (rendered as ⏱ in the pi TUI), and the failure is deterministic — never
    retried (neither explicit retry nor 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 record timedOut on the phase state.
  • Output schema contracts (expect). A JSON-emitting phase
    (agent/gate/reduce/loop with output: "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 explicit retry policy — turning
    "phase completed but the shape is wrong and downstream silently
    mis-parses" into an immediate, precise failure at the source. Statically,
    validateTaskflow rejects malformed contracts and verifyTaskflow gains a
    contract pass 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 the script phase 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

Choose a tag to compare

@github-actions github-actions released this 02 Jul 03:54
dacc0b4

[0.1.3] — 2026-07-02

Added

  • Codex MCP taskflow_compile renders 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 the codex-taskflow adapter — core's Mermaid compile artifact is
    unchanged (taskflow_run / taskflow_verify return text only).

Fixed

  • Eval gates could silently auto-PASS on an unresolved ref or parse error. A
    contains check with a missing {steps.*} LHS, or any eval with a parse
    error, used to skip the LLM gate (evaluateCondition fails open with true).
    Both now fail-safe — a missing ref or unparseable eval falls through to the LLM
    gate instead of bypassing the safety check.
  • A map phase with a literal-array over crashed the whole run with
    over.match is not a function. The map runtime assumed over was always a
    string interpolation ref (e.g. {steps.scan.json}) and called .match() on
    it. Two-layer fix: validateTaskflow now rejects a non-string over up front
    with an actionable message (emit the list from an upstream phase and reference
    its .json), and directRef guards 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 missing phases; taskflow_compile
    false-passed an empty flow (✓ PASS) and crashed on a non-string map.over or
    a phase missing its id. 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 SVG truncate helper coerces non-string fields so the
    renderer can never throw. Hardening was extended to the full class of
    JSON-valid-but-malformed inputs: non-string id/task/agent/when, null
    or non-object phase elements, non-string gate eval entries, malformed
    cache/cache.fingerprint, and non-object branches entries all return a
    structured validation error instead of throwing, and every diagram renderer
    (Mermaid + SVG + text outline) is total against a non-array phases.
  • Static verification ignored reduce.from edges. verify.ts built its
    successor / terminal / connectivity graphs from dependsOn only, so a phase
    feeding a reduce solely via from was falsely flagged terminal/dead-end and
    the reduce falsely flagged unreachable — contradicting the runtime and the
    compile outline, which use dependenciesOf (dependsOn ∪ from). All graph
    helpers now use dependenciesOf.
  • Broken install contract on Node 22.0–22.18. engines.node was >=22 on
    all packages, but the locked Pi SDK requires >=22.19.0; with
    engine-strict=true that meant a hard install failure once the Pi deps were
    reached. Bumped all four engines.node to >=22.19.0 to match the real floor.

Changed

  • The Codex plugin pins the MCP package version it launches. .mcp.json now
    runs npx -y -p codex-taskflow@<version> (was unpinned), so the installed
    plugin version binds the exact code executed. The publish workflow verifies the
    pin (and plugin.json's version) equals the release tag.
  • CI matured for the multi-host monorepo. Node 22/24 test matrix,
    cross-platform npm install, a build (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 renamed heggria/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 the pi-taskflow package keeps its
    pi-* keywords + pi manifest field, so Pi package indexing and
    pi install npm:pi-taskflow are unaffected.
  • MCP serverInfo now reports taskflow / 0.1.3 (was pi-taskflow /
    0.0.28).

Added

  • Codex plugin (packages/codex-taskflow/plugin/) for zero-config,
    plug-and-play install: codex plugin marketplace add heggria/taskflow then
    codex plugin add taskflow@taskflow. Ships a .codex-plugin/plugin.json
    manifest, a .mcp.json that launches the MCP server via npx codex-taskflow
    (no separate global install), and a routing SKILL.md so Codex reaches for the
    taskflow_* tools on multi-phase / fan-out work automatically. A repo-root
    .claude-plugin/marketplace.json makes the plugin discoverable.
  • tool_timeout_sec: 1800 in 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-phase taskflow_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.toml under
    [mcp_servers.taskflow] if you need more or less.

v0.1.2

Choose a tag to compare

@github-actions github-actions released this 30 Jun 07:21

[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 called discoverAgents(cwd, "both") without the model-roles map, so
    the built-in agents' {{fast}}/{{strong}}/… placeholders were never
    resolved. Fixed: it now calls readSubagentSettings() and passes
    modelRoles through, 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 with Model metadata for <id> not found. Fixed: runCodexAgentTask
    now drops a model that still looks like a pi-provider path (contains /) or an
    unresolved {{placeholder}}, so codex exec falls 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

Choose a tag to compare

@github-actions github-actions released this 29 Jun 12:20

[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 broke runFlow (every foreground run) and both
    recomputeTaskflow paths in pi-taskflow/src/index.ts: they constructed
    RuntimeDeps without a runTask, so every phase hit the noRunnerInjected
    stub. Pre-refactor this worked only because the engine's default runTask
    was runAgentTask (same package). Fixed by explicitly injecting
    piSubagentRunner.runTask at all three call sites. Added a structural
    regression test that scans the production source and fails if any
    executeTaskflow/recomputeTaskflow deps omits runTask.
  • Detached (background) runs crashed on launch (issue #3). The detached
    runner specifier resolved to dist/detached-runner.js.js (double .js) under
    taskflow-core's "./*" export rewrite, so the spawned child died at import
    with Cannot find module. Now resolved with a suffix-less specifier
    (taskflow-core/detached-runner) → dist/detached-runner.js. The stale
    --experimental-strip-types flag (the runner ships compiled) is dropped.
  • Detached runs could never execute any phase (issue #3, deeper). The
    detached-runner called executeTaskflow with no runTask (see the default
    above), so every detached phase failed with "No subagent runner injected" even
    after the module loaded. Fixed: the host serializes a runnerModule/
    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 injects runTask.
  • A crashed detached runner no longer leaves the run stuck at running
    forever
    (issue #3, secondary). The host now pipes stderr and attaches
    exit/error handlers that, when the child dies before reaching a terminal
    state, persist status: "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

Choose a tag to compare

@github-actions github-actions released this 29 Jun 10:01

[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
    SubagentRunner contract). 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: a codex exec-backed
    subagent runner, plus a dependency-free MCP server (codex-taskflow-mcp) that
    exposes taskflow_run/list/show/verify/compile to Codex users. Register with
    codex mcp add taskflow -- codex-taskflow-mcp.
  • Host-neutral SubagentRunner seam — the engine drives any host via an
    injected runTask; piSubagentRunner and codexSubagentRunner are the two
    implementations.

Changed

  • pi-taskflow is now the Pi adapter package (same published name — existing
    pi install npm:pi-taskflow users are unaffected). It depends on
    taskflow-core.
  • Repo restructured to npm workspaces under packages/*. Each package now
    builds to dist/ (tsc.js + .d.ts) and publishes the compiled
    output — required because Node refuses to type-strip .ts under node_modules.
    Dev still runs the TypeScript sources directly (a development export
    condition + --conditions=development resolves taskflow-core to src, so a
    fresh clone runs typecheck/test with 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.md for the publish flow.

pi-taskflow 0.0.28

Choose a tag to compare

@github-actions github-actions released this 27 Jun 06:36

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 map caching — change 1 of N items, only that item re-executes. The other N−1 are served for $0.

🎛️ One-flag opt-in

  • incremental flag (flow-level and run arg) — defaults every phase to cross-run reuse, no per-phase annotation needed. Per-phase cache and blocked types (gate/approval/loop/tournament) still win; default stays the safe run-only.

🔍 See what got reused

  • Reuse reporting — the end-of-run report and /tf recompute now show reused-vs-executed counts plus a per-phase Why trace: ▲ rerun · ✂ cutoff · ✓ reused · ✗ failed, with ← causedBy.

Implementation details & soundness
  • v3:phasefp hashes the phase plus its transitive dependsOn ∪ from closure, replacing the whole-flow v2:flowdef hash. cacheKeys emits a 4-tier read ladder (v3:phasefp write → 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-wide contextSharing, any shareContext phase 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 over source) 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) under run-only/off scope, shareContext / flow-wide contextSharing, or inside a runtime-generated sub-flow.
  • phaseFingerprint now strips cache, 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 / RecomputeDecision in extensions/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

Choose a tag to compare

@github-actions github-actions released this 25 Jun 06:45

[0.0.27] — 2026-06-25

Evidence release: the incremental-recompute cost win is now proven, not
asserted.
v0.0.25 made /tf recompute trustworthy 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.
  • Tests: 802 → 804 (+2).

Changed

  • README test count and feature line refreshed (was stale at 702/34 files):
    now 804 tests across 42 files, with incremental recompute and
    FlowIR compile seam listed among the headline capabilities.

Notes

  • Scope held deliberately. Two further H2 ideas — flipping run to
    auto-recompute by default, and precise ir-changed / map item-level reuse —
    are not in this release. The first changes every user's run behavior
    (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

Choose a tag to compare

@github-actions github-actions released this 25 Jun 04:40

[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
    exposes compileTaskflowToIR(def) → { ir, meta, hash, usedFallbackHash, warnings, errors } — a typed, never-throwing projection of a desugared flow
    into a content-addressed IR. The runtime now routes flowDefHash through this
    seam instead of inlining it. translate is currently a 1:1 stub projection
    (so usedFallbackHash is true and the hash equals the vendored
    flowDefHash); it becomes the genuine overstory compiler once that kernel is
    vendored, at which point the cache-key version advances v2: → v3:.
  • /tf ir <flow> command + ir tool action. Renders the compiled IR plus
    its hash and any structured CompileError[] — zero tokens, no LLM.
  • Declared dependency plane (M2). compileTaskflowToIR synthesizes per-phase
    DeclaredDeps { reads, writes } from interpolation refs
    (task/over/when/until/eval/branches/with/context) and
    dependsOn, attaches them to ir.meta.declaredDeps, and persists them to
    RunState. /tf recompute now 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, plus e2e-flowir.mts
    and e2e-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). cacheKey is now versioned
    (v2:flowdef:) with a 3-tier lookup: new key → bare flowdef: 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 includes flow:${name} so two different flows can never
    collide.
  • Declared plane and recompute guard now see loop.until and gate.eval.
    collectRefs skipped until (loop convergence) and eval[] (gate zero-token
    checks), so a dependency expressed only in those fields was absent from the
    declared plane and from the dryRun:false unobserved-dependency guard. Both
    are now scanned. (Closes the two MEDIUM findings from the H1 risk review.)

Compatibility

  • Backward compatible. RunState.flowDefHash and RunState.declaredDeps
    are optional — pre-0.0.26 run states load unchanged. A compile/hash failure
    fails open: usedFallbackHash stays 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 stored inputHash predates the v2: prefix.

pi-taskflow 0.0.25

Choose a tag to compare

@github-actions github-actions released this 24 Jun 13:32
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.