Add the shared fabro-http transport crate and route hand-written HTTP client construction through it.
Use FABRO_HTTP_PROXY_POLICY for test no-proxy defaults, remove direct reqwest deps from ordinary crates, and add clippy bans for raw reqwest entrypoints.
Add the stripped twin-github test server to the workspace, wire it through
fabro-test, and cover fabro-github's real HTTP auth and pull-request flows
with twin-backed integration tests. This also refactors the GitHub helper
entry points to take explicit base URLs so tests and callers share the same
request path.
Introduce an HttpClient trait abstraction over reqwest::Client so tests
use a lightweight MockHttpClient instead of spawning a TCP server via
mockito. This removes the mockito dev-dependency entirely and makes
tests faster and more deterministic.
Also add self-loop detection in ImportTransform to poison placeholders
that have edges pointing back to themselves.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Configure clippy `wildcard_imports = "warn"` at the workspace level and
opt all 28 crates in via `[lints] workspace = true`. Fix the three
production glob imports that triggered warnings: fabro-sandbox
read_guard, fabro-cli main, and fabro-api demo module (allowed via
attribute since it constructs many API types by design). Document the
import style convention in CLAUDE.md.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This PR consolidates the tracker ecosystem from three crates
(`fabro-tracker`, `fabro-linear`, `fabro-github`) into two by merging
both tracker implementations into `fabro-tracker` and deleting
`fabro-linear`. The `GitHubTracker` and its supporting functions
(`execute_github_graphql`, `normalize_github_item`,
`fetch_project_items_page`) have been moved from `fabro-github` into a
new `fabro-tracker/src/github.rs` module, while the Linear
implementation from `fabro-linear` moves into
`fabro-tracker/src/linear.rs`. The duplicate `Issue` and `BlockerRef`
type definitions that existed in `fabro-linear` are removed in favor of
the canonical types already defined in `fabro-tracker`.
The dependency direction between `fabro-github` and `fabro-tracker` is
intentionally reversed: `fabro-tracker` now depends on `fabro-github`
for auth primitives (`GitHubAppCredentials`, `sign_app_jwt`,
`create_installation_access_token_for_projects`), while `fabro-github`
drops its dependency on `fabro-tracker` entirely. This eliminates the
circular dependency risk and keeps `fabro-github` focused on its core
responsibility of GitHub App authentication and REST/GraphQL transport.
A shared `execute_graphql_request` helper is introduced in
`fabro-tracker` to reduce duplication between the GitHub and Linear
GraphQL implementations.
All tests that previously lived in `fabro-github` and `fabro-linear` are
relocated to their respective new modules in `fabro-tracker`. The
`test_rsa_key()` helper used in GitHub tracker tests is duplicated in
`fabro-tracker/src/github.rs` since test utilities are not importable
across crate boundaries. The Linear `normalize_issue` function is
updated to set `project_item_id: None` to conform to the shared `Issue`
type, and existing Linear tests are updated accordingly.
### Fabro Details
<details>
<summary>Ran 9 stages in 24m 3s for $6.93</summary>
| Stage | Duration | Cost | Retries |
|---|---|---|---|
| start | 0s | – | 0 |
| toolchain | 0s | – | 0 |
| preflight_compile | 1m 15s | – | 0 |
| preflight_lint | 13s | – | 0 |
| implement | 14m 56s | $4.58 | 0 |
| simplify_opus | 6m 50s | $2.35 | 0 |
| simplify_gpt | 0s | – | 0 |
| verify | 19s | – | 0 |
| fmt | 1s | – | 0 |
| **Total** | **24m 3s** | **$6.93** | **0** |
</details>
<details>
<summary>Ran <code>ImplementAndSimplify.fabro</code> (12 nodes and 15
edges)</summary>
```dot
digraph ImplementAndSimplify {
graph [
goal="Implement and simplify",
model_stylesheet="
* { backend: api; model: claude-opus-4-6;}
"
]
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 clippy -q --workspace -- -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-54)", prompt="@prompts/simplify.md", model="gpt-54"]
verify [label="Verify", shape=parallelogram, script="cargo clippy -q --workspace -- -D warnings 2>&1 && cargo nextest run --cargo-quiet --workspace --status-level fail 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 and test failures.", max_visits=3]
fmt [label="Format", shape=parallelogram, script="cargo fmt --all 2>&1", max_retries=0]
start -> toolchain
toolchain -> preflight_compile [condition="outcome=success"]
toolchain -> exit
preflight_compile -> preflight_lint [condition="outcome=success"]
preflight_compile -> exit
preflight_lint -> implement [condition="outcome=success"]
preflight_lint -> fix_lints
fix_lints -> preflight_lint
implement -> simplify_opus -> simplify_gpt -> verify
verify -> fmt [condition="outcome=success"]
verify -> fixup
fixup -> verify
fmt -> exit
}
```
</details>
⚒️ Generated with [Fabro](https://fabro.sh)
---------
Co-authored-by: Fabro <noreply@fabro.sh>