Syncs the post-acceptance state from lithoscomputer/code-review: the
wiring simplification pass, the smoke-variant inputs the shared graph's
publish_pr node now requires, the hunk-header parsing hardening, the
root-commit diff base, and the verified-missing retry. These include the
fixes for what the publisher's own first live run reported on PR #815.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WqM6MiUp32js5YrbiW777k
Syncs the workflow from lithoscomputer/code-review: the SARIF renderer,
the deterministic PR publisher (publish_pr.py plan/apply plus the opt-in
publish_pr graph node and post_pr inputs), the GitHub permissions grant
that has Fabro inject a scoped GITHUB_TOKEN, and a planted-bug probe
fixture so this refresh commit itself yields inline-postable findings
for the publisher's live acceptance run.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WqM6MiUp32js5YrbiW777k
Share the RunIntent shape between the scheduler and the API trigger via
AutomationRunMaterialized::into_run_intent, drop the pass-through
packaging wrappers and the unreachable VersionIdMismatch error, and move
the config-path and version-ID derivations onto WorkflowVersion so the
server, validator, and collector stop re-deriving them.
The collector now owns the collected sources (moving file contents
instead of cloning them), resolves the workflow location once, and shares
the not-found probe with build_run_manifest. The bundler reads a goal
file once instead of twice.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Revert the run summary -> run record rename. The SQLite store is still
the summary read model today; it only grows an inactive events table
here. Renaming it now made the store file show as a delete plus add and
touched nine unrelated files. The final rename happens once, when the
SQL store becomes the run authority.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Share one bind helper across the runs insert/upsert/update statements,
compute the next event sequence once per append, and decode stored
sequence columns through a single helper. Check the run head before
decoding events, rewrite the first-visit stage listing as a UNION ALL so
each arm uses its partial index, and share the run_events insert SQL
with the test seeder.
Collapse the duplicated in-memory pool fixture, remove two tests that
only asserted Arc sharing, and fold the fabro-db test row helpers.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The openai_compatible stream decoder grew its tool call accumulator with
empty placeholder entries whenever a delta arrived with a sparse index,
then emitted every slot as a real tool call at finish. A provider that
numbers tool_calls[].index wrongly (Venice's Anthropic translation
passes through content-block positions, so a first tool call after text
arrives with index 1) therefore produced a phantom tool call with an
empty id and name. The phantom poisoned the conversation: the agent
answered it with a tool error, and the next request was rejected by the
provider (400: tool_use.id must match '^[a-zA-Z0-9_-]+$'), failing the
run as a non-retryable deterministic error.
A gap in the index sequence is indistinguishable from lost chunks, so
the decoder now fails the stream with a clear error naming the provider
and index instead of fabricating a tool call. Error::Stream is
classified retryable, so stage retries resample the turn rather than
replaying a poisoned history.
Observed on run 01M11JZVT7V507R56BCJJHZB1B; reproduced against the live
Venice API on claude-opus-5 and claude-sonnet-5 (four non-Claude models
stream index 0 correctly) and reported to Venice.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C6JBmbpi2NeZXNEsftAhzd
- Materializer derives the manifest GitContext from RunTarget::validate()
instead of hand-building it and re-parsing the repository slug
- Drop parse_github_repository_slug and InvalidRepositorySlug, now unused
- Store reuses Automation::git_target() instead of a private duplicate
- Legacy TOML import returns the target directly rather than a tuple
- Automation target migration updates columns with a single UPDATE ... FROM
- Web: share gitTarget(), targetFromFormValues(), and one SHA validator
across the automation form, list, detail, new, and edit views
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Introduce a PinnedRevision enum in clone_source so the Docker and Daytona
providers run one fetch/checkout/verify sequence for both an exact commit
and a tag instead of two near-identical arms. Fold the tag-specific
command builders into the generic ones, share the bare-ref grammar check
between branch and tag validation, and derive the workflow clone source
from the validated Git target in a single match.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A failed node with an effective `succeed` policy and no explicit recovery
route now finishes as `succeeded` and follows normal success routing. The
original failure stays on the outcome so the stage.completed event and the
checkpoint keep the diagnostic, and the outcome notes record which scope
promoted it.
- OnFailure gains a Succeed variant; Node::on_failure resolves the
deprecated auto_status=true attribute as an alias, with an explicit
on_failure winning
- The core executor applies the policy before the lifecycle observes the
result, so the recorded outcome, context keys, goal gates, events, and
routing all see the effective outcome; this replaces AutoStatusLifecycle
- Explicit routes take priority: a matching condition, preferred label,
suggested next node, or handler jump keeps the outcome failed. A failed
outcome takes an unconditional edge only under route, so under succeed
any edge selection is an explicit route
- succeed applies only to failed, matching exit; the auto_status alias no
longer promotes partially_succeeded
- Parallel branches promote after their retry loop, so a failed succeed
branch counts as succeeded in the parent aggregate
- Validation accepts succeed and adds an auto_status_deprecated warning
that suggests on_failure="succeed"
- Document the policy table, semantics, and deprecation; add a changelog
entry
Closes#807
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Both commands always registered the MCP client entry under the fixed
`mcpServers` key `fabro`, so users could not register separate Fabro
servers (for example production and testing) without editing the client
JSON by hand.
`--name <NAME>` now selects the `mcpServers` key. It defaults to `fabro`
for backward compatibility and rejects empty values. `fabro mcp init`
upserts only the named entry and preserves entries with other names, so
reusing a name updates that entry in place.
Closes#808
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>