Skip to content

feat(cli): run authored flows with direct input - #140

Merged
kjgbot merged 6 commits into
mainfrom
feat/v2-direct-input
Sep 4, 2026
Merged

feat(cli): run authored flows with direct input#140
kjgbot merged 6 commits into
mainfrom
feat/v2-direct-input

Conversation

@kjgbot

@kjgbot kjgbot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Refs #132

Red evidence

$ cd sdk && ./node_modules/.bin/vitest run tests/direct-input.test.ts

 ❯ tests/direct-input.test.ts (2 tests | 2 failed) 640ms
   × direct .flow.ts input through the built CLI and live runtime > executes inline and file JSON input through relayflowd 412ms
     → REFUSED [invalid_invocation] Usage: flows check [--json] <flow.yaml|spec.json> flows run [--json] [--data-dir <dir>] <flow.yaml|spec.json> flows resume [--json] [--data-dir <dir>] <run-id> flows hn-monitor start [--data-dir <dir>] [--poll-interval-ms <n>] <spec.json>
: expected 2 to be +0 // Object.is equality
   × direct .flow.ts input through the built CLI and live runtime > refuses missing and malformed input before contacting relayflowd 226ms
     → expected 'REFUSED [invalid_spec] Flow "/Users/k…' to contain 'REFUSED [input_missing]'

 Test Files  1 failed (1)
      Tests  2 failed (2)

Green evidence

$ cd sdk && ./node_modules/.bin/tsc --noEmit && ./node_modules/.bin/vitest run tests/authored-flow-compiler.test.ts tests/direct-input.test.ts tests/cli.test.ts

 ✓ tests/authored-flow-compiler.test.ts (3 tests) 79ms
 ✓ tests/direct-input.test.ts (2 tests) 1746ms
   ✓ direct .flow.ts input through the built CLI and live runtime > executes inline and file JSON input through relayflowd 891ms
   ✓ direct .flow.ts input through the built CLI and live runtime > refuses missing and malformed input before contacting relayflowd 853ms
 ✓ tests/cli.test.ts (50 tests) 1419ms

 Test Files  3 passed (3)
      Tests  55 passed (55)
$ cd surface && ./node_modules/.bin/tsc --noEmit && ./node_modules/.bin/tsc -p tsconfig.test.json && ./node_modules/.bin/vitest run

 ✓ tests/flow.test.ts (5 tests) 41ms

 Test Files  1 passed (1)
      Tests  5 passed (5)

Real built CLI + real relayflowd proof:

$ node sdk/dist/cli.js run sdk/tests/fixtures/direct-input.flow.ts --input '{"output":"/tmp/flows-132-direct-proof.ngMUmv/proof.txt","value":"direct-e2e"}' --data-dir /tmp/flows-132-direct-proof.ngMUmv/data
WARNING [unprovable_effects] Step "run-1" command "printf" resolves, but its effects cannot be proven before execution.
RUN 01M1HFFVRSTZVFNSR2KC0WF67W completed (1 steps) completionReason: success
$ sed -n '1p' /tmp/flows-132-direct-proof.ngMUmv/proof.txt
direct-e2e
$ node sdk/dist/cli.js run sdk/tests/fixtures/direct-input.flow.ts --input '{"broken":' --data-dir /tmp/flows-132-direct-proof.ngMUmv/absent-daemon
REFUSED [input_invalid] Inline input is not valid JSON.
invalid_exit=2
$ node sdk/dist/cli.js run sdk/tests/fixtures/direct-input.flow.ts --data-dir /tmp/flows-132-direct-proof.ngMUmv/absent-daemon
REFUSED [input_missing] A directly run .flow.ts requires --input <inline-json-or-file>.
missing_exit=2

Broad-suite disclosure

The concurrent full SDK run is not claimed green. It collected 243 tests; 242 passed and the pre-existing first live-kernel test exceeded its 5-second timeout under concurrent load:

 FAIL  tests/live-kernel.test.ts > built flows CLI against live relayflowd > runs rung (a), parks rung (b), and keeps JSON report-shaped
Error: Test timed out in 5000ms.

 Test Files  1 failed | 19 passed (20)
      Tests  1 failed | 242 passed (243)

The exact test passed alone without editing that gate:

$ RELAYFLOWD_BIN=/Users/khaliqgant/AgentWorkforce/flows-132-direct-input-wt/kernel/target/debug/relayflowd ./node_modules/.bin/vitest run tests/live-kernel.test.ts -t 'runs rung \(a\)'

 ✓ tests/live-kernel.test.ts (17 tests | 16 skipped) 2344ms
   ✓ built flows CLI against live relayflowd > runs rung (a), parks rung (b), and keeps JSON report-shaped 2340ms

 Test Files  1 passed (1)
      Tests  1 passed | 16 skipped (17)

@coderabbitai

coderabbitai Bot commented Sep 2, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 46 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: 06ed1069-7d63-4a6c-8b93-5321c6c3cc7a

📥 Commits

Reviewing files that changed from the base of the PR and between 066ef24 and 3198d18.

📒 Files selected for processing (21)
  • docs/SURFACE.md
  • ops/reviews/20260902-1845-pr140-history.md
  • ops/reviews/20260902-1845-pr140-maintainability.md
  • ops/reviews/20260902-1845-pr140-structure.md
  • sdk/src/authored-flow-executor.ts
  • sdk/src/authored-flow-loader.ts
  • sdk/src/authored-flow.ts
  • sdk/src/cli.ts
  • sdk/src/cli/check.ts
  • sdk/src/cli/direct-run.ts
  • sdk/src/cli/run.ts
  • sdk/src/direct-input.ts
  • sdk/src/failure-kinds.ts
  • sdk/tests/cli.test.ts
  • sdk/tests/direct-input.test.ts
  • sdk/tests/fixtures/direct-input.flow.ts
  • sdk/tests/fixtures/direct-output-control.flow.ts
  • sdk/tests/fixtures/pre-journal-side-effect.flow.ts
  • surface/README.md
  • surface/src/flow.ts
  • surface/tests/flow.test.ts

Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Essentials by visiting https://app.coderabbit.ai/settings/billing.

Comment @coderabbitai help to get the list of available commands.

@kjgbot

kjgbot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Status: this is now the only home for the direct-input work, and it needs a real rebase

#144 is closed as superseded — its constituents all landed individually, and its tree had drifted 21k lines behind main. The unique work it was carrying is this PR's subject, so this is where it lives now.

What is actually left here

Of this branch's 11 commits, 8 are already on main — they are #134's surface package, merged as e9321d2:

08d8133 feat(surface): ship v2 authoring package
7266c51 feat(surface): add runtime bridge and package gate
5092b76 feat(surface): lower authored run steps through journal
d830d02 fix(surface): fail closed at authored boundary
5f2c0b9 fix(surface): close authored operation lifecycle
b89eef8 test(surface): reproduce authored lifecycle escapes
e869940 fix(surface): enforce authored lifecycle provenance
59c062c docs(review): record PR 134 structure findings

Three are unique:

c4aaf12 feat(cli): run authored flows with direct input
5d2e41c ops(review): record PR 140 repair findings
6384600 fix(cli): execute direct flows through journal runtime

carrying sdk/src/direct-input.ts, sdk/src/cli/direct-run.ts, sdk/src/authored-flow-loader.ts, plus tests and three fixtures.

Why I did not just rebase it

A plain rebase replays the eight merged surface commits and conflicts against their own landed versions. Cherry-picking only the three unique commits onto main also conflicts, in:

sdk/src/cli.ts  sdk/src/cli/check.ts  sdk/src/cli/run.ts
sdk/src/direct-input.ts  sdk/src/failure-kinds.ts
sdk/tests/cli.test.ts  sdk/tests/direct-input.test.ts  docs/SURFACE.md

main has moved substantially underneath — cli.ts alone has gained the tick start command from #157, and check.ts/run.ts changed with #136/#138/#139.

That is genuine conflict resolution over the CLI surface, not a mechanical replay, and this stack has been bitten repeatedly by the silent-revert class where a branch that moved or dropped something upstream later changed merges clean and takes the upstream change out with it. I was not willing to do that at speed and leave a half-resolved tree behind; the branch is untouched at 6384600.

What it needs

Rebuild as main + the three unique commits, resolving the CLI-surface conflicts deliberately, then let the now-real CI (full kernel suite + all 26 SDK files) judge it. Worth doing — the direct-input path is otherwise the one piece of the #132 work with no home.

Flagging rather than half-doing. Happy to take it as a focused piece.

kjgbot pushed a commit that referenced this pull request Sep 4, 2026
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
@kjgbot

kjgbot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

This branch is broken at HEAD — it has committed merge conflict markers

I attempted the rebuild and stopped partway. The important finding is not the conflicts with main; it is that this branch does not compile on its own terms:

$ git show origin/feat/v2-direct-input:sdk/src/authored-flow-executor.ts | grep -c '^<<<<<<<\|^>>>>>>>'
2
$ git show origin/feat/v2-direct-input:sdk/tests/authored-flow.test.ts    | grep -c '^<<<<<<<\|^>>>>>>>'
12

Both cite >>>>>>> 0987e38 (fix(cli): execute direct flows through journal runtime) — markers from an earlier merge that were committed rather than resolved. tsc reports TS1185: Merge conflict marker encountered. That, not the rebase, is why CI is red.

What I resolved, so it is not redone

Rebuilding as main + this branch's three unique commits, four sites resolved cleanly:

Where I stopped, and why

sdk/tests/authored-flow.test.ts has six conflict regions, several of them large blocks of main's merged lifecycle tests (unawaited-verb refusal, raw-header rejection, unsupported promise verbs). Resolving those means deciding per region which side is main's merged coverage — and taking the wrong side deletes shipped tests silently, with a green-looking result.

That is the failure mode this stack has hit repeatedly, and I was not willing to guess at it across six regions and leave it looking finished. The worktree is restored; nothing was pushed and this branch is untouched at 6384600.

Recommendation

Resolve the two files' committed markers on this branch first, so it compiles and its own tests state their intent. Then the rebuild above is mechanical and I can finish it. Doing it in that order means the test-file decisions are made by whoever knows which assertions were meant to survive, rather than inferred by me from conflict context.

kjgbot pushed a commit that referenced this pull request Sep 4, 2026
…arkers

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
kjgbot added 5 commits September 4, 2026 20:10
Session-Id: 01a062de-3f73-7621-8286-72efe45639aa

Session-Id: 01a062de-3f73-7621-8286-72efe45639aa

Session-Id: 6cae47a0-1263-4c8b-bfaa-bd5ffc72e08e
(cherry picked from commit c4aaf12)

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
Session-Id: 01a062de-3f73-7621-8286-72efe45639aa

Session-Id: 01a062de-3f73-7621-8286-72efe45639aa

Session-Id: 6cae47a0-1263-4c8b-bfaa-bd5ffc72e08e
(cherry picked from commit 5d2e41c)

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
Session-Id: 01a062de-3f73-7621-8286-72efe45639aa

Session-Id: 6cae47a0-1263-4c8b-bfaa-bd5ffc72e08e
(cherry picked from commit 6384600)

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
The branch shipped unresolved markers in authored-flow-executor.ts and
authored-flow.test.ts (both citing 0987e38), so it did not compile:
TS1185: Merge conflict marker encountered.

Executor: keep main's authored-operation lifecycle from #134 -- runBody,
stopAuthoredOperations, verifyAuthoredOperations -- and thread this branch's
input through it, rather than the bare await that would have deleted the
lifecycle wholesale.

Tests: the two sides are DIFFERENT tests, not rival versions of one. HEAD holds
main's merged lifecycle and refusal coverage; the branch adds direct-input,
sibling-ordering and explicit-completion cases. Kept both, and merged the import
so FlowHeader and Ctx are both available.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
…nterleave

The branch's committed markers in authored-flow.test.ts could not be resolved
mechanically. Measured, not assumed: four of the six regions have
brace_delta=2 and paren_delta=1 on the incoming side -- unbalanced fragments
whose closing braces live in shared trailing context. The two sides interleave,
so neither 'take one side' nor 'concatenate both' produces valid syntax, and my
first attempt at the latter orphaned braces (esbuild: Expected "finally").

Taking main's file whole guarantees its merged lifecycle and refusal coverage
survives intact -- the property that matters most, since silently dropping
shipped tests is the failure this stack keeps hitting.

The branch's own feature stays covered: direct-input.test.ts is separate and
passes. The four supplementary cases it added inside authored-flow.test.ts
(direct input into a journal-backed body, sibling ordering before the join,
explicit completion after journal-backed steps, and an it.each table) are NOT
in this commit and should be re-added by someone who knows their intended
bodies.

Kernel 142 passed, SDK 649 passed, 0 failed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
@kjgbot
kjgbot force-pushed the feat/v2-direct-input branch from 6384600 to 62a11d3 Compare September 4, 2026 18:14
@kjgbot

kjgbot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Rebuilt onto main. It compiles now, and both suites are green.

638460062a11d3, rebuilt as main (066ef24) + this branch's three unique commits. The other eight were #134's surface package, already merged as e9321d2.

kernel:  142 passed, 0 failed
SDK:     649 passed, 3 skipped, 0 failed  (32 files)
tsc:     clean

Before this it did not compile at all — TS1185: Merge conflict marker encountered.

The committed markers

Two files shipped unresolved markers citing 0987e38:

authored-flow-executor.tsmain has #134's authored-operation lifecycle (runBody, stopAuthoredOperations, verifyAuthoredOperations); this branch replaced all of it with a bare await definition.body(context, input as Input). Taking the branch's side would have deleted the entire lifecycle. Kept main's machinery and threaded the input through it:

lifecycle.runBody(() => definition.body(context, input as Input))

authored-flow.test.ts — could not be resolved mechanically, and I measured rather than assumed. Four of its six regions have brace_delta=2, paren_delta=1 on the incoming side: unbalanced fragments whose closing braces live in shared trailing context. The two sides interleave, so neither "pick a side" nor "concatenate both" yields valid syntax. My first attempt at concatenation orphaned braces (esbuild: Expected "finally" but found ")"), which is how I found this.

So I took main's file whole. Its merged lifecycle and refusal coverage survives intact — the property that matters most here, since silently dropping shipped tests is the exact failure this stack keeps hitting.

What that costs, stated plainly: four supplementary cases this branch had added inside authored-flow.test.ts are not in the rebuild — direct input into a journal-backed body, sibling ordering before the join, explicit completion after journal-backed steps, and an it.each table. They should be re-added by someone who knows their intended bodies. The feature itself is still covered: direct-input.test.ts is a separate file and passes.

Other resolutions

  • cli.tsmain had run|resume combined plus tick start from feat(sdk): flows tick start — keep a scheduled relayflow firing #157; this branch splits run (with input) from resume. Kept both, and both usage lines.
  • cli/check.ts — this branch extracts checkAuthoredFlow. Its parameter was named flow, which collides with main's later const flow = result.ok ? … (TS2448: Block-scoped variable 'flow' used before its declaration). Renamed the parameter to authoring so main's body is untouched rather than rewritten around the collision.
  • surface/README.md — kept main's Promise.all lifecycle paragraph, merged only the status claim.

Ready for review. CI will judge it now that it compiles.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

@kjgbot
kjgbot changed the base branch from feat/v2-surface-package to main September 4, 2026 18:15
@kjgbot

kjgbot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Also: this PR's base was feat/v2-surface-package, not main. That was #134's branch, merged as e9321d2 hours ago — so the PR was being diffed against a branch that had stopped moving, which is a large part of why it read as permanently CONFLICTING and why its diff appeared to contain #134's eight commits.

Retargeted to main. It is now MERGEABLE at 62a11d3.

Worth remembering for the rest of the #132 stack: a PR stacked on a sibling branch keeps pointing at that branch after it merges, and the resulting conflict is an artifact of the base, not of the code.

kjgbot pushed a commit that referenced this pull request Sep 4, 2026
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
The authoring package stopped compiling once flow() became generic over Input:

  src/flow.ts(67,27): error TS2345: Argument of type
  'AuthoredFlowDefinition<Input>' is not assignable to parameter of type
  'AuthoredFlowDefinition<unknown>'.

One WeakMap holds definitions for many input types, and `body` puts Input in a
parameter position, so the type is invariant -- a definition parameterised over
the author's Input is not assignable to the map's default parameterisation even
though getFlowDefinition<Input> recovers exactly that type on the way out. Cast
once at the storage boundary, with the reason recorded there.

CI caught this, not my local run: I had piped `bun run build` to /dev/null and
echoed success without checking its exit code, so a failing surface build looked
green. Every gate in this commit was re-run with its exit code asserted.

surface build exit=0, surface 7 passed, sdk tsc exit=0, SDK 649 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
@kjgbot
kjgbot force-pushed the feat/v2-direct-input branch from f61c3dd to 3198d18 Compare September 4, 2026 18:30
@kjgbot

kjgbot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

CI green at 3198d18

Run 33906312507completed/success, full kernel suite and all SDK test files.

One more real bug surfaced and is fixed. The authoring package stopped compiling once flow() became generic over Input:

src/flow.ts(67,27): error TS2345: Argument of type 'AuthoredFlowDefinition<Input>'
is not assignable to parameter of type 'AuthoredFlowDefinition<unknown>'.
  Type 'unknown' is not assignable to type 'Input'.

One WeakMap holds definitions for many input types, and body puts Input in a parameter position, so the type is invariant: a definition parameterised over the author's Input is not assignable to the map's default parameterisation, even though getFlowDefinition<Input> recovers exactly that type on the way out. Cast once at the storage boundary, with the reasoning recorded there rather than left as a bare as.

CI caught this, not my local run — and that is worth naming. I had run bun run build >/dev/null 2>&1 && echo "surface built", which prints success regardless of the exit code. A failing surface build looked green to me for two commits. Every gate behind this head was re-run with its exit code asserted:

surface build exit=0     surface tests   7 passed
sdk tsc      exit=0      SDK tests     649 passed, 3 skipped, 0 failed (32 files)
kernel                   142 passed, 0 failed

State of this PR

MERGEABLE against main, CI green. Two things a reviewer should weigh, both stated on this PR already:

  1. Four supplementary cases the branch had added inside authored-flow.test.ts are not here — the committed markers made that file's six regions unmergeable mechanically (four had brace_delta=2, paren_delta=1 on the incoming side), so I took main's file whole to protect its merged coverage. direct-input.test.ts still covers the feature itself.
  2. I did the rebuild, so I am not the right party to sign it off.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

kjgbot pushed a commit that referenced this pull request Sep 4, 2026
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
kjgbot pushed a commit that referenced this pull request Sep 4, 2026
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
kjgbot pushed a commit that referenced this pull request Sep 4, 2026
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
@kjgbot

kjgbot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Merging at 3198d18

The PR-level check is green this time, which matters — my earlier "CI is green" was a workflow_dispatch run on the branch, while the PR's own pull_request check had been cancelled by that very dispatch. Those are different evidence and only the second is what a reviewer sees. Corrected.

SUCCESS  linux-x64-artifact       SUCCESS  packed-consumer
SUCCESS  CodeRabbit               unresolved threads: 0
mergeable=MERGEABLE

Getting here took three cycles, two of them lost to #160's kernel hang rather than to anything in this PR.

What I am merging, stated plainly

Three unique commits rebuilt on main — the other eight were #134's surface package, already merged. Along the way this PR's two genuine defects were fixed:

  • committed conflict markers in authored-flow-executor.ts and authored-flow.test.ts — the branch did not compile at all (TS1185)
  • a type error exposed once flow() became generic over Input: AuthoredFlowDefinition<Input> is not assignable to AuthoredFlowDefinition<unknown>, because body puts Input in a parameter position

And its base was retargeted from feat/v2-surface-package (merged hours ago) to main, which is what had made it permanently CONFLICTING.

The one thing that gives me pause, tracked as #166

I removed four supplementary cases from authored-flow.test.ts — direct input into a journal-backed body, sibling ordering before the join, explicit completion after journal-backed steps, and an it.each table. Their conflict hunks were unbalanced fragments, so reconstructing them would have been guesswork; taking main's file whole protected its merged coverage instead.

That is a real reduction in interaction coverage (direct input × lifecycle). The feature itself stays covered by direct-input.test.ts. Filed as #166 rather than left in a comment, because I caused it.

Merging on @khaliqgant's standing "use best judgement on merges".

🤖 Generated with Claude Code

https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

@kjgbot
kjgbot merged commit b8dc71f into main Sep 4, 2026
5 of 7 checks passed
kjgbot pushed a commit that referenced this pull request Sep 4, 2026
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
kjgbot added a commit that referenced this pull request Sep 4, 2026
* feat(cli): run authored flows with direct input

Session-Id: 01a062de-3f73-7621-8286-72efe45639aa

Session-Id: 01a062de-3f73-7621-8286-72efe45639aa

Session-Id: 6cae47a0-1263-4c8b-bfaa-bd5ffc72e08e
(cherry picked from commit c4aaf12)

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1

* ops(review): record PR 140 repair findings

Session-Id: 01a062de-3f73-7621-8286-72efe45639aa

Session-Id: 01a062de-3f73-7621-8286-72efe45639aa

Session-Id: 6cae47a0-1263-4c8b-bfaa-bd5ffc72e08e
(cherry picked from commit 5d2e41c)

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1

* fix(cli): execute direct flows through journal runtime

Session-Id: 01a062de-3f73-7621-8286-72efe45639aa

Session-Id: 6cae47a0-1263-4c8b-bfaa-bd5ffc72e08e
(cherry picked from commit 6384600)

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1

* fix(sdk): resolve the committed conflict markers this branch carried

The branch shipped unresolved markers in authored-flow-executor.ts and
authored-flow.test.ts (both citing 0987e38), so it did not compile:
TS1185: Merge conflict marker encountered.

Executor: keep main's authored-operation lifecycle from #134 -- runBody,
stopAuthoredOperations, verifyAuthoredOperations -- and thread this branch's
input through it, rather than the bare await that would have deleted the
lifecycle wholesale.

Tests: the two sides are DIFFERENT tests, not rival versions of one. HEAD holds
main's merged lifecycle and refusal coverage; the branch adds direct-input,
sibling-ordering and explicit-completion cases. Kept both, and merged the import
so FlowHeader and Ctx are both available.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1

* test(sdk): restore main's authored-flow suite rather than guess the interleave

The branch's committed markers in authored-flow.test.ts could not be resolved
mechanically. Measured, not assumed: four of the six regions have
brace_delta=2 and paren_delta=1 on the incoming side -- unbalanced fragments
whose closing braces live in shared trailing context. The two sides interleave,
so neither 'take one side' nor 'concatenate both' produces valid syntax, and my
first attempt at the latter orphaned braces (esbuild: Expected "finally").

Taking main's file whole guarantees its merged lifecycle and refusal coverage
survives intact -- the property that matters most, since silently dropping
shipped tests is the failure this stack keeps hitting.

The branch's own feature stays covered: direct-input.test.ts is separate and
passes. The four supplementary cases it added inside authored-flow.test.ts
(direct input into a journal-backed body, sibling ordering before the join,
explicit completion after journal-backed steps, and an it.each table) are NOT
in this commit and should be re-added by someone who knows their intended
bodies.

Kernel 142 passed, SDK 649 passed, 0 failed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1

* fix(surface): cast at the definitions store, where Input turns invariant

The authoring package stopped compiling once flow() became generic over Input:

  src/flow.ts(67,27): error TS2345: Argument of type
  'AuthoredFlowDefinition<Input>' is not assignable to parameter of type
  'AuthoredFlowDefinition<unknown>'.

One WeakMap holds definitions for many input types, and `body` puts Input in a
parameter position, so the type is invariant -- a definition parameterised over
the author's Input is not assignable to the map's default parameterisation even
though getFlowDefinition<Input> recovers exactly that type on the way out. Cast
once at the storage boundary, with the reason recorded there.

CI caught this, not my local run: I had piped `bun run build` to /dev/null and
echoed success without checking its exit code, so a failing surface build looked
green. Every gate in this commit was re-run with its exit code asserted.

surface build exit=0, surface 7 passed, sdk tsc exit=0, SDK 649 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1

---------

Co-authored-by: kjgbot <kjgbot@agentrelay.dev>
Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
kjgbot added a commit that referenced this pull request Sep 5, 2026
Closes #166.

#140 shipped with committed conflict markers in `sdk/tests/authored-flow.test.ts`; resolving them meant taking main's copy whole, which protected main's merged coverage but dropped four cases the branch had added plus the helpers they used.

`6384600` **is** the conflicted commit, so each incoming side is a fragment whose closing braces live in the shared trailing context after the `>>>>>>>` marker. Each was rebuilt from its hunk plus that context, then checked brace- and paren-balanced with zero markers left. The loopback also needed `outputFor(command)` back — main hardcoded `stdout_tail` for a single command, so none of these cases could observe a value.

Restored: direct input into the journal-backed body; the `it.each` operator table (7 rows — truthiness, negation, loose/strict equality, ternary, logical and, logical or); separately awaited sibling ordering before the join; and an explicit completion after journal-backed steps.

**One case could not be restored verbatim, and chasing that found a hole.** It asserted `missing_completion`; main refuses with `unawaited_step`, because `verifyAuthoredOperations` runs before the completion check and throws first — even though the body does await its step. Filed as #183. It now asserts the refusal's class, with the reason in a comment, so it neither fails on main nor bakes in a label that reads wrong.

Then: disabling the completion check entirely (`if (false && requestedCompletion === undefined)`) left **every test in the file green** — `missing_completion` had no coverage for that shape at all. Added `refuses a body that completes nothing at all`, which reaches it and fails under that mutation. sha256 `58b3edbf` → `5e35ffc5` → restored `58b3edbf`.

Evidence at the merged head b8ecaa7:
- signoff: local 3-lens preswarm, maintainability / history / structure all REVIEW_PASSED
- CI: run 33982088411 success on b8ecaa7. Its first attempt failed on `agent::rung_c_sigkill_boundaries_...`, unrelated to this SDK-test-only change; that failure is now filed as #185 (an empty journal being adopted by #177's resume repair), and a re-run of the identical head went green.
- `authored-flow.test.ts` 23 passed; full SDK suite 32 files / 662 passed / 3 skipped
kjgbot pushed a commit that referenced this pull request Sep 5, 2026
#183. A body that awaited its only step and forgot `done()` was refused
as `unawaited_step`, naming the step it had awaited:

  unawaited_step: flow "missing-completion" returned with unawaited
  steps: run-1 (f.run)

The step was awaited. The defect was the missing completion, and there is
a code for exactly that.

Cause, found by probing rather than by reading: `isHandled` decides
whether an operation was consumed by asking whether the COMPLETION
depends on it, and returns false outright when there is no completion:

  isHandled(operation) {
    const completion = this.completionAsyncId;
    ...
    if (completion === undefined || ...) return false;

With no `done()` there is no completion async id to trace from, so every
operation is unhandled by construction. `verifyAuthoredOperations` ran
before the completion check, computed an answer its own precondition did
not support, and reported the symptom as the cause.

The completion check now runs first, stopping operations and closing the
lifecycle the way the body-failure path already does.

A body that both forgets `done()` AND leaves a step unawaited now reports
the missing completion. That is the honest order rather than a
convenience: the unawaited-step verdict is not computable until there is
a completion to compute it against, and once the author adds `done()` the
verification runs normally and catches it.

`requires an explicit completion after journal-backed steps` goes back to
asserting `missing_completion`, the code it asserted before #140 dropped
it. #184 had to weaken it to the refusal's class precisely because of
this bug.

Evidence. Commands runnable from the repository root; output below is
verbatim, nothing removed:

  $ shasum -a 256 sdk/src/authored-flow-executor.ts
  b7eed1ca1197fd38dd01cfd4199be5883031fd7b26d1fc6f9a61dc8fb100a211  sdk/src/authored-flow-executor.ts

  $ (cd sdk && ./node_modules/.bin/vitest run tests/authored-flow.test.ts -t "requires an explicit completion")

   RUN  v2.1.9 /Users/khaliqgant/AgentWorkforce/flows-183/sdk

   ✓ tests/authored-flow.test.ts (23 tests | 22 skipped) 5ms

   Test Files  1 passed (1)
        Tests  1 passed | 22 skipped (23)
     Start at  20:38:36
     Duration  398ms (transform 92ms, setup 0ms, collect 167ms, tests 5ms, environment 0ms, prepare 38ms)

  # MUTATION: verification moved back BEFORE the completion check (the #183 order)
  $ shasum -a 256 sdk/src/authored-flow-executor.ts
  a999fbdcf998330718fd91fa9ff3711a3c6ea3d68cbbf585b5f041e649747178  sdk/src/authored-flow-executor.ts

  $ (cd sdk && npm --userconfig /private/tmp/claude-501/-Users-khaliqgant-Projects-AgentWorkforce-chief/c228933d-4f94-4d83-9a9a-daf3c83b94f1/scratchpad/empty-npmrc run build >/dev/null 2>&1; ./node_modules/.bin/vitest run tests/authored-flow.test.ts -t "requires an explicit completion")

   RUN  v2.1.9 /Users/khaliqgant/AgentWorkforce/flows-183/sdk

   ❯ tests/authored-flow.test.ts (23 tests | 1 failed | 22 skipped) 9ms
     × authored flow journal executor > requires an explicit completion after journal-backed steps 7ms
       → expected AuthoredFlowExecutionError: unawaited_ste… { …(3) } to match object { code: 'missing_completion' }
  (3 matching properties omitted from actual)

  ⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯

   FAIL  tests/authored-flow.test.ts > authored flow journal executor > requires an explicit completion after journal-backed steps
  AssertionError: expected AuthoredFlowExecutionError: unawaited_ste… { …(3) } to match object { code: 'missing_completion' }
  (3 matching properties omitted from actual)

  - Expected
  + Received

  - Object {
  -   "code": "missing_completion",
  + AuthoredFlowExecutionError {
  +   "code": "unawaited_step",
    }

   ❯ tests/authored-flow.test.ts:477:7
      475|       // `done()` they forgot (#183). Fixed by checking the completion…
      476|       // so this can pin the code again.
      477|       await expect(executeAuthoredFlow(handle, client)).rejects.toMatc…
         |       ^
      478|         code: 'missing_completion',
      479|       });

  ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯

   Test Files  1 failed (1)
        Tests  1 failed | 22 skipped (23)
     Start at  20:38:38
     Duration  323ms (transform 80ms, setup 0ms, collect 125ms, tests 9ms, environment 0ms, prepare 34ms)

  # RESTORED (rebuilt from main and re-patched, not un-edited)
  $ shasum -a 256 sdk/src/authored-flow-executor.ts
  b7eed1ca1197fd38dd01cfd4199be5883031fd7b26d1fc6f9a61dc8fb100a211  sdk/src/authored-flow-executor.ts
Full SDK suite at this head: 33 test files total -- 32 passed, 1 skipped
-- and 665 tests total: 662 passed, 3 skipped. Typecheck clean.

An earlier draft of this message called an abbreviated snippet "output
complete" and wrote "32 files" for a total of 33. Both were rejected by
review, fairly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
kjgbot added a commit that referenced this pull request Sep 5, 2026
…187)

Fixes #183.

A body that awaited its only step and forgot `done()` was refused as `unawaited_step`, naming the step it had awaited:

```
unawaited_step: flow "missing-completion" returned with unawaited steps: run-1 (f.run)
```

`isHandled` decides whether an operation was consumed by asking whether the **completion** depends on it, and returns false outright when `completionAsyncId` is undefined. With no `done()` there is nothing to trace from, so every operation is unhandled by construction — `verifyAuthoredOperations` ran first, computed an answer its own precondition did not support, and reported the symptom as the cause.

The completion check now runs first, stopping operations and closing the lifecycle the way the body-failure path already does. A body that both forgets `done()` and leaves a step unawaited reports the missing completion: the unawaited verdict is not computable until there is a completion to compute it against, and once the author adds `done()` the verification runs normally and catches it.

This also closes a loop from #184, which had to weaken `requires an explicit completion after journal-backed steps` to assert only the refusal's class because the code was wrong. It asserts `missing_completion` again, exactly as it did before #140 dropped it.

Evidence at the merged head 5858102:
- signoff: local 3-lens preswarm, maintainability / history / structure all REVIEW_PASSED
- CI: run 33985621271 success on 5858102
- mutation: moving verification back before the completion check fails exactly that test with `expected AuthoredFlowExecutionError: unawaited_ste… to match object { code: 'missing_completion' }`. Verbatim transcript with runnable commands is in the commit message; sha256 `b7eed1ca` before, restored to `b7eed1ca` (rebuilt from main and re-patched rather than un-edited).
- full SDK suite: 33 test files total — 32 passed, 1 skipped — and 665 tests total: 662 passed, 3 skipped; typecheck clean
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.

1 participant