Autoflow — add single-writer orchestration controller - #82
Conversation
Establish one private Autoflow write-authority owner (Decision 060, AUTOFLOW ORCHESTRATION CONTROLLER / SINGLE-WRITER BOUNDARY). This is NOT autonomous orchestration. - AutoflowRuntime.open() enforces the single-active-workflow invariant: OPEN/AWAITING_HUMAN_DECISION reject with WORKFLOW_ALREADY_ACTIVE (the active workflow is never clobbered); CLOSED permits a new open; the pure domain openWorkflow is unchanged. - AutoflowOrchestrator privately owns the write-capable runtime and exposes only open() and reader(); the writer never leaks. - Bounded startup-open input seam (process-scoped env only); no post-start production WorkflowEvent source. - Cockpit receives the read-only reader; loopback/D1/D2/D4/provenance unchanged. No Git/GitHub/network/provider/Policy authority, no persistence/replay. Six strong boundaries byte-identical. Full suite 1857/1857; independent uncommitted-candidate validation PASS. Runtime cutover is a separate gate; live PID remains untouched. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015R3Z9oKuFnRjoY981RV5sb
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (8)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughThe change adds ChangesAutoflow startup flow
Estimated code review effort: 4 (Complex) | ~45 minutes Merge Risk: ⚪ Minimal · up to Startup workflow opening is fail-closed and remains contained behind the orchestrator, while Cockpit receives only read access. No current merge-blocking risk remains. Sequence Diagram(s)sequenceDiagram
participant LiveCockpit
participant StartupConfig
participant AutoflowOrchestrator
participant AutoflowRuntime
LiveCockpit->>StartupConfig: readStartupWorkflowConfig
StartupConfig-->>LiveCockpit: WorkflowBinding or null
LiveCockpit->>AutoflowOrchestrator: open(binding)
AutoflowOrchestrator->>AutoflowRuntime: open(binding)
AutoflowRuntime-->>AutoflowOrchestrator: AutoflowOpenResult
LiveCockpit->>AutoflowOrchestrator: reader()
AutoflowOrchestrator-->>LiveCockpit: AutoflowStateReader
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
@codex review Please perform one substantive review of this PR at exact head Review focus (the actual 8-file diff):
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 571b2ca8f7
ℹ️ 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".
Repairs two Codex P1 findings on PR #82 (Decision 060). P1 #1 (capability containment): AutoflowRuntime.reader() closed over `this` and dispatched through the mutable AutoflowRuntime.prototype.current, so a downstream prototype swap could recover the write-capable runtime. The runtime now holds a private state cell; open/apply/current read/write that one cell, and reader() closes over the cell alone (a plain data read, no prototype dispatch), so the writer is unrecoverable. Domain semantics and the six strong boundaries are unchanged. P1 #2 (fail-closed config): readStartupWorkflowConfig's presence check ignored the optional workflow variables, so an optional-only config (pull-request id or workflow repository id only) silently returned null instead of failing closed. Presence now covers every workflow-open variable; any present makes it a request, and an incomplete request throws. Adds an adversarial prototype-swap reader test and optional-only config cases. Full suite 1862/1862; typecheck/lint/build/diff-check clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015R3Z9oKuFnRjoY981RV5sb
|
@codex review Please perform one substantive re-review of this PR at exact head Focus:
|
|
Codex Review: Didn't find any major issues. 🎉 Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
|
@coderabbitai review Final substantive review at exact current head |
🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
base="843d517a3251e365e84ab0bd31eaaba74cd50f5d"
head="d8b7493604c6206aa0f18a32155d72ecf0c0205a"
printf '%s\n' '=== checked-out HEAD ==='
git rev-parse HEAD
printf '%s\n' '=== requested commits ==='
git rev-parse "$base^{commit}"
git rev-parse "$head^{commit}"
printf '%s\n' '=== changed files ==='
git diff --name-status "$base" "$head"
printf '%s\n' '=== changed-file count ==='
git diff --name-only "$base" "$head" | wc -l
printf '%s\n' '=== complete range diff ==='
git diff --find-renames --find-copies --check "$base" "$head"
git diff --find-renames --find-copies "$base" "$head"
printf '%s\n' '=== changed-file outlines ==='
git diff --name-only "$base" "$head" | while IFS= read -r file; do
printf '\n--- %s ---\n' "$file"
ast-grep outline "$file" --items all || true
done
printf '%s\n' '=== targeted symbols and construction/application sites ==='
rg -n -C 4 --hidden \
-g '!node_modules/**' -g '!dist/**' -g '!build/**' \
'new\s+AutoflowRuntime\s*\(|new\s+AutoflowOrchestrator\s*\(|\.apply\s*\(|\.open\s*\(|\.reader\s*\(|AutoflowRuntime|AutoflowOrchestrator|orchestration-input|AGENTBRIDGE_WORKFLOW_' \
.
printf '%s\n' '=== strong-boundary range changes ==='
git diff --name-only "$base" "$head" -- \
'*producer.ts' \
'*read-model.ts' \
'*evidence-freshness-projection.ts' \
'*autoflow-projection.ts' \
'*domain/workflow.ts' \
'*domain/workflow-transitions.ts'
printf '%s\n' '=== package and entrypoint candidates ==='
fd -HI '^(package\.json|.*\.(ts|tsx|js|mjs|cjs))$' . \
-E node_modules -E dist -E build | head -n 300Length of output: 108746 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '=== package scripts and entrypoint exports ==='
sed -n '1,240p' package.json
printf '%s\n' '--- src/index.ts ---'
cat -n src/index.ts
printf '%s\n' '=== live runtime composition ==='
sed -n '1,270p' src/runtime/live-cockpit.ts
printf '%s\n' '=== cockpit server loopback configuration ==='
sed -n '1,260p' src/cockpit-host/server.ts
printf '%s\n' '=== repository observer authority surface ==='
sed -n '1,260p' src/runtime/repository-observer.ts
printf '%s\n' '=== runtime and domain workflow status definitions ==='
sed -n '1,250p' src/autoflow/runtime.ts
ast-grep outline src/domain/workflow.ts --items all
ast-grep outline src/domain/workflow-transitions.ts --items all
rg -n -C 5 --glob 'src/**/*.ts' 'WORKFLOW_STATUS|status:|CLOSED|AWAITING_HUMAN_DECISION|OPEN' src/domain/workflow.ts src/domain/workflow-transitions.ts
printf '%s\n' '=== production-only Autoflow writer construction and calls ==='
rg -n -C 5 --glob 'src/**/*.ts' \
'new\s+AutoflowRuntime\s*\(|new\s+AutoflowOrchestrator\s*\(|\b[A-Za-z_$][A-Za-z0-9_$]*\.apply\s*\(|\b[A-Za-z_$][A-Za-z0-9_$]*\.open\s*\(|\.reader\s*\(' \
src
printf '%s\n' '=== direct imports of runtime/orchestrator in production source ==='
rg -n -C 3 --glob 'src/**/*.ts' \
'from .*\bautoflow/(runtime|orchestrator)\.js' \
src
printf '%s\n' '=== workflow event sources and external-capability imports in changed production files ==='
rg -n -C 3 \
'WorkflowEvent|HEAD_OBSERVED|HUMAN_GATE_OPENED|CLOSE_REQUESTED|setInterval|setTimeout|node:child_process|node:fs|node:https|node:http|fetch\s*\(|GitHub|octokit|Policy|provider' \
src/autoflow/orchestrator.ts src/autoflow/runtime.ts src/runtime/live-cockpit.ts src/runtime/orchestration-input.ts
printf '%s\n' '=== exact unchanged strong-boundary blob identity ==='
for path in \
src/cockpit-snapshot/producer.ts \
src/cockpit/read-model.ts \
src/cockpit/evidence-freshness-projection.ts \
src/cockpit/autoflow-projection.ts \
src/domain/workflow.ts \
src/domain/workflow-transitions.ts
do
printf '%s ' "$path"
git rev-parse "843d517a3251e365e84ab0bd31eaaba74cd50f5d:$path"
git rev-parse "d8b7493604c6206aa0f18a32155d72ecf0c0205a:$path"
doneLength of output: 93925
I found no actionable issues. The review confirmed these points:
✅ Action performedReview finished.
|
Autoflow Orchestration Controller / Single-Writer Boundary (Decision 060)
This is NOT autonomous orchestration. It establishes the single-writer boundary and a bounded startup-open path; real post-start event adapters remain separate future gates.
What this does
AutoflowOrchestrator): it privately owns the write-capableAutoflowRuntimeand exposes onlyopen()andreader(). The writer never leaks.AutoflowRuntimeprevents replacement of an active workflow:open()rejects withWORKFLOW_ALREADY_ACTIVEwhilecurrent()isOPENorAWAITING_HUMAN_DECISION(the active workflow is never clobbered). The pure domainopenWorkflowis unchanged.CLOSEDis allowed — one active workflow at a time, not one per process lifetime. No workflow history is retained (in-memory limitation).AGENTBRIDGE_WORKFLOW_ID,AGENTBRIDGE_WORKFLOW_BOUND_COMMIT_SHA, optionalAGENTBRIDGE_WORKFLOW_PULL_REQUEST_ID; repository identity must equal the runtime identity). Absent → no workflow; partial/mismatch/malformed → fail closed.RepositoryObserver, D1/D2/D4, fixture rules, and source provenance are all unchanged.What this deliberately does NOT do
WorkflowEventsource (no autonomousHEAD_OBSERVED/HUMAN_GATE_OPENED/CLOSE_REQUESTED/invocation/review/evidence events).current() === null; no recovery is claimed.Boundaries & validation
producer.ts,read-model.ts,evidence-freshness-projection.ts,autoflow-projection.ts,domain/workflow.ts,domain/workflow-transitions.ts.git diff --checkall pass.Scope note
Runtime cutover to this composition is a separate gate; the live runtime (PID 23880 on
127.0.0.1:4317) remains untouched.🤖 Generated with Claude Code
https://claude.ai/code/session_015R3Z9oKuFnRjoY981RV5sb
Summary by CodeRabbit
New Features
Bug Fixes