continue_adjusting/revert/retry_after_revert all return to
build.initial_plan without resetting the already-built graph. Re-walking
find_resources -> confirm_resources -> approve_plan then re-emits the same
fixed create_node/connect intents, and the second apply_repair raised
ValueError on the colliding node id, dead-ending the session at
build.plan_approval.
Fix: before applying, read the current draft graph and drop any
create_node whose node_id already exists or connect whose (from, to) edge
already exists, so a re-approve after a loop-back is a no-op apply_repair
rather than a crash. built_node_ids is still set from the full 4-node set
regardless of what was actually applied this pass.
Adds an end-to-end loop test via the real Runner covering both the
continue_adjusting and retry_after_revert paths, asserting the session
reaches build.execution with the graph still at 4 nodes / 3 edges.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Completes Slice 1 Task 9's 'widen all DifyPort implementers' invariant — the
whole-branch review caught this third conformance stub that the per-task
reviews (aware of only WorkflowServiceDifyPort + FakeDifyPort) missed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add contract_gen.py: a pure, deterministic generator that discovers every
StrEnum/dataclass/card defined in contract.py (plus EntryMode,
ConversationItem, and SessionView) via introspection and emits the checked-in
contract_schema.json (JSON Schema draft 2020-12) and
web/.../contract/types.ts. The discriminated ConversationItem union is built
from _Card.__subclasses__(), one oneOf/union branch per card kind.
test_contract.py gains a lockstep test that byte-compares a fresh
generate() call against the checked-in files (guards drift/hand-edits) and a
jsonschema.validate test that proves the generated schema is itself valid
and accepts a real SessionView instance.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The add_workflow_copilot_tables migration (b28a1b2fbf4d) kept down_revision=e4708db55c1d after the branch was rebased onto main, forking the Alembic graph into two heads and letting a fresh DB skip the main lineage (e.g. workflows.version_number), which 500s the editor /workflows/draft. Re-point down_revision to the main head (56124e050600) so the chain is linear.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
create_fix_session now treats failed_run_id as the Dify workflow-run id and
records it as an immutable original-failed CopilotRun, pointing
fc.failed_run_id at that row so the async diagnose can resolve it. Closes the
FE->diagnose gap for a live Fix flow.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>