Commit graph

43 commits

Author SHA1 Message Date
Bryan Helmkamp
15a1188bb9
Keep engine fix in separate pull request 2026-08-28 15:32:01 -04:00
Bryan Helmkamp
c2ac1a8500
Fix large parallel review inputs 2026-08-28 15:07:39 -04:00
Bryan Helmkamp
7416745cd7
Harden code review workflow release 2026-08-28 14:32:33 -04:00
Bryan Helmkamp
81d714d8b7
Add focused publisher canary fixture 2026-08-28 14:14:29 -04:00
Bryan Helmkamp
1c35efb495
Refresh structured code review comments 2026-08-28 13:53:59 -04:00
Bryan Helmkamp
afa7298071
Refresh the code-review workflow (bodyless review posts)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WqM6MiUp32js5YrbiW777k
2026-08-28 11:04:30 -04:00
Bryan Helmkamp
aa09341f4e
Refresh the code-review workflow (acceptance-run fixes)
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
2026-08-28 10:04:40 -04:00
Bryan Helmkamp
9827efaaa6
Refresh the code-review workflow (SARIF output, P1 PR publisher)
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
2026-08-28 09:22:50 -04:00
Bryan Helmkamp
1ad5d16af3
Refresh the code-review workflow (cross-target cell packing, 12-check cap) 2026-08-26 22:13:41 -04:00
Bryan Helmkamp
14c99e8f34
Refresh the code-review workflow (conventions filter, duplicate folding) 2026-08-26 20:53:55 -04:00
Bryan Helmkamp
0fea550842
Install the code-review workflow for calibration runs 2026-08-26 19:39:38 -04:00
Bryan Helmkamp
e80be78c81
chore(workflows): use Claude Opus 2026-07-30 07:01:23 -04:00
Bryan Helmkamp
7ff153d222
chore: bump verify timeout to 20m 2026-07-23 15:21:56 -04:00
Bryan Helmkamp
30d770046a
chore(workflows): use xhigh for implement-plan 2026-07-23 12:46:36 -04:00
Bryan Helmkamp
b505833ccd
Update implement-plan workflow models 2026-07-22 22:51:35 -04:00
Bryan Helmkamp
b765940f8c
Track implement-plan simplify prompt 2026-07-22 19:50:31 -04:00
Scott Werner
8c3f035ea9
Add pr-simplify workflow for automated PR simplify passes (#557)
## What

Adds `pr-simplify`, a Fabro workflow that runs a "simplify" code-review
pass over an existing PR and updates that same PR in place.

## How it works

- **One agent, three parallel reviews.** A single agent node runs the
pass and uses `spawn_agent` to fan out three reviewers — code reuse,
code quality, and efficiency — concurrently, then aggregates their
findings. Sub-agent results return directly to the orchestrator, which
is the clean way to aggregate multiple perspectives. (A fork +
`tripleoctagon` fan-in was the wrong primitive here: fan-in selects a
single "best" branch and merges only its worktree, so it would silently
drop two of the three reviews.)
- **Updates the existing PR — no new PR.** The agent runs `gh pr
checkout` on the PR's branch, applies the fixes, commits, and pushes —
landing one fixup commit on the existing PR, plus a summary comment and
a `simplify:<model>` label. `[run.pull_request] enabled = false` keeps
Fabro from opening a second PR from its run branch.
- **Fable by default, overridable.** The graph sets
`default_model=claude-fable-5`, which floors the orchestrator and all
three reviewers to Fable. `--model <id>` wins over it per run
(`configured model → graph default_model → catalog default`), and the
label reflects whatever actually ran.

## Usage

```bash
fabro run pr-simplify -I pr=<number>              # Fable (default)
fabro run pr-simplify -I pr=<number> --model gpt-55   # override the model
```

Requires GitHub token permissions `contents` / `pull_requests` /
`issues` = write (declared in the workflow) so it can push the commit,
comment, and label.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: Bryan Helmkamp <19+brynary@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Bryan Helmkamp <19+brynary@users.noreply.github.com>
2026-07-08 12:01:41 -04:00
Scott Werner
b13f5362a5
Add patch-cves workflow for Dependabot alert triage (#559)
Adds a `patch-cves` workflow that triages GitHub Dependabot alerts and
opens verified dependency-patch PRs, one per alert group. Intended to be
driven by a scheduled automation targeting this repo.

## What's included

- **`.fabro/workflows/patch-cves/workflow.fabro`** — single agent stage
pinned to `claude-opus-4-8`.
- **`.fabro/workflows/patch-cves/prompts/patch-cves.md`** — the bundled
prompt with the full CVE-patching procedure: query Dependabot alerts,
rank and group them, choose the smallest safe fix, patch + regenerate
lockfiles, verify (local gates + GitHub checks), and re-query alerts.
Ecosystem rules cover Rust/Cargo and TypeScript/Bun (Bun only — never
npm/npx/yarn/pnpm). Treats all advisory/package/log text as untrusted
data.
- **`.fabro/workflows/patch-cves/workflow.toml`** — requests the GitHub
App installation-token permissions the run needs:
`vulnerability_alerts=read`, `contents=write`, `pull_requests=write`,
`checks=read`. Sets `run.pull_request.enabled = false` so fabro's
run-branch finalization PR doesn't race the per-group PRs the agent
opens directly via `gh`.

## Design

The instructions ship as a bundled prompt file
(`@prompts/patch-cves.md`) that travels in the run manifest, so the
workflow is fully self-contained — no external skill or runtime
discovery involved.

Validated with `fabro validate patch-cves` (OK).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 21:13:24 -04:00
Bryan Helmkamp
d228ad3e02
chore: add demo workflows 2026-06-04 18:54:23 -04:00
Bryan Helmkamp
ec1b3f2084
feat(sandbox): secure daytona snapshot names (#429)
## Summary

Secures Daytona custom snapshot creation by removing user-controlled
snapshot/image references and replacing them with deterministic names
Fabro computes internally. Docker image selection now uses
`image.docker`, while Daytona only accepts `image.dockerfile` for custom
snapshots and continues to use `daytona-medium` when no Dockerfile is
configured.

## Changes

- Replaces public `image.ref` config/API shape with Docker-specific
`image.docker` across Rust settings, OpenAPI, generated TypeScript
client, docs, defaults, examples, and web samples.
- Adds Daytona snapshot identity generation using HMAC-SHA256 over a
canonical manifest keyed by the Daytona API key, producing
`fabro-<uuid>` snapshot names without exposing Dockerfile text or key
material.
- Routes Daytona custom Dockerfiles, including devcontainer-generated
Dockerfiles, through the same computed identity path before calling
Daytona snapshot APIs.
- Updates sandbox initialization events and store projections so
initialized run state can show the resolved image and computed Daytona
snapshot after startup.
- Updates legacy config migration behavior so Docker image refs map to
`image.docker`, while Daytona legacy snapshot names are not preserved.

## Breaking Changes

- `image.ref` is no longer accepted in new environment config.
- Docker environments should use `image.docker` for image selection.
- Daytona environments reject `image.docker`; use `image.dockerfile` to
request a custom computed snapshot.

## Verification

- `cargo build -p fabro-api`
- `cd lib/packages/fabro-api-client && bun run generate`
- `cd lib/packages/fabro-api-client && bun run typecheck`
- `cd apps/fabro-web && bun run typecheck`
- `cargo +nightly-2026-04-14 fmt --check --all`
- `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D
warnings`
- `ulimit -n 4096 && cargo nextest run --no-fail-fast -p fabro-cli -p
fabro-config -p fabro-sandbox -p fabro-workflow -p fabro-store -p
fabro-server -p fabro-api`
- `cargo insta pending-snapshots`

---

[![Compound
Engineering](https://img.shields.io/badge/Compound_Engineering-6366f1)](https://github.com/EveryInc/compound-engineering-plugin)
🤖 Generated with GPT-5 via [Codex](https://openai.com/codex)
2026-05-27 11:52:35 -04:00
Bryan Helmkamp
c20c6b5361
chore: update goal workflow 2026-05-27 08:06:56 -04:00
Bryan Helmkamp
8102c11919
chore(workflows): use gpt-55 xhigh for goal workflow
Switches the goal workflow's work and audit nodes from the default
claude-sonnet to gpt-55 with xhigh reasoning, matching the implement
node in implement-plan.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 22:43:33 -04:00
Bryan Helmkamp
4faa34059d
feat(workflow): add goal workflow (#407)
## Summary

Adds a reusable `goal` workflow that runs an immutable user goal through
a Work -> Completion Audit loop. The work prompt keeps the full
objective intact, while the audit prompt uses validated routing JSON to
either exit when the goal is proven complete or loop back with concrete
remaining work.

## Workflow Diagram

![Goal workflow
diagram](https://raw.githubusercontent.com/fabro-sh/fabro/2069a692e3d681c41afbfeca0ee86a604151475a/.fabro/workflows/goal/workflow.svg)

## Verification

- `cargo run -q -p fabro-cli -- validate
.fabro/workflows/goal/workflow.fabro`
- `cargo run -q -p fabro-cli -- run goal --goal "Test the reusable goal
workflow" --dry-run`
- `cargo run -q -p fabro-cli -- preflight
.fabro/workflows/goal/workflow.toml --goal "Test the reusable goal
workflow"`
- `xmllint --noout .fabro/workflows/goal/workflow.svg`

---

[![Compound
Engineering](https://img.shields.io/badge/Compound_Engineering-6366f1)](https://github.com/EveryInc/compound-engineering-plugin)
Generated with GPT-5 via Codex
2026-05-25 21:57:02 -04:00
Bryan Helmkamp
1c2abbb2f5
Configure implement-plan sandbox tooling (#377)
Fixes implement-plan runs failing at verify time when cloned sandboxes
lack Git identity, and prevents the verify forbidden-pattern scan from
being silently skipped when `rg` is unavailable.

## Changes
- Install `ripgrep` in the Fabro Daytona image and bump the snapshot ref
to `fabro-v12` so Daytona rebuilds it.
- Configure repository-local Git `user.name` and `user.email` from
`run.git.author` during workflow initialization before lifecycle setup
commands or workflow stages run.
- Make the implement-plan verify stage fail explicitly if `rg` is
missing.

## Validation
- `cargo test -p fabro-workflow
configure_sandbox_git_identity_uses_run_author --quiet`
- `cargo +nightly-2026-04-14 fmt --check --all`
- `cargo +nightly-2026-04-14 clippy -p fabro-workflow --all-targets --
-D warnings`
- `cargo run -p fabro-cli -- validate
.fabro/workflows/implement-plan/workflow.fabro`

---

[![Compound
Engineering](https://img.shields.io/badge/Compound_Engineering-6366f1)](https://github.com/EveryInc/compound-engineering-plugin)
🤖 Generated with GPT-5 via [Codex](https://openai.com/codex)
2026-05-23 19:21:28 -04:00
fabro-sh-0530[bot]
7f84ac5e3f
Replace run-scoped sandbox config with named environments (#360)
## Summary

Replaces the `[run.sandbox]` configuration surface with a named,
provider-explicit environment catalog. Runs now select an environment by
slug (`[run.environment] id = "..."`) rather than configuring a sandbox
inline. Fabro resolves the catalog through normal settings precedence,
applies sparse run-level overrides, and creates a concrete sandbox from
the resolved environment.

This is a clean break — no `[run.sandbox]` compatibility layer.

### Plan Summary

- **New config shape:** Top-level `[environments.<slug>]` catalog valid
in `settings.toml`, `.fabro/project.toml`, and `workflow.toml`. Runs
reference a slug via `[run.environment] id = "..."` with optional sparse
overrides under `[run.environment.*]`.
- **Unified environment fields:** `provider`, `image` (ref +
dockerfile), `resources` (cpu/memory/disk), `network` (mode + allow
CIDRs), `lifecycle` (preserve/stop_on_terminal/auto_stop), `labels`,
`volumes`, `env` — replacing the previous split between `[run.sandbox]`,
`[run.sandbox.docker]`, `[run.sandbox.daytona]`, and
`[run.sandbox.daytona.snapshot]`.
- **OpenAPI schema update:** `RunSandboxSettings`, `DockerSettings`,
`DaytonaSettings`, and `DaytonaNetworkLayer` replaced with
`RunEnvironmentSettings`, `EnvironmentSettings`, `EnvironmentProvider`,
`EnvironmentImageSettings`, `EnvironmentResourcesSettings`,
`EnvironmentNetworkSettings`, `EnvironmentLifecycleSettings`, and
`EnvironmentVolumeSettings`.
- **CLI flag rename:** `--sandbox <provider>` → `--environment <slug>`
on `run`, `create`, `preflight`, and `server start/restart`.
- **Provider capability model:** Hard errors for security properties a
provider cannot enforce (local with blocked/CIDR networking; docker with
CIDR allow-lists). Warnings for unsupported resource limits, volumes,
labels, auto-stop, and Docker Dockerfiles.
- **Docs and internal code updated** throughout: `.fabro/project.toml`,
workflow configs, all public docs, CLI args, manifest builders, and the
runner's GitHub credentials check.

### Provider mapping

| Environment field | Local | Docker | Daytona |
|---|---|---|---|
| `image.ref` | Ignored | Docker image | Snapshot name |
| `image.dockerfile` | Ignored | Warning; ignored | Snapshot Dockerfile
(requires `image.ref`) |
| `resources.cpu/memory/disk` | Warning; ignored | cpu_quota / memory
limit / warning | Snapshot sizing |
| `network.mode = block` | **Error** | `network_mode = none` | Daytona
block |
| `network.mode = cidr_allow_list` | **Error** | **Error** | Daytona
CIDR allow-list |
| `labels` | Warning; ignored | Warning; ignored | Daytona labels |
| `volumes` | Warning; ignored | Warning; ignored | Daytona volume
mounts |
| `lifecycle.auto_stop` | Warning; ignored | Warning; ignored | Daytona
auto-stop interval |
| `env` | Process env overlay | Container env | Sandbox env |


### Fabro Details

<details>
<summary>Ran 11 stages in 217m 39s for $129.86</summary>

| Stage | Duration | Cost | Retries |
|---|---|---|---|
| start | 0s | – | 0 |
| toolchain | 1s | – | 0 |
| preflight_compile | 4m 7s | – | 0 |
| preflight_lint | 4m 9s | – | 0 |
| fix_lints | 3m 46s | $1.06 | 0 |
| implement | 76m 6s | $57.39 | 0 |
| simplify_opus | 71m 50s | $38.17 | 0 |
| simplify_gpt | 8m 27s | $2.24 | 0 |
| verify | 6m 10s | – | 0 |
| fixup | 42m 1s | $31.00 | 0 |
| fmt | 3s | – | 0 |
| **Total** | **217m 39s** | **$129.86** | **0** |

</details>

<details>
<summary>Ran <code>ImplementPlan.fabro</code> (12 nodes and 15
edges)</summary>

```dot
digraph ImplementPlan {
    graph [
        goal="Implement and simplify",
        model_stylesheet="
            * { model: claude-opus-4-7; }
        "
    ]
    rankdir=LR

    start [shape=Mdiamond, label="Start"]
    exit  [shape=Msquare, label="Exit"]

    toolchain         [label="Toolchain", shape=parallelogram, script="command -v cargo >/dev/null || { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && sudo ln -sf $HOME/.cargo/bin/* /usr/local/bin/; }; cargo --version 2>&1", max_retries=0]
    preflight_compile [label="Preflight Compile", shape=parallelogram, script="cargo check -q --workspace 2>&1", max_retries=0]
    preflight_lint    [label="Preflight Lint", shape=parallelogram, script="cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1", max_retries=0]
    fix_lints         [label="Fix Lints", prompt="The preflight lint step failed. Read the build output from context and fix all clippy lint warnings.", max_visits=3]
    implement         [label="Implement", prompt="Read the plan file referenced in the goal and implement every step. Make all the code changes described in the plan. Use red/green TDD.", model="gpt-55", reasoning_effort="xhigh"]
    simplify_opus     [label="Simplify (Opus)", prompt="@prompts/simplify.md"]
    simplify_gpt      [label="Simplify (GPT-55)", prompt="@prompts/simplify.md", model="gpt-55"]
    verify            [label="Verify", shape=parallelogram, script="cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1 && cargo nextest run --cargo-quiet --workspace --status-level fail 2>&1 && cargo dev docs refresh 2>&1 && cargo dev docs check 2>&1", goal_gate=true, retry_target="fixup"]
    fixup             [label="Fixup", prompt="The verify step failed. Read the build output from context and fix all clippy lint warnings, test failures, and generated docs errors.", max_visits=3]
    fmt               [label="Format", shape=parallelogram, script="cargo +nightly-2026-04-14 fmt --all 2>&1", max_retries=0]

    start -> toolchain
    toolchain -> preflight_compile [condition="outcome=succeeded"]
    toolchain -> exit
    preflight_compile -> preflight_lint [condition="outcome=succeeded"]
    preflight_compile -> exit
    preflight_lint -> implement [condition="outcome=succeeded"]
    preflight_lint -> fix_lints
    fix_lints -> preflight_lint
    implement -> simplify_opus -> simplify_gpt -> verify
    verify -> fmt   [condition="outcome=succeeded"]
    verify -> fixup
    fixup -> verify
    fmt -> exit
}

```

</details>

⚒️ Generated with [Fabro](https://fabro.sh)

---------

Co-authored-by: Fabro <noreply@fabro.sh>
Co-authored-by: Bryan Helmkamp <bryan@brynary.com>
Co-authored-by: Bryan Helmkamp <bhelmkamp@users.noreply.github.com>
2026-05-23 13:03:21 -04:00
Bryan Helmkamp
a64a58d567
fix: gate implement-plan PRs on CI checks 2026-05-23 10:23:56 -04:00
Bryan Helmkamp
37e527a115
chore: use GPT-55 for implement-plan implementation 2026-05-22 20:38:42 -04:00
Bryan Helmkamp
178adf15ea
feat(web): add Context tab to the stage detail view (#340)
## What

Adds a **Context** tab to the stage detail view
(`/runs/:id/stages/:stageId`), beside the existing primary tab (Thread /
Logs / …) and Debug tab.

It surfaces a stage's *deliberate per-visit outputs* — the data the
workflow author makes a stage write into shared context, plus the
routing hints it emitted:

- **Routing** — `preferred_label` and `suggested_next_ids`
- **Context writes** — author-set `context_updates` keys

This data flow was previously invisible in the UI, which made it hard to
debug "why did the next stage get the wrong input / take the wrong
edge".

## Why no backend change

The per-visit `stage.completed` event already carries `context_updates`,
`preferred_label`, and `suggested_next_ids`, and the web UI already
fetches it via `useRunStageEvents`. The checkpoint's `node_outcomes` map
was rejected as a source: it is keyed by `node_id` only, so it is lossy
across visits (`implement@2` would overwrite `implement@1`).

## How

- `extractStageContext` (in `stage-renderers/helpers.ts`) reads the
`stage.completed` event and filters `context_updates` through an
engine-key denylist: `last_stage`, `last_response`, `response.*`,
`internal.*`, `current.*`, `command.output`, `human.gate.*`,
`parallel.*`. Those are bookkeeping or already shown in the stage's
primary tab.
- It returns `null` when nothing is left, so the tab stays
**conditional** — same pattern as Thread/Logs. It only appears when a
stage actually wrote something deliberate.
- New `stage-context.tsx` renders the result, reusing `CodeBlock` /
`JsonBlock`.
- `run-stages.tsx` gains a dynamic `availableTabs` list; `effectiveTab`
falls back to `primary` gracefully when the Context tab is absent.

## Verification

- `bun run typecheck` clean, `bun test` — 412 pass / 0 fail (4 new tests
for the denylist + routing extraction).
- Live run `01KS5WBZAE7K8321NHR7KFAHF9` (`context-demo` workflow): the
`emit@1` stage emitted `demo.greeting` / `demo.answer` / `demo.payload`
plus `preferred_label: "Done"` and `suggested_next_ids: ["exit"]`, and
the Context tab rendered them correctly.

## Notes

- The second commit adds a small `context-demo` workflow used for that
verification — kept separate so it can be dropped independently.
- Known limitations (acceptable for v1): data is read from
`stage.completed` only, so a stage ending in `stage.failed` shows no
tab; parallel stages write `parallel.*` directly to context
(denylisted), so they show no tab.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 20:19:55 -04:00
Bryan Helmkamp
b4c7ab5800
feat(workflows): add daytona-medium sandbox check 2026-05-11 13:28:10 -04:00
Bryan Helmkamp
d84e7a28ef
feat(workflows): add interview workflow
Add a progressive human interview workflow and teach human gates to honor explicit question_type values so the workflow can exercise yes/no, confirmation, multiple-choice, multi-select, and freeform prompts before summarizing the answers.
2026-05-08 07:44:33 -07:00
Bryan Helmkamp
5ed9e9134f
feat(workflows): add sleeper interrupt workflow 2026-05-08 07:44:33 -07:00
Bryan Helmkamp
10f5eac1d2
chore: add gh-list workflow 2026-05-06 07:15:18 -04:00
fabro-sh-0530[bot]
e40dc7d9ad
Move GitHub token permissions to [run.integrations.github.permissions] (#215)
## Summary

Token scopes describe what *a run* is authorized to do, not server
identity. Today they live under
`[server.integrations.github.permissions]`, which can't be overridden by
`workflow.toml` / `project.toml` (server keys are stripped from
per-workflow layers) — so projects and workflows can't tighten or relax
permissions despite the docs already advertising a per-run config. This
PR moves them under `[run.integrations.github.permissions]`, where the
standard layer-merge (workflow > project > user > defaults) Just Works.
Greenfield, no migration shim.

## What changed

- **New layer/resolved types** in `fabro-config` and `fabro-types`:
`RunIntegrationsLayer`, `RunIntegrationsGithubLayer`, and resolved
counterparts. `permissions` becomes a flat `HashMap<String,
InterpString>` post-resolve; empty = no token requested.
- **Server schema**: `permissions` removed from `GithubIntegrationLayer`
/ `GithubIntegrationSettings`. `deny_unknown_fields` rejects the stale
path.
- **Bundled `workflow.toml` parsing** (`run_manifest.rs`): now goes
through `SettingsLayer` via the new `parse_run_layer_from_settings_toml`
helper, so stale `[server.integrations.github.permissions]` errors
instead of being silently dropped by the old `toml::Table` lift-out.
- **Consumers updated**: server preflight, run launch path, and the CLI
worker (`runner.rs`) all read run-level permissions. CLI worker
previously hardcoded `HashMap::new()` — runs launched via the local CLI
path were getting no `GITHUB_TOKEN` regardless of TOML.
- **Shared helpers** on `RunIntegrationsGithubSettings`:
`is_token_requested()` and `resolve_permissions(lookup)` so server and
CLI don't drift.
- **OpenAPI + TS client** regenerated; new `RunIntegrationsSettings` /
`RunIntegrationsGithubSettings` schemas added, `permissions` removed
from `GithubIntegrationSettings`.
- **Repo workflows + docs** rewritten to the new path. Docs gain a
security-model note (boundary = installation grants; no Fabro-side cap).

## Key design decision: hand-rolled `Combine` for
`RunIntegrationsGithubLayer`

`ReplaceMap`'s "empty inherits from below" semantics (`maps.rs:76-80`)
are wrong here — we want `permissions = {}` in a higher layer to act as
an explicit clear. So the layer field is `Option<HashMap<...>>` with
hand-rolled `Combine`:

| Higher layer | Lower layer | Result |
|---|---|---|
| `None` | anything | lower (inherit) |
| `Some(map)` | anything | `Some(map)` (full replace, including
`Some({})` = clear) |

Not derived: the blanket `Option<T: Combine>` impl would recurse into
the inner `HashMap` and reintroduce empty-fallback. Documented inline in
`layers/run.rs`.

`InterpString` is preserved through resolve and only flattened to
`String` at the start-services boundary, matching the existing pattern.

### Plan Summary

- New `[run.integrations.github.permissions]` layer + resolved types;
remove from server side.
- Hand-rolled `Combine` so empty-wins-as-clear; no change to
`ReplaceMap` semantics for other consumers.
- Strict `SettingsLayer` parse for bundled `workflow.toml` so stale
schema errors loudly.
- Both server and CLI worker paths read run-level permissions via shared
helpers.
- OpenAPI + TS client regenerated; parity test added.
- Repo workflow TOMLs and `integrations/github.mdx` rewritten.


### Fabro Details

<details>
<summary>Ran 0 stages in 61m 23s for $53.41</summary>

| Stage | Duration | Cost | Retries |
|---|---|---|---|
| **Total** | **61m 23s** | **$53.41** | **0** |

</details>

<details>
<summary>Ran <code>ImplementPlan.fabro</code> (12 nodes and 15
edges)</summary>

```dot
digraph ImplementPlan {
    graph [
        goal="Implement and simplify",
        model_stylesheet="
            * { model: claude-opus-4-7; }
        "
    ]
    rankdir=LR

    start [shape=Mdiamond, label="Start"]
    exit  [shape=Msquare, label="Exit"]

    toolchain         [label="Toolchain", shape=parallelogram, script="command -v cargo >/dev/null || { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && sudo ln -sf $HOME/.cargo/bin/* /usr/local/bin/; }; cargo --version 2>&1", max_retries=0]
    preflight_compile [label="Preflight Compile", shape=parallelogram, script="cargo check -q --workspace 2>&1", max_retries=0]
    preflight_lint    [label="Preflight Lint", shape=parallelogram, script="cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1", max_retries=0]
    fix_lints         [label="Fix Lints", prompt="The preflight lint step failed. Read the build output from context and fix all clippy lint warnings.", max_visits=3]
    implement         [label="Implement", prompt="Read the plan file referenced in the goal and implement every step. Make all the code changes described in the plan. Use red/green TDD."]
    simplify_opus     [label="Simplify (Opus)", prompt="@prompts/simplify.md"]
    simplify_gpt      [label="Simplify (GPT-55)", prompt="@prompts/simplify.md", model="gpt-55"]
    verify            [label="Verify", shape=parallelogram, script="cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1 && cargo nextest run --cargo-quiet --workspace --status-level fail 2>&1 && cargo dev docs refresh 2>&1 && cargo dev docs check 2>&1", goal_gate=true, retry_target="fixup"]
    fixup             [label="Fixup", prompt="The verify step failed. Read the build output from context and fix all clippy lint warnings, test failures, and generated docs errors.", max_visits=3]
    fmt               [label="Format", shape=parallelogram, script="cargo +nightly-2026-04-14 fmt --all 2>&1", max_retries=0]

    start -> toolchain
    toolchain -> preflight_compile [condition="outcome=succeeded"]
    toolchain -> exit
    preflight_compile -> preflight_lint [condition="outcome=succeeded"]
    preflight_compile -> exit
    preflight_lint -> implement [condition="outcome=succeeded"]
    preflight_lint -> fix_lints
    fix_lints -> preflight_lint
    implement -> simplify_opus -> simplify_gpt -> verify
    verify -> fmt   [condition="outcome=succeeded"]
    verify -> fixup
    fixup -> verify
    fmt -> exit
}

```

</details>

⚒️ Generated with [Fabro](https://fabro.sh)

---------

Co-authored-by: Fabro <noreply@fabro.sh>
Co-authored-by: Bryan Helmkamp <bryan@brynary.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 15:33:31 -04:00
Bryan Helmkamp
7be557312e
chore(workflows): bump implement-plan simplify stage to gpt-5.5
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 11:12:40 -04:00
Bryan Helmkamp
0fca8a3625
fix(workflows): refresh and check generated docs in verify gate
PR #202 shipped a new CLI subcommand without regenerating
docs/public/reference/cli.mdx, so the Generated Docs CI job failed on
push. The implement-plan workflow's verify gate had no equivalent of
`cargo dev docs check`.

Append `cargo dev docs refresh && cargo dev docs check` to verify so
the gate auto-fixes drift and surfaces real authoring errors (missing
help text, removed generated-region fences) through the fixup loop.
Also broaden the fixup prompt to cover docs errors.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 11:12:40 -04:00
Bryan Helmkamp
33180703dd
chore(workflows): bump default model to claude-opus-4-7
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 11:12:40 -04:00
Bryan Helmkamp
ea3b7437e2
fix(workflows): add --all-targets to clippy in fabro workflows
The implement-plan and smoke workflows ran clippy without --all-targets,
so test, example, and bench targets were skipped. CI runs clippy with
--all-targets, so lint errors in test code passed the workflow's verify
gate but failed CI on push. Aligns the workflow lint commands with CI
and CLAUDE.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 11:12:40 -04:00
Bryan Helmkamp
e5c5feaa8d
fix(workflows): pin clippy and fmt to nightly-2026-04-14
The smoke and implement-plan workflows ran cargo clippy without a
toolchain prefix, so on the Daytona snapshot they fell through to the
baked-in stable toolchain. clippy.toml now uses allow-unwrap-types
(added in clippy 1.95), which the stable in fabro-v7 doesn't recognize.
Pin every fmt and clippy invocation to nightly-2026-04-14 so they match
.github/workflows/rust.yml. Also update the public repl-handoff example
to keep the documented template consistent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 17:32:46 -04:00
Bryan Helmkamp
f16391485b
refactor(workflow): update stage outcome semantics 2026-04-30 06:06:51 -04:00
Bryan Helmkamp
3c1aee39bf
chore: remove workflow-level sandbox override 2026-04-27 16:03:23 -07:00
Bryan Helmkamp
5e61518769
chore: temporarily update hello workflow 2026-04-25 08:55:47 -04:00
Bryan Helmkamp
2db50966e8
fabro 2026-04-15 10:15:28 -04:00
Bryan Helmkamp
dc93404e38 refactor(config): move project state under .fabro
Keep project config and checked-in workflows under .fabro so they stay out of
normal repo listings. Update config discovery, CLI project commands, fixtures,
docs, and checked-in workflow paths to use .fabro/project.toml and
.fabro/workflows/*.
2026-04-11 12:55:46 -04:00