Skip to content

Implement workflow orchestration engine (P0 + P1)#714

Merged
itlackey merged 53 commits into
mainfrom
claude/workflow-orchestration-improvements-k39vjo
Jul 8, 2026
Merged

Implement workflow orchestration engine (P0 + P1)#714
itlackey merged 53 commits into
mainfrom
claude/workflow-orchestration-improvements-k39vjo

Conversation

@fwdslsh-dev

Copy link
Copy Markdown
Collaborator

Summary

Implements the orchestration plan P0 (Workflow Plan Graph compiler) and P1 (native executor) for engine-driven workflow execution. Workflows can now declare per-step orchestration subsections (### Runner, ### Model, ### Timeout, ### Fan-out, ### Schema, ### Env, ### Depends On) and be executed via akm workflow run with automatic unit dispatch, fan-out concurrency, schema-validated structured output with retry, and per-unit persistence.

Key changes:

Parser & Schema

  • Extended workflow Markdown grammar with orchestration subsections (parser-orchestration.ts)
  • Added WorkflowStepOrchestration type to schema with runner, model, timeout, fan-out, schema, env, and dependency declarations
  • Semantic validation for Depends On references

IR Compilation (P0)

  • ir/schema.ts: Backend-agnostic Workflow Plan Graph with IrStepPlan, IrAgentNode, IrMapNode, and IrGateNode
  • ir/compile.ts: Deterministic compiler from parsed workflows to plan graphs; linear workflows compile to agent chains (no behavior change), orchestrated steps compile fan-out into map nodes

Native Executor (P1)

  • native-executor.ts: Executes one step's IR subgraph with fan-out through the scheduler, schema-validated structured output via runStructured, per-unit persistence, and workflow_unit_* events
  • scheduler.ts: Concurrency policy layer with engine-wide limits (16 cores max, lifetime unit cap per run)
  • unit-writer.ts: Serialized writer queue for workflow_run_units to prevent SQLite contention
  • run-workflow.ts: Engine loop that walks the plan graph and advances steps through completeWorkflowStep gate

Storage & Persistence

  • Migration 004: workflow_run_units table for per-unit state (runner, model, input hash, result, tokens, failure reason, timestamps)
  • WorkflowRunUnitRow and related types in workflow-runs-repository.ts

Supporting Infrastructure

  • json-schema.ts: Bounded JSON Schema subset validator for structured output normalization
  • env-binding.ts: Extracted env-ref resolution shared between akm env run and workflow engine
  • Unit preamble template with run/step/unit context and ground rules
  • Check-in surfacing fixes (C2, M1) for stalled-run detection in plain-text output

Documentation & Tests

  • Comprehensive test suites for parser, IR compilation, native executor, and unit persistence
  • Updated workflow feature docs with orchestration examples
  • Added fable model alias (Mythos-class tier)
  • STABILITY.md notes experimental status

The implementation maintains the gated step spine invariant: every step advances strictly through completeWorkflowStep, never by direct row writes. Durable-row resume re-dispatches only steps that never completed, using input hashes to avoid double-issuing side-effecting work.

Test plan

  • Added 552 lines of tests in native-executor.test.ts covering fan-out dispatch, item interpolation, evidence collection, schema validation with retry, and reducer logic
  • Added 231 lines in parser-orchestration.test.ts for extended grammar parsing
  • Added 150 lines in ir-compile.test.ts for plan graph compilation
  • Added 182 lines in run-units.test.ts for unit persistence and writer queue
  • Added 79 lines in json-schema-subset.test.ts for schema validation
  • Added 102 lines in checkin-surfacing.test.ts for check-in rendering fixes
  • Updated migration characterization snapshot to include migration 004
  • All existing workflow tests pass; no behavior change for linear workflows

Checklist

  • Tests pass (bun test)
  • Lint passes (bun run lint)
  • Docs updated (workflows.md, CHANGELOG.md, STABILITY.md)

https://claude.ai/code/session_018DptAcDcuW2TF2RfM3a4ok

claude added 2 commits July 6, 2026 02:11
…, native executor, akm workflow run

Implements P0 + P1 of docs/technical/akm-workflows-orchestration-plan.md,
plus the open P0 hygiene items:

- IR: Workflow Plan Graph (src/workflows/ir/schema.ts, compile.ts). Linear
  workflows compile to one agent node + gate per step — no behavior change;
  fan-out steps compile to a map node with collect/vote reducers.
- Extended Markdown grammar: ### Runner / Model / Timeout / Fan-out /
  Schema / Env / Depends On step subsections (parser-orchestration.ts),
  additive and backward-compatible, errors accumulated; Depends On ids
  validated in validator.ts.
- Persistence: migration 004 workflow_run_units (+ failure_reason), unit
  CRUD on WorkflowRunsRepository, serialized writer queue
  (exec/unit-writer.ts) so N parallel unit completions never contend on
  SQLite's single writer.
- Native executor (exec/native-executor.ts): fan-out via a scheduler built
  on concurrentMap (cap min(16, cores−2), lifetime unit cap, per-unit
  timeout default 10m), schema-validated structured output via the
  runStructured core + a bounded JSON-Schema-subset validator
  (core/json-schema.ts), unit preamble asset, env bindings through the
  akm env run machinery extracted to env-binding.ts (secret tokens,
  dangerous-key policy, keys-only audit), workflow_unit_* events.
- Engine loop (exec/run-workflow.ts) + `akm workflow run` CLI: advances the
  gated spine strictly through completeWorkflowStep; gate rejections stop
  the engine and surface corrective feedback. Marked experimental in
  STABILITY.md.
- Hygiene (check-in review C2/M1): plain-text formatters render the
  CONTINUE directive; every run-detail response evaluates the check-in;
  validator error message lists name/updated; removed the phantom
  `workflow step` alias from docs.
- Model tiers: new `fable` built-in alias (claude-fable-5 /
  opencode/claude-fable-5); docs recommend fast/balanced/deep tiers with
  deep → fable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DptAcDcuW2TF2RfM3a4ok
Two independent adversarial reviews of a1b9637 confirmed 8 findings; all
fixed with regression tests:

- Prompt interpolation used string replaceAll, so item/param values
  containing GetSubstitution patterns ($&, $$, $', $`) silently corrupted
  unit prompts. All template replacements now use function callbacks.
- `akm workflow run` on a failed/blocked run dispatched the terminal
  step's units (real cost) before completeWorkflowStep's preflight threw.
  The engine now refuses non-active runs before any dispatch.
- The llm runner ignored per-unit timeout, abort signal, ### Model, and
  native structured output; chatCompletion now receives timeoutMs
  (### Timeout none → max 32-bit delay), signal, responseSchema, and a
  model override resolved through the global alias tiers.
- The lifetime unit cap reset per invocation; it is now seeded from the
  run's workflow_run_units journal so it is truly per-run.
- ### Env bindings were silently dropped on sdk/llm runners (audit event
  fired, nothing injected). Units now fail loudly with env_unsupported;
  docs updated (env requires the agent runner).
- Fan-out concurrency defaulted to the engine cap, violating the repo's
  local-model-first LLM-defaults rule; it now defaults to 1 and the cap
  only clamps.
- Resume re-dispatched and clobbered completed unit rows; completed units
  with a matching input_hash are now reused from the journal (durable-row
  resume at unit granularity).
- ### Depends On was parsed and validated but never consulted; the engine
  now asserts dependencies are completed/skipped before dispatching a step.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DptAcDcuW2TF2RfM3a4ok

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an engine-driven workflow orchestration layer to AKM: workflows can now declare per-step orchestration metadata (runner/model/timeout/fan-out/schema/env/depends-on), compile to a deterministic IR plan graph, and execute via a new akm workflow run path with per-unit persistence, concurrency controls, and structured-output validation.

Changes:

  • Extend workflow Markdown parsing + schema to support orchestration subsections and validate Depends On references.
  • Introduce a Workflow Plan Graph IR compiler (P0) and a native executor + scheduler + unit journal/writer queue (P1), plus a new CLI subcommand workflow run.
  • Add migration 004 and repository support for workflow_run_units, plus substantial tests and docs for the new engine and related fixes (check-in surfacing, model alias).

Reviewed changes

Copilot reviewed 39 out of 39 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/workflows/run-units.test.ts New tests for workflow_run_units persistence + serialized writer queue behavior.
tests/workflows/parser-orchestration.test.ts New tests for orchestration subsection parsing and error cases.
tests/workflows/native-executor.test.ts New tests for fan-out dispatch, schema validation/retry, durable-row reuse, and engine loop behavior.
tests/workflows/migrations.test.ts Updates migration ledger expectations to include migration 004.
tests/workflows/ir-compile.test.ts New tests for deterministic IR compilation and fan-out → map node lowering.
tests/workflows/checkin-surfacing.test.ts New tests ensuring stalled-run check-in directives surface in status and plain text.
tests/storage/sqlite-migrations.characterization.test.ts Updates workflow DB schema characterization to include workflow_run_units.
tests/storage/snapshots/sqlite-migrations.characterization.test.ts.snap Snapshot update reflecting migration 004 schema additions.
tests/json-schema-subset.test.ts New tests for the bounded JSON-Schema-subset validator.
tests/agent/agent-builders.test.ts Adds tests for the new built-in fable model alias resolution.
STABILITY.md Marks akm workflow run + orchestrated steps as experimental/stability caveat.
src/workflows/validator.ts Adds semantic validation for ### Depends On references; fixes frontmatter allowed-keys messaging.
src/workflows/schema.ts Adds orchestration types (runner, model, timeout, fanOut, schema, env, dependsOn).
src/workflows/runtime/workflow-asset-loader.ts Retains full parsed workflow document for IR compilation while keeping the existing step projection.
src/workflows/runtime/runs.ts Includes check-in evaluation in run-detail shaping (checkin surfaced when stalled).
src/workflows/parser.ts Integrates orchestration parsing and improves unknown-subsection error messaging.
src/workflows/parser-orchestration.ts New orchestration subsection parser (Runner/Model/Timeout/Fan-out/Schema/Env/Depends On).
src/workflows/ir/schema.ts New backend-agnostic plan-graph IR schema for workflow execution nodes + gated step spine.
src/workflows/ir/compile.ts New deterministic compiler from parsed workflows → plan graph IR (linear and fan-out).
src/workflows/exec/unit-writer.ts New serialized write queue to avoid concurrent SQLite writer contention.
src/workflows/exec/scheduler.ts New scheduler policy layer (global concurrency cap + lifetime unit cap + abort support).
src/workflows/exec/run-workflow.ts New engine loop implementing akm workflow run, advancing steps strictly via completeWorkflowStep.
src/workflows/exec/native-executor.ts New native executor for one step plan: dispatch fan-out units, validate structured output, persist unit rows.
src/workflows/db.ts Adds migration 004 creating workflow_run_units table + index.
src/workflows/cli.ts Registers run as a workflow subcommand.
src/storage/repositories/workflow-runs-repository.ts Adds workflow_run_units row types and repo methods to insert/finish/query units.
src/output/text/workflow.ts Registers a plain-text formatter for workflow-run output.
src/output/text/helpers.ts Adds workflow run plain formatter; restores check-in directive in plain status output.
src/output/shapes/passthrough.ts Adds workflow-run to passthrough output command list.
src/integrations/agent/model-aliases.ts Adds built-in fable alias mapping for supported platforms.
src/core/json-schema.ts New bounded JSON-Schema-subset validator used for structured output verification.
src/core/events.ts Adds workflow_unit_started / workflow_unit_finished event types.
src/commands/workflow-cli.ts Adds akm workflow run JSON command wiring and flag parsing (--params, --max-steps).
src/commands/env/env-cli.ts Refactors env injection path to reuse extracted resolveEnvBinding.
src/commands/env/env-binding.ts New shared env-ref resolution + safety enforcement used by both env run and workflow units.
src/assets/prompts/workflow-unit-preamble.md New standardized per-unit preamble template injected into dispatched prompts.
docs/features/workflows.md Documents orchestrated steps and the new akm workflow run behavior and semantics.
docs/features/agent-integration.md Documents fable as a built-in model alias.
CHANGELOG.md Adds release notes for orchestration engine, check-in surfacing fixes, and fable alias.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/workflows/exec/native-executor.ts Outdated
Comment thread src/workflows/parser-orchestration.ts Outdated
Comment thread src/workflows/exec/run-workflow.ts Outdated
claude added 11 commits July 6, 2026 04:11
- resolveFanOutSource now uses own-property checks (Object.hasOwn) so a
  fan-out `over:` key can never resolve to an Object.prototype member
  (toString, constructor, ...) instead of real params/evidence.
- `### Env` entries are validated with parseAssetRef instead of an
  "env:" substring probe: the `environment:` alias and origin-qualified
  refs are accepted, while non-env refs like "myenv:foo" or
  "secret:token" are rejected at parse time.
- Corrected the run-workflow header comment: the plan is compiled once
  per invocation (the run's step snapshot is fixed at start), not per
  step.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DptAcDcuW2TF2RfM3a4ok
…surfacing, scheduler tests

Final sweep against docs/technical/akm-workflows-orchestration-plan.md
closed the remaining committed-scope gaps:

- ### Route (the routing pattern, explicitly in P1 + the touch list):
  `input:` / `when: <value> => <step-id>` / `default:` grammar with
  accumulated parse errors; validator enforces targets exist, come after
  the routing step, and never self-route; compiles to IrRouteSpec on the
  step plan (IrRouterNode remains the node-level form for the imperative
  frontend). The engine evaluates the route from the step's structured
  result (schema field or vote winner), run params, or prior evidence —
  own-property lookups, primitives only — records the decision in step
  evidence, fails the step on unroutable values, and auto-skips
  unselected branch targets via completeWorkflowStep(status: skipped).
- Conformance suite (tests/workflows/conformance/): golden linear,
  fan-out+schema+vote, and routed workflows pinned as explicit expected
  plans + executed unit graphs, structured so P3/P4 backends register in
  BACKENDS and must reproduce identical graphs (plan §Anti-drift).
- akm show now surfaces each step's orchestration summary
  (runner/profile/model/timeout/fan-out/schema-presence/env/dependsOn/
  route) via WorkflowStepOrchestrationSummary.
- Direct scheduler tests: default concurrency 1, clamp to the engine
  cap, min(16, cores-2) derivation, pre-dispatch lifetime-cap error,
  abort stops claiming, sibling isolation on failure.

Deliberately deferred per the plan's phasing: best-of-n reducer and
looping gates (P4 judge machinery), phases/watch/budget/worktree (P3/P4),
CC emitter + report (P3), per-harness extractors (P2), replay resume (P5).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DptAcDcuW2TF2RfM3a4ok
…identity, unit session ids

Orchestration plan P2, executed via multi-agent workflow with peer review:

- AkmHarness descriptor fields: pattern (in-harness|local-runner|
  cloud-delegate), structuredOutput (native-schema|native-json|none),
  resume { flag, takesSessionId }, identityEnv (session-id vars) split
  from presenceEnv (presence-only flags), resultExtractor seam.
- Adapters (builder + result extractor + tests each): codex (exec,
  --output-schema native schema, JSONL), copilot (-p --allow-all-tools,
  json), pi (-p --mode json), gemini (-p --output-format json), aider
  (-m --yes-always, prompt-injected schema), amazonq (q chat
  --no-interactive, bare --resume flagged takesSessionId: false),
  openhands (--headless -t --json). All registered in HARNESS_REGISTRY;
  BUILTIN_BUILDERS derivation makes them dispatchable, and the
  missing-builder ConfigError no longer fires for codex/gemini/aider.
- agent-identity.ts and the session-log provider list are now derived
  from the registry; presence flags (CODEX_SANDBOX, GEMINI_CLI) infer
  the harness but never persist as agent_session_id.
- Migration 005: workflow_run_units.session_id — harness-native session
  ids journaled opportunistically via the extractor seam, threaded
  through the executor (UnitOutcome → finishUnit → durable-row reuse).
- Builtin profiles for copilot/pi/amazonq/openhands (+ headless
  variants); executor applies a harness's result extractor to agent
  stdout before recording unit text.

P3+ intentionally NOT started: paused for a redesign review of the
remaining phases (Claude Code coupling concerns).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DptAcDcuW2TF2RfM3a4ok
… semantics

Post-P2 owner review found the P1 execution semantics brittle and
coupled to Claude Code. Root cause recorded in the addendum: P1 ported
CC's features (fan-out, schemas, routes) without its foundation
(immutable plan, explicit data flow, journaled replay, single driver).

Owner decisions (2026-07-06), superseding the P1 grammar and the P3/P5
designs:
- Orchestrated workflows become a YAML program format with a published
  JSON Schema and a closed, parsed expression language
  (params/steps.<id>.output/item) — no ambient evidence search, no
  string re-substitution.
- Full replay semantics: plan frozen per run (plan_json/plan_hash,
  migration 006), content-derived unit identity, replay-divergence
  detection, run lease; orchestration decisions byte-reproducible.
- P1 markdown orchestration subsections will be removed (linear
  markdown workflows stay stable/unchanged); DB stays additive.
- Failure policy surface (on_error/retry on the failure_reason
  taxonomy), fail-fast default.
- P3 CC-delegation compiler replaced by a harness-neutral driver
  protocol (workflow brief + report). Copilot cloud delegate and stash
  MCP server remain out of scope.

Phases R1-R4 replace P3-P5. No code changes in this commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DptAcDcuW2TF2RfM3a4ok
…ded; compiler mid-flight

Paused snapshot of the R1 workflow run (wf_35e2cd58-5d6), resuming on a
timer. State:

- DONE (green, tested): src/workflows/program/ expression language
  (closed ${{ }} grammar, parsed AST, single-pass resolution) +
  program-expressions tests; schemas/akm-workflow.json + YAML program
  parser/validator (src/workflows/program/{schema,parser}.ts) +
  program-parser tests.
- IN FLIGHT (agent died mid-task; will re-run on resume):
  src/workflows/ir/schema.ts is rewritten to IR v2, but ir/compile.ts
  and the executor still expect v1 — THIS COMMIT DOES NOT TYPECHECK.
  Migration 006 and asset plumbing not started.

Do not build on this commit; the resumed workflow completes the
compiler, frozen-plan, asset-plumbing, and engine-cutover phases.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DptAcDcuW2TF2RfM3a4ok
…en plans, asset plumbing in flight

Mid-run durability checkpoint of the resumed R1 workflow (wf_35e2cd58-5d6).
Landed since fa0267d (compiler + frozen-plan phases, both green):

- ir/schema.ts + ir/compile.ts at IR v2: compileWorkflowProgram (YAML,
  full expression validation, defaults merged at compile) and
  compileWorkflowPlan (linear markdown golden) both lowering to one plan
  graph; ir/plan-hash.ts canonical JSON + sha256.
- Migration 006: plan_json/plan_hash + engine lease columns;
  setRunPlan in the repository; startWorkflowRun compiles and freezes
  the plan in the same transaction as the run insert; run-workflow
  executes the frozen plan with hash integrity check + legacy fallback.
- ir-compile/conformance/migration tests rewritten for v2.

Asset-plumbing agent still running; engine cutover pending — tree may
not fully typecheck. Do not build on this commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DptAcDcuW2TF2RfM3a4ok
…plete; paused before docs

R1 workflow (wf_35e2cd58-5d6) paused at the docs phase per owner
instruction. Landed since 24949aa (both phases green; tree typechecks,
596 workflow/agent tests pass):

- Asset plumbing: workflow:<name> refs resolve .yaml/.yml; loader
  projects programs into WorkflowAsset (program field); indexer matcher
  + renderer for YAML programs (show carries the orchestration
  summary); workflow validate lints YAML via parse+compile; workflow
  template --yaml (src/workflows/authoring/workflow-program-template.yaml).
- Engine cutover to IR v2 + expressions: executor resolves ${{ item }} /
  ${{ params.* }} / ${{ steps.<id>.output.* }} via the parsed-AST
  single-pass resolver (ambient evidence search DELETED); failure
  policy live (on_error continue + bounded retry on failure_reason,
  fail-fast default, retry attempts journaled as <unitId>~r<n>); route
  v2 (explicit input expression + when map); P1 markdown orchestration
  grammar REMOVED (parser-orchestration.ts deleted, schema/validator/
  renderer trimmed; linear markdown unchanged); executor/conformance
  test suites rewritten against YAML program sources.

Remaining on resume: r1-core adversarial review (its first attempt died
mid-run and is un-journaled, so it re-runs BEFORE docs), docs phase,
final verify. Script hardened: a dead review agent now halts the run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DptAcDcuW2TF2RfM3a4ok
…written

R1 run wf_35e2cd58-5d6: the r1-core adversarial review returned 6
findings (4 actionable); the fix agent resolved all four at root cause
with regression tests — notably a route-dispatch defect in
run-workflow.ts and a `templating: verbatim|expressions` discriminator
on IR agent nodes so linear-markdown instructions are never
expression-parsed. Tree is green (tsc clean, 603 workflow/agent tests).

The docs agent wrote its doc updates (CHANGELOG/STABILITY/workflows.md/
plan annotations) but died before journaling a result; it re-runs on
resume, followed by the final verify + final review phases.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DptAcDcuW2TF2RfM3a4ok
…s, cascaded routing, per-dispatch cap)

Closes R1 of the redesign addendum (run wf_35e2cd58-5d6). This commit
lands the docs phase and the final whole-diff review's three confirmed
fixes (7 regression tests):

- Step-output contract: engine steps journal a promoted artifact under
  evidence.output (solo result/text; collect -> per-item array with
  null slots for on_error:continue failures; vote -> winner), and the
  exported projectStepOutput() is the single projection used by both
  the unit-template scope and route evaluation — so the documented
  ${{ steps.<id>.output.<path> }} addressing resolves against real
  results end-to-end. Manual/pre-R1 evidence is exposed as-is.
- Cascaded routing: a skipped router's own targets are marked
  skip-on-reach (live path AND journaled decisions on resume), so an
  unselected branch's downstream branch targets never dispatch and the
  skipped router's input is never resolved.
- Lifetime unit cap is now consumed per ACTUAL journaled dispatch
  (retries included, durable-row reuses free) via a DispatchBudget in
  the executor; the pre-batch check that made large partially-completed
  fan-outs unresumable is removed. Capped steps fail hard regardless of
  on_error.

Docs: workflows.md rewritten for the YAML program format (expression
table, frozen-plan semantics, failure policy, step-output contract),
STABILITY + CHANGELOG updated, plan addendum annotated R1 SHIPPED.

Verified post-fix: biome clean (12 intentional template-literal
warnings in test fixtures), tsc clean, unit 23780 pass / integration
735 pass with only the documented pre-existing failures.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DptAcDcuW2TF2RfM3a4ok
…ergence + run lease

R2 run wf_98b198b1-d0e, first two phases green (tsc clean, 274 workflow
tests pass):

- Unit identity is content-derived (<node_id>:<sha256(item)[:12]>,
  :solo for single units; ~r<n> retries and ~l<loop> gate-loop rows
  stack on top) — identity survives item-list regeneration/reordering.
  Duplicate fan-out items fail the step deterministically. Journaled
  COMPLETED rows with matching identity but different input_hash raise
  a hard "replay divergence" failure, never a silent re-dispatch;
  pre-release positional-id rows are ignored cleanly.
- Run lease enforced (columns from migration 006): atomic acquire
  before dispatch, renew per step, release on exit incl. failure paths;
  a second engine invocation on a live lease refuses up front; expired
  leases are claimable; manual `workflow complete` is refused while an
  engine holds the spine (leaseHolder threaded through
  CompleteWorkflowStepInput, engine-only).

Run halted at the artifact-gates phase by the account monthly spend
limit (must() guard, working as designed). Remaining on resume:
artifact gates + max_loops, budget, watch, worktree+SDK, docs, verify.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DptAcDcuW2TF2RfM3a4ok
…iew r2-a fixes applied

Watchdog checkpoint (run wf_98b198b1-d0e in flight, Budget phase active).
Landed since aa1dbc7, tsc clean + 287 workflow tests green:

- Typed artifacts: outputSchema validated against the promoted artifact
  before completion; artifact-judging gates (judge receives the
  artifact, canonical JSON clipped, not engine prose); gate evaluator
  calls journaled as unit rows (<stepId>.gate:l<loop>); bounded gate
  loops (max_loops) with feedback threaded into re-run prompts, loop
  rows journaled as ~l<loop>.
- Review r2-a: 5 findings, actionable ones fixed — notably the
  run-workflow !result.ok branch failing runs unconditionally, which
  broke on_error: continue semantics at the engine level.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DptAcDcuW2TF2RfM3a4ok
@itlackey

itlackey commented Jul 7, 2026

Copy link
Copy Markdown
Owner

I would not merge this PR as-is. It is large — 107 files, 15,948 additions, 268 deletions — and the riskiest changes are in execution, persistence, replay, and validation paths.

I did a static review from the GitHub PR/source. I did not execute the test suite locally.

Blocking / high-risk correctness issues

  1. Run lease is explicitly not enforced, so two workflow run processes can double-dispatch work

The PR adds lease columns, but both docs and migration comments say enforcement is deferred. The runtime still loads the next step and frozen plan, then dispatches without acquiring any cross-process lease.

The in-process unit writer queue does not solve this. It serializes writes only inside one Bun process; it does not prevent a second CLI process from dispatching the same unit.

Impact: duplicated agent/LLM spend, duplicated side effects, corrupt run evidence, nondeterministic branch execution.

Fix: implement a DB-backed lease before executing any step. Use an atomic UPDATE ... WHERE engine_lease_until IS NULL OR engine_lease_until < now pattern, store holder id, refresh while running, and release on completion/failure.


  1. Durable-row replay hash is incomplete and can reuse stale work under changed execution conditions

The input hash only includes prompt, runner, model, and schema. It excludes resolved profile, resolved runner kind, resolved model after alias lookup, environment bindings, timeout, isolation, config defaults, tool policy, and harness adapter behavior.

Worse, environment bindings are resolved before checking completed unit rows, so a fully reusable completed step can fail just because a now-unused env asset was deleted or changed.

The default dispatcher also resolves live config at execution time, not from a frozen execution profile.

Impact: the engine can silently skip work that should be re-run, or fail to replay completed work because live config/env changed.

Fix: either freeze the resolved execution context into the plan/run, or include every execution-affecting input in the unit hash. Move env resolution into the dispatch path after durable-row reuse, or persist env binding identity/version in the hash.


  1. Completion gates judge a synthetic summary, not the real artifact/output

The native executor returns summaries like “Executed N units…” rather than a task-specific summary of the actual work. That synthetic string is then passed into completeWorkflowStep as the summary for completion criteria validation.

The docs admit artifact-judging gates are not implemented yet.

Impact: gates can falsely reject good work or falsely pass bad work. on_error: continue makes this worse because the gate receives a generic count of failed units rather than structured evidence.

Fix: do not market this as gated artifact execution yet. Either pass actual artifact/evidence into the judge, or disable criteria validation for native engine execution until R2 artifact gates exist.


  1. Scheduler swallows thrown exceptions and converts them into generic “aborted” unit failures

concurrentMap catches every thrown worker error and leaves the slot undefined. The native executor then maps that to an "aborted" failure with a generic message.

Under on_error: continue, those swallowed infrastructure errors can still allow the step to pass.

Impact: DB write failures, programming errors, and unexpected dispatcher bugs can be hidden as ordinary unit failures.

Fix: distinguish expected unit failures from infrastructure exceptions. concurrentMap should return { ok:false, error } or rethrow fatal errors, not erase them.


  1. Empty-string text outputs are persisted as NULL

resultJson uses outcome.text ? JSON.stringify(outcome.text) : null, so a successful text output of "" becomes NULL. On replay, NULL becomes undefined, and the successful output is lost.

Impact: downstream expressions can see null/missing output even though the original unit succeeded with an empty string.

Fix: use outcome.text !== undefined instead of truthiness.


  1. Parameter JSON schemas are documented but not enforced

Docs say params are JSON-Schema declarations. But the compiler only keeps parameter names in the frozen plan, dropping the schemas.

startWorkflowRun accepts and stores raw params without validating them against the declared schemas.

Impact: invalid params fail later as expression/runtime errors, not as authoring/start validation errors. The declared schema is mostly documentation.

Fix: freeze param schemas into the plan and validate --params at start/auto-start.


  1. Docs claim lint catches unknown params and bad output paths, but compiler does not

The docs say akm workflow validate checks “unknown step, unknown param, bad path” at lint time.

The compiler checks whether a referenced step exists and comes earlier, but it does not validate the output path against the producer’s declared output schema. It explicitly does not check param presence.

Impact: users will trust validation results that do not actually prove what the docs say they prove.

Fix: either implement schema-aware path validation and declared-param checks, or weaken the docs.


  1. Step id grammar is internally inconsistent

The program schema allows step ids beginning with digits and containing dots.

The expression parser’s requires a letter/underscore first and allows letters, digits, underscores, and hyphens — but not dots.

So a step id like 1.review can be valid as a step id but impossible to reference as ${{ steps.1.review.output... }}.

Impact: valid workflow programs can contain step ids that cannot be addressed by the expression language.

Fix: make step ids expression-addressable, or tighten the step id pattern to the expression identifier grammar.


  1. JSON Schema subset validator has object/property correctness bugs

For required fields, the validator uses key in record, so inherited prototype properties can satisfy required. It also uses key in record for properties.

additionalProperties: false is enforced only when properties exists. A schema like { type: "object", additionalProperties: false } should only allow {}, but this implementation will allow arbitrary properties.

Impact: structured output validation can pass objects that violate the declared schema.

Fix: use Object.hasOwn(record, key) and enforce additionalProperties: false even when properties is absent.


  1. Retry taxonomy does not include several failure reasons the executor actually emits

The allowed retry.on vocabulary is the PROGRAM_RETRY_REASONS set.

But the executor can emit reasons outside that set, including validation_error, expression_error, unit_cap_exceeded, env_unsupported, and dispatch_error.

Impact: workflow authors cannot write retry policies for actual engine failures. Some retryable conditions become unretryable by schema.

Fix: define one canonical workflow failure taxonomy and make parser, executor, docs, and tests all use it.


  1. CommonJS require is used inside ESM output paths

The package is "type": "module" and TypeScript emits module: "ESNext".

Yet runtime code uses bare require(...) in native-executor.ts and runs.ts.

The repo has Node compatibility scripts, so this is not just a Bun-only concern.

Impact: Node ESM execution can throw ReferenceError: require is not defined on these paths.

Fix: use dynamic await import(...), or explicitly create require via createRequire(import.meta.url) in every ESM module that needs it.


  1. Completed route replay trusts journaled selected without validating it is a legal target

seedJournaledRouteDecisions reads evidence.route.selected and applies it directly.

journaledRouteSelection only checks that it is a non-empty string.

applyRouteDecision then adds that arbitrary selected step to routeSelected and marks all declared targets as unselected.

Impact: corrupted/manual/tampered evidence can cause all real branches to be skipped.

Fix: validate journaled selected against Object.values(route.when) plus defaultStepId; fail loudly if it is not a legal branch target.


  1. A completed run still loads/verifies the plan before returning no-op state

runWorkflowSteps calls getNextWorkflowStep, then seeds units and loads the frozen plan before the main loop. That happens even when next.done is already true.

loadFrozenPlan can throw for corrupt plan JSON/hash or fall back to loading the live asset for legacy runs.

Impact: akm workflow run can fail because a completed run’s plan is corrupt or legacy asset is missing, even though no execution is needed.

Fix: return final state before loading the plan when next.done is true.


  1. Unit row replacement can hide repeated dispatch attempts and weaken the lifetime cap

The repository uses INSERT OR REPLACE for unit starts.

The lifetime cap is seeded from the number of unit rows.

Impact: repeated crash/retry of the same base unit id can replace the row instead of increasing the durable dispatch count. That makes the “lifetime” cap less durable than advertised.

Fix: append attempts instead of replacing, or keep a separate monotonic dispatch-attempt counter.


  1. finishUnit does not verify that it updated a row

finishUnit runs an UPDATE ... WHERE run_id = ? AND unit_id = ? but ignores the affected row count.

Impact: if the insert failed, row was deleted, or unit id mismatched, the executor can proceed as though persistence succeeded.

Fix: check changes; throw a fatal persistence error if no row was updated.


Major design / architecture issues

  1. The native engine is built around current live config, not a fully frozen run contract

The plan is frozen, but execution substrate is not. Runner resolution, default profile, default LLM, model aliases, and harness resolution are read from live config at execution time.

Impact: the same frozen plan can execute differently across invocations if config changes.

Fix: freeze resolved execution settings or record config fingerprints into the run/unit hash.


  1. Summary gates fail open when no LLM judge is configured

completeWorkflowStep builds a judge only when a default LLM exists; otherwise judge is null and validation effectively does not block completion.

Impact: “completion gates” are optional in a way that may surprise users, especially because the native engine claims to advance through gates.

Fix: make fail-open explicit in docs/CLI output, or provide a config option to fail closed for workflows that require gates.


  1. Structured output claims “JSON Schema” but implements a permissive subset

The parser only verifies that output is an object.

The validator ignores $ref, combinators, pattern, format, and every unknown keyword.

Impact: users can write schemas that look strict but are silently weak.

Fix: either use a real JSON Schema validator or clearly name this as akmSchemaSubset and reject unsupported keywords instead of ignoring them.


  1. The workflow format exposes declarations the engine does not enforce

Docs list typed step-output validation, artifact gates, gate.max_loops, run leases, budget ceilings, watch, and worktree isolation as not enforced.

The compiler still carries some of those fields into the IR.

Impact: users can author workflows that look safer/more controlled than they are.

Fix: hide or reject not-yet-enforced fields unless an explicit experimental flag is enabled.


  1. workflow_step_completed event is emitted for skipped/failed steps and leaks raw notes

completeWorkflowStep always appends workflow_step_completed, regardless of whether the status is completed, failed, blocked, or skipped. It also includes notes in metadata.

The engine uses completeWorkflowStep for skipped route targets and failed execution.

This conflicts with the nearby event hygiene rule that raw workflow-authored content should stay out of events.

Impact: consumers of events can misinterpret failed/skipped steps as completed, and raw notes may become prompt-injection surface.

Fix: emit status-specific events or include status in metadata; remove raw notes from event metadata.


  1. Route failure evidence drops the actual route failure detail

On route evaluation failure, the code writes notes with the route error, but persists only result.evidence, not an evidence field describing the route failure.

Impact: status/evidence consumers do not get structured route failure diagnostics.

Fix: persist evidence.routeError or equivalent.


  1. Active-run creation guard is not atomic across processes

startWorkflowRun checks for an existing active run and then inserts a new run in the same callback, but there is no unique partial index or lock shown here.

Impact: two processes can race and both create active runs for the same workflow/scope.

Fix: enforce this at the database level, not only in application code.


  1. workflow validate mishandles origin-qualified workflow refs

Runtime code supports origin-qualified refs by parsing origin//workflow:name.

But resolveWorkflowFilePath only treats values starting with workflow: as refs; anything else is returned as a filesystem path.

Impact: akm workflow validate some-origin//workflow:foo will be treated as a path, while other workflow commands can treat it as a ref.

Fix: parse refs with parseAssetRef instead of startsWith("workflow:").


  1. YAML workflow authoring is only partially integrated into workflow create

workflow template --yaml can print a YAML program.

But createWorkflowAsset always parses imported content as markdown and writes through the markdown workflow creation path.

Impact: users can validate/run YAML workflows, but the create command does not provide a clean YAML creation path.

Fix: add akm workflow create --yaml or a separate workflow program create.


  1. Timeout docs and parser disagree

Docs say timeout values are strings: "ms" | "s" | "m" | "none".

Parser also accepts bare numbers as milliseconds.

Impact: docs and schema expectations drift; tests may miss numeric timeout behavior.

Fix: either document bare numeric milliseconds or reject them.


  1. The parser’s expression checker is fragile and silently disables itself

loadExpressionChecker uses createRequire(import.meta.url) with "./expressions" and returns null on any failure.

It also catches parser/checker exceptions and returns null, meaning malformed expressions can pass this parser pass if the checker import/API fails.

Impact: duplicated parser/compiler validation gives false confidence and hides broken imports.

Fix: directly import the expression parser. The “parallel task may not exist” TODO should be removed now that the file exists.


  1. Environment merging silently overwrites duplicate keys

resolveEnvBindings merges each env ref with Object.assign. Later refs silently overwrite earlier keys.

Impact: a workflow author may think multiple env assets are additive when they are order-dependent.

Fix: detect duplicate keys and fail unless an explicit override policy is declared.


Documentation / release-quality issues

  1. Changelog contradicts itself about markdown orchestration

The “Added” section says workflow orchestration is declared with markdown subsections like ### Runner, ### Fan-out, ### Schema, etc.

The “Changed” section then says that same markdown grammar was removed before release and replaced with YAML programs.

Impact: release notes are confusing and imply a surface that no longer exists.

Fix: remove the obsolete markdown-orchestration “Added” language and describe only the final YAML surface.


  1. Codex adapter comments are stale

The Codex builder says it is “NOT registered anywhere yet.”

But the Codex harness imports the builder and exposes it on the registered harness descriptor.

The result extractor has the same stale comment.

Fix: remove stale integration-task comments before merge.


  1. Built-in model alias docs look brittle

The code hardcodes fable to claude-fable-5 / opencode/claude-fable-5.

Docs present fable as a built-in tier and recommend it for deep work.

Impact: hardcoded provider model ids age badly. If these ids are not valid in every supported environment, default workflows fail.

Fix: keep semantic aliases in docs/config examples, but avoid hardcoding speculative or provider-specific defaults unless covered by integration tests or official compatibility checks.


  1. There is a stray duplicate block comment

runs.ts has a duplicate /** before buildDefaultSummaryJudge.

Fix: remove the extra opener.


  1. Compiler comment says retry is future work even though retry is implemented

compileProgramUnit says retry dispatch is “engine-rework scope,” but the executor implements bounded retry and tests it.

Fix: update the comment; stale architecture comments are dangerous in this PR because the surface is already complex.


Missing tests I would require before merge

The added tests cover a lot of happy-path and peer-review scenarios, but the native executor tests intentionally use injected fake dispatchers and avoid real agent binaries/LLMs.

I would add tests for:

  1. Two-process run lease race: launch two workflow run invocations against the same active run and prove only one dispatches.

  2. Replay hash correctness: change env refs, profile, model alias config, default agent, and timeout between invocations; prove reuse does or does not happen intentionally.

  3. Env-after-reuse: complete all units, delete the env asset, rerun/resume, and prove completed units can still replay.

  4. Empty string output persistence: successful unit returns ""; verify row stores JSON string and downstream ${{ steps.x.output }} resolves to "".

  5. Parameter schema validation: invalid --params fails at start/auto-start, not during execution.

  6. Bad path lint: ${{ steps.discover.output.missing.deep }} should fail validation when producer output schema makes that impossible.

  7. Step id grammar: ids with dots/digit starts should either fail parsing or be referenceable.

  8. JSON schema edge cases: required: ["toString"], additionalProperties: false with no properties, inherited/prototype keys, unsupported keywords.

  9. Retry taxonomy closure: every executor-emitted failureReason is either accepted by retry.on or explicitly documented as non-retryable.

  10. Origin-qualified validate: akm workflow validate origin//workflow:name.

  11. Node ESM execution: exercise workflow run, default LLM gate, and default unit dispatcher under Node compatibility mode.

  12. Tampered route evidence: completed route with evidence.route.selected = "not-a-target" must fail loudly.

  13. Event semantics: failed/skipped steps must not emit misleading workflow_step_completed without status.

  14. No-op completed run: completed run with corrupt/missing legacy plan should return done status without trying to execute/load.

Merge recommendation

Block this PR until at least these are fixed: run lease enforcement, replay hash completeness, param/schema validation alignment, expression id grammar mismatch, JSON Schema validator bugs, ESM require usage, and misleading gate semantics. The rest can be follow-up only if the feature remains clearly experimental and the docs stop overstating what is enforced.

claude added 4 commits July 7, 2026 04:07
…ll green

Watchdog checkpoint (run wf_98b198b1-d0e in flight, review r2-b active).
Landed since 51f98f3, tsc clean + 293 workflow tests green:

- Budget ceilings end to end: YAML budget { max_tokens, max_units } →
  schema/parser/compiler → frozen plan.budget; enforcement journal-
  seeded, abort-driven, hard failure naming the ceiling.
- workflow watch: NDJSON backlog + --stream polling with terminal-status
  exit (src/workflows/exec/watch.ts), onEvent seam on RunAgentOptions.
- SDK per-cwd seam (open decision 1, proper option) + worktree isolation
  on agent AND sdk runners (git worktree lifecycle, worktree_path
  journaled at dispatch, clean-remove/dirty-retain) + sdk env bindings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DptAcDcuW2TF2RfM3a4ok
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DptAcDcuW2TF2RfM3a4ok
Watchdog checkpoint (run wf_98b198b1-d0e, final-check phase active).
Since ed78519, tsc clean + 294 workflow tests green:

- Review r2-b: 5 findings, 4 confirmed and fixed at root cause with
  regression tests proven to fail pre-fix (incl. worktree-leftovers
  integration coverage).
- R2 docs: workflows.md YAML program section extended (budget,
  isolation, gate max_loops, replay divergence, run lease, watch),
  STABILITY.md orchestration bullet updated, CHANGELOG R2 entry, plan
  addendum annotated R2 SHIPPED.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DptAcDcuW2TF2RfM3a4ok
Closes R2 of the redesign addendum (run wf_98b198b1-d0e, 14 agents,
zero errors). This commit lands the final whole-diff review's confirmed
fix plus the final-check's test relocation:

- Budget-seed accounting: gate-evaluation journal rows (now stamped
  phase="gate" at insert) are excluded from the resume-time seed for
  budget.max_units and the lifetime unit cap — resumed runs no longer
  hit spurious "budget exceeded" failures from counting judge calls as
  dispatches. Discriminator is the phase column (step ids may contain
  dots, so a node_id suffix match could collide). Regression test
  proven to fail against the pre-fix code.
- tests/workflows/worktree.test.ts moved unchanged to
  tests/integration/workflow-worktree-leftovers.test.ts: it spawns real
  git subprocesses, which unit scope forbids (lint-tests-isolation
  Rule 5); integration scope is the rule's own remedy.

R2 delivered across this run (see the wip checkpoints for detail):
content-derived unit identity + replay divergence, single-driver run
lease, typed step artifacts + artifact-judged gates + bounded
max_loops with journaled gate evaluations, budget ceilings, workflow
watch + onEvent, SDK-per-cwd seam + worktree isolation + sdk env
bindings. Verified: biome clean (12 intentional template-literal
warnings), tsc clean, unit 24020 pass / integration 747 pass with only
the documented pre-existing failures.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DptAcDcuW2TF2RfM3a4ok

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

@itlackey

itlackey commented Jul 7, 2026

Copy link
Copy Markdown
Owner

@codex do an exhaustive code review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Something went wrong. Try again later by commenting “@codex review”.

Unknown error
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@itlackey

itlackey commented Jul 7, 2026

Copy link
Copy Markdown
Owner

@codex do an exhaustive code review

Extract the engine's step semantics (work-list/identity/hashing/prompt
assembly, gate-feedback recovery, completion + route helpers) into
src/workflows/exec/step-work.ts, behavior-preserving, and add the
read-only `akm workflow brief` surface on top of it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DptAcDcuW2TF2RfM3a4ok

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e78cb16797

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


/** Lease lifetime: long enough to survive slow steps between renewals, short
* enough that a crashed engine frees the run quickly. Renewed per step. */
const RUN_LEASE_TTL_MS = 90_000;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep run leases alive during long unit dispatches

With the lease expiring after 90s and only being renewed between steps, any unit that runs longer than the TTL (the default unit timeout is 10 minutes) leaves the run claimable while the first engine is still dispatching. A second akm workflow run after the expiry can acquire the lease and dispatch the same pending/running units, and insertUnit uses the same unit ids, so the two invocations can clobber each other's journal rows and double-run side-effecting work. The lease needs a heartbeat during long step execution or a TTL that cannot expire while dispatch is in flight.

Useful? React with 👍 / 👎.

Comment on lines +55 to +59
// Explicit extension wins (accepts refs like "release/ship.yaml").
const lower = name.toLowerCase();
for (const ext of WORKFLOW_EXTENSIONS) {
if (lower.endsWith(ext)) return path.join(typeRoot, name);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject YAML suffixes when creating markdown workflows

Because explicit workflow extensions now win here, akm workflow create foo.yaml routes the create path to workflows/foo.yaml, but createWorkflowAsset still writes and validates the markdown template with parseWorkflow. The resulting asset is immediately unusable: show/start/validate choose the YAML program parser by the .yaml extension and reject the markdown body. Either the create path should reject .yaml/.yml names or write/validate a YAML program for those suffixes.

Useful? React with 👍 / 👎.

Comment thread src/workflows/program/schema.ts Outdated
Comment on lines +67 to +68
/** Step ids: `[A-Za-z0-9][A-Za-z0-9._-]*` (also pinned in the JSON Schema). */
export const PROGRAM_STEP_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Align step IDs with the expression grammar

This pattern accepts step ids that start with digits or contain dots, but step-output references are parsed with readIdent, which requires a letter/underscore first character and stops before dots. In workflows that later need to consume such a step's output, ids like 1build or build.js are valid in the parser/schema but cannot be addressed as ${{ steps.<id>.output }}, so map inputs, routes, and later unit templates become impossible to write without renaming the step.

Useful? React with 👍 / 👎.

Comment thread src/core/json-schema.ts Outdated
Comment on lines +919 to +921
: outcome.text
? JSON.stringify(outcome.text)
: null,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve empty-string unit outputs in the journal

Successful text units that legitimately return an empty string are journaled as NULL here because the check uses truthiness instead of outcome.text !== undefined. On a later resume, reuseCompletedUnit rehydrates that completed row with no text or result, so the promoted step artifact changes from "" on the live run to null on replay, which can break downstream ${{ steps.*.output }} consumers and schema gates.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The live/replay divergence you identified was real and is fixed, but in the opposite direction (37e504c): an empty successful free-text output now normalizes to absent on every surface — live dispatch, journal rehydration on resume, and the R3 workflow report ingest all produce result_json = NULL and the same promoted artifact. Preserving "" was the other consistent option, but "no output" is the more honest reading of an empty result, and it keeps the failed-branch semantics (which already mapped ""→NULL) uniform. The semantics are documented in the executor module doc, and the EMPTY_OUTPUT driver-parity golden plus a chaos test pin all three surfaces to the identical unit graph. Schema-bearing units are unaffected: an empty output there fails schema validation rather than passing a null through.


Generated by Claude Code

Comment thread src/workflows/runtime/workflow-asset-loader.ts Outdated
Comment thread src/workflows/exec/worktree.ts
Comment thread src/workflows/exec/native-executor.ts Outdated
Comment thread src/storage/repositories/workflow-runs-repository.ts Outdated
Comment thread src/integrations/harnesses/copilot/result-extractor.ts Outdated

Copy link
Copy Markdown
Collaborator Author

Codex round-3 findings and the remaining plan items are all landed (head 1eb9a23, CI green).

Round-3 fixes — all 10 findings verified true: engine resume now seeds gate-loop state from the journal via the same shared helpers brief/report use (crash-after-rejection resumes at loop 2 with stored feedback, pinned by a parity golden); report --rerun carries prior attempts' tokens forward so max_tokens holds run-lifetime; a reported failure under on_error: fail finalizes the step immediately (retry-eligible failures excepted); report/brief rehydrate ~r retry rows so a successful engine retry survives a driver handoff; a required gate whose judge throws now blocks instead of failing open; status --units surfaces stale claims; undeclared-param references warn at validate (see the inline thread for why warning rather than rejection); create rejects cross-extension canonical-name collisions; and report --settle (emitted by brief, guarded by lease/CAS/--expect-step) makes route-first workflows drivable externally.

One disposition note on the Claude structured-output finding (review body, no inline thread): fixed in substance — the builder now emits --output-format json plus the shared schema directive for schema-bearing units, with a new result extractor unwrapping the {"type":"result",...} envelope — but the registry claim was corrected from native-schema to native-json: the headless claude -p CLI has no output-schema flag; the JSON envelope is its documented structured path, and the engine's schema validation still runs on top.

Plan completion: the addendum's last outstanding promises are in — workflow.maxConcurrency config knob (CPU-derived default min(16, max(1, cores−2)), explicit values clamped to [1, 64]; drivers unaffected) and the non-fatal validator warnings channel (untyped step artifacts + undeclared param references, surfaced in validate JSON/text and as stderr warnings at start). A ~45-promise audit of the plan and feature docs against the code found everything else holding; the plan doc, workflows.md, STABILITY.md, and the CHANGELOG were reconciled to describe the shipped reality. One non-workflow item recorded for owner triage rather than changed: STABILITY.md's "11 supported asset types" list predates env/secret/session/fact (code has 14) and still names deprecated vault — classifying those is an owner call.


Generated by Claude Code

@itlackey

itlackey commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Manual validation pass on the new workflow feature. I reviewed the docs, workflow-related stash memories/context, the new CLI/exec/runtime code paths, and then ran break-oriented CLI scenarios in an isolated sandbox.

Sandbox/context used:

  • branch: claude/workflow-orchestration-improvements-k39vjo
  • sandbox root: /tmp/opencode/workflow-pr714-manual
  • docs reviewed: docs/features/workflows.md, docs/technical/akm-workflows-orchestration-plan.md
  • targeted tests re-run:
    • bun test tests/commands/workflow-driver-cli.test.ts
    • bun test tests/integration/worktree-isolation.test.ts

Findings

  1. workflow list --active includes blocked runs, despite the flag/docs saying “active only”.
  • src/commands/workflow-cli.ts:218-223 passes activeOnly, but src/storage/repositories/workflow-runs-repository.ts:257-259 filters with status IN ('active', 'blocked').
  • Manual repro: after forcing a required gate to block, bun src/cli.ts workflow list --active returned the blocked run.
  • Impact: scripts using --active can treat blocked runs as still-executable work.
  1. The external-driver claim flow appears to misrender claimed units in workflow brief.
  • In my run, workflow report ... --status running succeeded and created a live claim; finishing without --session-id was correctly refused, proving the claim was enforced.
  • But the next workflow brief still showed the same unit as action: "pending" with a normal completion command, instead of claimed.
  • Intended behavior seems to be in src/workflows/exec/brief.ts:560-561, with unit/journal association built in src/workflows/exec/brief.ts:410-459.
  • Impact: two external drivers can be misled into thinking work is free when it is actually claimed.
  1. Resuming a required-gate-blocked run leaves brief in a weak state for external drivers.
  • After workflow resume, the run became active, but workflow brief showed the only unit as action: "done" with no report command, while the step was still current and the message still said “Execute them, then report each result.”
  • Relevant behavior:
    • src/workflows/exec/brief.ts:334-345
    • src/workflows/exec/brief.ts:598-603
    • src/workflows/exec/brief.ts:611-626
  • Re-reporting the completed unit idempotently re-blocked the gate, so there is a recovery path, but brief does not make it obvious.
  • Impact: external-driver recovery after gate blocks is awkward and easy to strand.
  1. Real workflow run + isolation: worktree completed functionally, but the CLI invocation did not exit cleanly in this environment.
  • I ran a live agent-backed workflow in a sandbox git repo.
  • The run reported completed in about 7 seconds, preserved a dirty isolation worktree correctly, and left the main repo clean.
  • But the shell call stayed alive until the 10-minute tool timeout.
  • I did not root-cause this to one file yet, but it points at process-lifecycle cleanup around live agent dispatch.
  • Impact: high for real usage if reproducible outside this environment, because a successful workflow can still hang the caller.

Validated

  1. External-driver happy path worked end to end.
  • I drove basic-check through brief/report across fan-out review, aggregate, route, and final ship.
  • Routing selected ship from aggregate.output.verdict = "pass" and advanced automatically.
  1. Required-gate blocking worked.
  • A workflow with gate.required: true and no LLM judge blocked exactly as expected on the report surface.
  1. Budget enforcement worked.
  • A max_tokens: 100 workflow failed hard when cumulative reported tokens hit 110, even with on_error: continue.
  1. Lease protection worked.
  • I simulated a live engine lease in workflow.db; both workflow complete and workflow report refused with clear holder/expiry messaging.
  1. Worktree isolation worked functionally.
  • The real agent run created worktree-sentinel.txt in the retained isolation worktree under /tmp/akm-worktrees/.../mutate-solo.
  • The main repo under /tmp/opencode/workflow-pr714-manual/project stayed clean.
  1. Watch output looked good.
  • workflow watch emitted the expected workflow_* NDJSON backlog and terminal envelope.

Open questions

  1. Is --active intended to include blocked, or is the current implementation drifting from the CLI/docs contract?
  2. For external-driver claims, should brief ever show a claimed unit as pending, or is that definitely a bug?
  3. After resume on a gate-blocked run, what is the intended external-driver UX when all units are already done but the gate still needs reevaluation?
  4. Have others seen workflow run hang after success with real agent dispatch, or should I treat this as a likely branch regression?

If useful, I can break these into follow-up issues with minimal repro commands per item.

claude added 3 commits July 8, 2026 02:33
…ed contract

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DptAcDcuW2TF2RfM3a4ok
…mands, settle for terminal steps, SDK server disposal

- `workflow list --active` returns exactly status='active'; blocked runs
  stay visible in the plain list (scope guards were already separate and
  are pinned unchanged).
- brief's report command for a live-claimed unit now carries the claim
  holder (--session-id), so a second driver cannot mistake claimed work
  for free.
- A fully-terminal-but-unfinalized step (e.g. required-gate blocked then
  resumed) is now first-class: brief says so and emits one settle
  command; `report --settle` finalizes it through the shared completion
  path under the finalize CAS.
- Root cause of the post-success CLI hang: the opencode SDK server
  registry cached live `opencode serve` children with teardown only on
  process exit — which the children themselves prevented. The engine now
  drains dispatch resources in its run finally on every exit path.
- Registry made a config leaf again (harness.ts) to fix the TDZ crash the
  disposal export introduced, with a subprocess load-order regression test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DptAcDcuW2TF2RfM3a4ok

Copy link
Copy Markdown
Collaborator Author

Thanks for the break-oriented pass — all four findings reproduced, root-caused, and fixed (head 16562fb). Answers to your open questions:

1. Is --active intended to include blocked? No — that was drift. listRuns was unioning ('active','blocked'); it now filters exactly status='active', and blocked runs stay visible (with their status) in the plain workflow list. The union semantics you might worry about elsewhere were already a separate repository method (findActiveOrBlockedRunForScope, used by the show scope guard), and the start guard was already active-only — both are now pinned by tests so neither can silently drift again.

2. Should brief ever show a claimed unit as pending? Definitely a bug, though a subtler one than it looked: the action mapping itself was rendering claimed correctly in our repro — what made claimed work look free was that the emitted report command omitted --session-id, so any driver copy-pasting it would attempt (and be refused) completion, and nothing signaled the claim in the command itself. The command for a live-claimed unit now carries the claim holder; stale (expired claim) keeps the plain reclaimable form. If you can still reproduce action: "pending" on a live claim after this, that's a repro I'd want — our CLI-level test (start → report --status running → brief) shows claimed with claimedBy/claimExpiresAt.

3. Intended UX after resume on a gate-blocked run? Your instinct was right that the state was underserved. "All units terminal but the step still needs finalization" is now first-class: brief detects it, drops the misleading "Execute them" message, explains what will happen (a required gate with no judge re-blocks; a passing judge completes), and emits exactly one actionable command — workflow report <run> --settle, which now finalizes a fully-terminal step through the same shared completion path under the finalize CAS. Settle still refuses steps with genuinely pending/in-flight/retry-eligible work.

4. The post-success hang — real, root-caused, fixed. The opencode SDK runner caches opencode serve child processes in a per-env registry for reuse, and its only teardown was process.once('exit') — but the live children themselves kept the event loop open, so on a successful run the exit event could never fire: a deadlock until your tool timeout. The engine now drains dispatch resources (closing cached SDK servers) in its run finally on every exit path, with the exit hook kept as backstop. Regression-tested end-to-end through the real dispatch path with a fake server factory (a real opencode serve child isn't spawnable in our sandbox — flagging that honestly; your environment is the true test). Fixing the disposal also surfaced and fixed a latent module-init cycle (TDZ crash when the SDK barrel loads first), now guarded by a subprocess load-order test.

No follow-up issues needed unless #4 still reproduces for you on this head — if it does, that repro would be gold.


Generated by Claude Code

@itlackey

itlackey commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Follow-up retest after the fixes landed.

Pulled latest claude/workflow-orchestration-improvements-k39vjo and reran the workflow-focused validation set.

Automated retest

  • Passed:
    • bun test tests/commands/workflow-driver-cli.test.ts tests/workflows/brief.test.ts tests/workflows/report.test.ts tests/workflows/conformance/driver-parity.test.ts tests/workflows/dispatch-disposal.test.ts tests/workflows/validate-summary.test.ts tests/integration/worktree-isolation.test.ts
    • bun test tests/workflow-cli.test.ts tests/workflows/status-units.test.ts tests/workflows/program-warnings.test.ts tests/workflows/scheduler.test.ts
  • Result: 203 pass, 0 fail

Manual retest in a fresh sandbox

  • Sandbox root: /tmp/opencode/workflow-pr714-repro
  • Revalidated the originally reported areas:
    • claimed units now render as action: "claimed" and inject --session-id
    • workflow list --active excludes blocked runs
    • blocked required-gate recovery now advertises and supports workflow report --settle
    • budget ceilings still fail hard as intended
    • worktree isolation still keeps the base repo clean and journals worktree_path

Additional real-world manual scenarios I added and ran

  • stale claim reclaim:
    • manually aged a claimed running unit in workflow.db
    • workflow brief surfaced it as action: "stale" with staleUnits.length === 1
    • a new driver could reclaim/finalize it
  • scope isolation across directories:
    • started the same workflow from two different working directories under the same sandbox
    • each directory saw only its own active run in workflow list --active
    • scope keys differed as expected
  • watch stream on blocked run:
    • workflow watch <run> --stream --interval-ms 5 exited cleanly
    • trailing envelope reported status: "blocked", streamed: true

Remaining manual caveat

  • I can still reproduce one live-harness issue:
    • bun src/cli.ts workflow run workflow:worktree-proof --max-steps 1
    • functional behavior is correct: run completes, retained worktree is created, base repo stays clean
    • but with the real opencode harness the parent process still stays alive until caller timeout, even though the command already printed a successful completed payload
  • So the review findings I raised earlier look addressed, but I would still treat the live-harness disposal/exit behavior as not fully closed.

Runbook updated

  • Added/expanded: docs/technical/pr-714-workflow-validation-repro.md
  • It now includes:
    • prerequisites
    • exact sandbox setup
    • exact config/workflow asset creation steps
    • automated suites to run
    • manual scenarios with shell variables instead of hardcoded run ids
    • explicit cleanup steps
    • notes on the remaining live-harness hang caveat

If helpful, I can split the remaining live-harness hang into a dedicated follow-up issue with the minimal repro from the runbook.

…r pin the CLI

Registry draining alone was insufficient: the SDK's createOpencodeServer
close() only sends SIGTERM and never unrefs the child or its stdio, so
akm's event loop stayed pinned until the real `opencode serve` process
actually exited — reproducibly hanging the caller after a successful run.

The runner now owns the spawn (createManagedOpencode; the SDK package is
used only for createOpencodeClient): after the URL handshake the child
and its pipes are unref'ed/destroyed, and close() sends SIGTERM with an
unref'ed 2s grace timer escalating to SIGKILL. The spawn stays in the
factory call's synchronous prefix, preserving the env-overlay contract.
Lifecycle proven against real children, including one that ignores
SIGTERM.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DptAcDcuW2TF2RfM3a4ok

Copy link
Copy Markdown
Collaborator Author

Thanks for the retest and the runbook — and especially for confirming the live-harness hang survived the registry drain. That repro pinpointed the real mechanism, now fixed in 8eae5dc:

Why the drain wasn't enough: disposeDispatchResources() was firing correctly, but the SDK's own createOpencodeServer close() only sends SIGTERM — and it never unref()s the child process or its stdio pipes. In Bun/Node, a spawned child's handle keeps the parent's event loop alive until the child actually exits. A real opencode serve (a live HTTP server, often with provider children) can outlive SIGTERM long enough that akm sits pinned on its handle indefinitely — exactly your symptom: successful payload printed, process alive until caller timeout. The fake-factory regression test couldn't see this because a fake has no OS handle.

The fix: the runner now owns the spawn (createManagedOpencode — the SDK package is used only for createOpencodeClient). After the URL handshake, the child and its pipes are unref()ed/destroyed so the handle can never hold akm open; close() sends SIGTERM and arms an unref'ed 2-second grace timer that escalates to SIGKILL. akm's exit is now fully decoupled from how the server child feels about dying. The spawn stays in the factory call's synchronous prefix, so the env-binding overlay contract is unchanged.

Proven against real children this time (tests/integration/opencode-sdk-managed-server.test.ts): a real spawned process that deliberately ignores SIGTERM — handshake resolves, closeServer() returns in <500ms without waiting, the stubborn child is SIGKILLed within the grace window, and the test process itself exits promptly (which it wouldn't with an un-unref'ed live child).

Your runbook's workflow run workflow:worktree-proof --max-steps 1 repro should now exit cleanly with the real binary — that's the one thing our sandbox genuinely can't verify (no opencode here), so a quick re-run on your side would close it out. If it still hangs after this, a dedicated issue with the runbook repro would be the right next step, but I'd bet on this one.


Generated by Claude Code

@itlackey

itlackey commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Follow-up: I implemented the shared spawn-layer fix, reran the regressions, and then reran the manual verification end to end.

What changed

  • Fixed the shared CLI harness spawn cleanup in src/integrations/agent/spawn.ts
    • readStream() now captures, clears, and unref()s its drain timeout
    • the timeout-path SIGKILL grace timer is now unref()'d too
  • Added regression coverage in:
    • tests/architecture/agent-spawn-seam.test.ts
    • tests/agent/agent-spawn.test.ts
  • Updated the manual verification runbook:
    • docs/technical/pr-714-workflow-validation-repro.md

Regression results

  • Passed:
    • bun test tests/architecture/agent-spawn-seam.test.ts tests/agent/agent-spawn.test.ts tests/workflows/dispatch-disposal.test.ts tests/integration/opencode-sdk-managed-server.test.ts
    • bun test tests/workflows/brief.test.ts tests/workflows/report.test.ts tests/integration/worktree-isolation.test.ts
  • Result: 116 targeted tests passing, 0 failures

Manual verification rerun

  • External-driver happy path: passed
  • Required-gate block / resume / settle: passed
  • Budget ceiling: passed
  • Stale claim reclaim: passed
  • Scope isolation across directories: passed
  • workflow watch --stream on blocked run: passed

Live harness matrix after the fix

  • opencode
    • workflow completed
    • retained worktree still journaled
    • command now exits normally
  • codex
    • workflow failed terminally due to local auth (401 Unauthorized)
    • command now exits normally
  • claude
    • workflow failed terminally due to local account state (Credit balance is too low)
    • command now exits normally

So the cleanup hang no longer reproduced in the live harness matrix I reran.

Production-style examples added to the manual workflow
To keep the manual verification grounded in real live usage, the runbook now also includes safe verification of production task/workflow assets from the current stash:

  1. Prompt-backed live task example
  • /home/founder3/akm/tasks/curate-agent-learning.yml
  • drives workflow:curate-to-wiki
  1. Command-backed live task example
  • /home/founder3/akm/tasks/akm-health-report.yml
  • env-injected hourly Discord health report
  1. Converted production orchestration example
  • source markdown workflow: /home/founder3/akm/workflows/web-ux-validation-gate.md
  • converted YAML workflow-program: /home/founder3/akm/workflows/web-ux-validation-gate-program.yaml
  • the new YAML validates cleanly as a workflow program with zero warnings and is documented in the runbook as a production-style orchestration example

Pushed commit

  • 265ee555fix(agent): release workflow-run spawn timers cleanly

One note: the converted web-ux-validation-gate-program.yaml lives in the working stash (/home/founder3/akm/workflows/), not in this repo, so it was used/documented for manual verification but is not part of the git push for this PR.

…mers

The spawn-timer cleanup (265ee55) now routes the stdout/stderr stream-drain
watchdogs through runAgent's injected setTimeoutFn seam, so this test's fake
registers three timers, not one. Its single shared `timerCb` let the later
drain-timer registrations clobber the wrapper-timeout callback, which then
never fired — the hanging spawn wedged the test to its 30s limit. Fire each
callback independently (matching the array-based fakes in the agent-spawn
suites); no production change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DptAcDcuW2TF2RfM3a4ok

Copy link
Copy Markdown
Collaborator Author

Great to see the live-harness matrix (opencode/codex/claude) all exit cleanly now — that closes finding 4. Your spawn-timer commit 265ee55 is integrated on the branch.

It did trip one CI casualty (test-unit (1)): reflect-propose-cli.test.ts's "short --timeout-ms is honoured" hung to 30s. Root cause was in the test, not your fix — that test's fake setTimeoutFn stored a single shared timerCb, but now that readStream routes the two stream-drain watchdogs through the same injected seam, the later drain-timer registrations clobbered the wrapper-timeout callback so it never fired and the hanging spawn wedged. Fixed in ee99d42 by firing each callback independently (matching the array-based fakes your new agent-spawn tests use). No production change; your spawn-seam / agent-spawn / dispatch-disposal / managed-server suites all pass together, and I swept for any other single-timerCb fakes — this was the only one.

The two spawn fixes are complementary and both needed: yours releases the CLI-path drain/SIGKILL timers, mine (8eae5dc) owns the opencode serve spawn so the server child is unref'd and force-killed on close. CI should be green once the new head finishes.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Heads-up on the CI red on 01598ab (test-unit shards 2/3/4) — I dug in but am holding off pushing since it's your active rework. Diagnostic findings:

The failures are all the same class and look environmental, not a hard bug:

  • Failing tests: tests/secret-indexing.test.ts ("secret values never appear in the FTS index…") and tests/parameter-metadata.test.ts ("indexed command entries include parameters in search text") — both 30s timeouts, not assertion failures.
  • Both drive indexing via runCliCapture, which needs an embedder — and embedder downloads are blocked in CI (the known pre-existing flake family: e2e/semantic/zero-config/FTS-ranking).
  • They pass in isolation on 01598ab (2–3s each) and pass running the two files together locally (23 pass, 10s). They only hang in the full 58-file CI shard, and they were green on the prior head ee99d42 — so this is shard-load-dependent, not deterministic.

Most likely mechanism: these blocked-embedding indexing tests already run near the 30s wall under shard resource pressure, and your readStream rewrite (Bun-native new Response(stream).text() → a manual getReader() loop with per-chunk decoder.decode() + Promise.race per chunk) adds a bit of per-spawn drain latency. readStream is on the hot path for every captured subprocess, so a small per-call cost compounds across a shard and can tip borderline-slow indexing spawns over the timeout. The hardening itself (structured StreamReadResult, timeout/error surfacing) is good — it's just the extra per-spawn cost interacting with the already-flaky blocked-embedding tests.

I didn't push anything since you're iterating on these exact files and there's no confident one-line fix — a CI re-run may well go green (flaky), or if you want the throughput back, restoring the Bun-native drain for the non-timeout/non-error path while keeping the structured result would remove the added latency. Happy to take either on if you'd like — just say the word. My two spawn-adjacent commits (8eae5dc managed serve-spawn, ee99d42 reflect test-fake) are unaffected by this.


Generated by Claude Code

itlackey added 2 commits July 8, 2026 02:01
…write files

codex exec defaults to a read-only sandbox that silently blocks file
writes. Without --sandbox workspace-write the unit returns 'workspace
is read-only' and the engine marks the step complete with no real
mutation — a silent no-op.

The builder now injects --sandbox workspace-write after exec and any
profile-supplied args, unless the profile already pins --sandbox/-s.
--ask-for-approval is NOT injected: it only exists on interactive codex,
not codex exec (verified via codex exec --help; attempting it errors
with 'unexpected argument').

Copy link
Copy Markdown
Collaborator Author

Quick note on the test-unit (3) red on 6a93892: it's a flaky pre-existing test, not your codex-builder change. The failure is tests/proposal-storage-sqlite.test.ts › concurrent reject + gate-decision mutation cannot revive a pending row — an assertion failure (143ms, not a hang) in a real-SQLite-WAL concurrency test, an unrelated subsystem to 6a93892 (which only touched codex/agent-builder.ts + its test). I reproduced against your exact head: it passes 8/8 locally (5× isolated + 3× full-file), so it's a WAL timing flake under CI shard load, not a regression.

Safe to re-run or ignore — your next push will re-trigger CI and it'll almost certainly go green (it was green on 19c66c71). I tried to kick a failed-jobs re-run myself but the integration lacks the workflow-rerun permission (403). I didn't touch the flaky test — it's out of this PR's scope and you're actively iterating — but if you'd like it de-flaked (the concurrent reject/gate-decision timing window looks tightenable), say the word and I'll take it on separately.


Generated by Claude Code

itlackey added 4 commits July 8, 2026 11:53
- configuration-agent-profiles.md: codex now has a dedicated builder
  (injects --sandbox workspace-write), not 'none — dispatch errors'
- akm-workflows-orchestration-plan.md: update capability matrix command
  shape and drift note to reflect codex builder
- pr-714-workflow-validation-repro.md: add codex sandbox notes to
  Scenario 8 expected interpretation and results sections

Also accepted stash proposal 4187b809 updating
knowledge:skills/ai-skills/agent-cli-tools/references/codex for
codex-cli 0.142.5.
@itlackey itlackey merged commit 88c74dd into main Jul 8, 2026
12 checks passed
@itlackey itlackey deleted the claude/workflow-orchestration-improvements-k39vjo branch July 8, 2026 20:05
@itlackey

itlackey commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Post-merge triage note (0.9.0): PR #714 merged and closed PR-linked issue #559. Remaining open 0.9.0 milestone items are #711, #692, #672 (all still open after sync/main). I did not find any other open 0.9.0 milestone issues. Work split: #711 tasks multi-stash execution, #692 search salience ranking config/outcome gate, #672 improve prep accumulator + salience/outcome repo/widened X4 boundary; none were addressed by #714.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants