feat(p2): unify retry on RetryConfig + add circuit breaker & bulkhead#3585
Closed
KooshaPari wants to merge 82 commits into
Closed
feat(p2): unify retry on RetryConfig + add circuit breaker & bulkhead#3585KooshaPari wants to merge 82 commits into
KooshaPari wants to merge 82 commits into
Conversation
- Bootstrap deny.toml with license allowlist + advisory ignores - Add license = MIT to workspace.package (was missing) - Add license.workspace = true to all 27 crate manifests - Ignore transitive unmaintained (bincode, yaml-rust, paste, rustls-pemfile) - Ignore transitive vulns (hickory-proto, rustls-webpki) via aws-sdk/reqwest Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Phenotype Agent <agent@phenotype.ai>
Pin all action refs to immutable SHAs across workflow files: - checkout@v4 → @11bd71901bbe5b1630ceea73d27597364c9af683 - checkout@v6 → @de0fac2e4500dabe0009e67214ff5f5447ce83dd - setup-node@v4/v5, setup-python@v4/v5, setup-go@v5 - upload-artifact@v4/v7, download-artifact@v4 - cache@v3/v4, github-script@v7 - configure-pages@v5/v6, deploy-pages@v4/v5 - upload-pages-artifact@v3/v5, dependency-review-action@v4 Fixes version-tag normalization (add v4/v5 tags where missing). Fixes double-SHA corruption artifacts from prior patching rounds. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Reduce README from 1124 to 169 lines (-85%). Keep: project name, description, quickstart, usage examples, why forge, installation, community, documentation link. Add fork disclaimer pointing to upstream tailcallhq/forgecode. Preserve all upstream content via pointer comment. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Reduce README from 1124 to 169 lines (-85%). Keep: project name, description, quickstart, usage examples, why forge, installation, community, documentation link. Add fork disclaimer pointing to upstream tailcallhq/forgecode. Preserve all upstream content via pointer comment. Co-authored-by: Phenotype Agent <agent@phenotype.ai> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Phenotype Agent <agent@phenotype.ai>
Verified resolved upstream; advisory no longer triggers. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…nale - Add 4 ignore entries for bincode 1.x (RUSTSEC-2025-0141), paste (2024-0436), rustls-pemfile (2025-0134), yaml-rust (2024-0320) — all transitive via upstream forgecode workspace deps; resolution depends on upstream tailcallhq/forgecode bumps. - Pre-existing fork-specific RUSTSEC-2026-* ignores preserved. - cargo deny check advisories: PASS.
…nale (#8) - Add 4 ignore entries for bincode 1.x (RUSTSEC-2025-0141), paste (2024-0436), rustls-pemfile (2025-0134), yaml-rust (2024-0320) — all transitive via upstream forgecode workspace deps; resolution depends on upstream tailcallhq/forgecode bumps. - Pre-existing fork-specific RUSTSEC-2026-* ignores preserved. - cargo deny check advisories: PASS. Co-authored-by: Phenotype Agent <agent@phenotype.ai>
Root cause: crossterm's cursor position CSI query times out (2s) when multiple concurrent sessions are running or terminal is under load. Fix: - Add error::is_cursor_timeout_error() to detect cursor position errors - Add terminal::get_cursor_position_with_retry() with backoff - Suppress cursor errors during shutdown in Ui::shutdown() - Add comprehensive tests for cursor error detection Fixes session crashes where user sees: 'cursor position could not be read within a normal duration' 'Resource temporarily unavailable (os error 35)' Tested: 337 tests pass (333 existing + 4 new cursor error tests)
Add summarization feature with: - llm_summarizer: Async LLM-based summarization service - adaptive_eviction: Importance-based eviction strategies - metrics: Summarization metrics tracking - prefilter: Pre-summarization filtering - Updated compaction config and strategy Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…p-go Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Restore the workspace members array by listing all crate directories present on disk, removing invalid glob patterns. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add [bans] section with recommended warnings - Update GitHub workflow files (trufflehog, stale, labels, release)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Cargo.lock, package-lock.json: keep ours - Cargo.toml, crates/forge_tracker/Cargo.toml: UNION deps (higher versions) - posthog-rs: 0.12.0 → 0.14.0 (upstream) - gix: 0.84 → 0.85 (upstream) - provider.json: keep ours (--ours) - model.rs: UNION AppCommand enum (Subagents, Goal, Loop, Parent, Reparent, Cwd, Sort, Search + ConversationTree) - ui.rs: keep ours (session-viewer feature)
- Implements stub handler for upstream's conversation-tree command - Reuses list_conversations() for now; can be enhanced later with tree-specific logic
…contention (P3) (#33) * feat(forge_dbd): scaffold single-writer daemon to collapse N:1 SQLite contention (P3) Design + compiling scaffold for forge-dbd: one daemon owns the only R/W connection to .forge.db; the ~13 forge processes become clients over a Unix socket, with writes batched into shared transactions (the durable fix for per-turn blob-rewrite amplification). Reads stay direct (WAL allows concurrent readers); same .forge.db file, zero data migration; swaps behind the single ConversationRepositoryImpl::run_with_connection seam in a follow-up. This commit lands the protocol enums, client/server skeletons (todo!() bodies), the workspace member, and the design doc. No existing crate behavior changes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(forge_dbd): async framed I/O + accept loop compiles (P3) Fixed E0308 type mismatches in server.rs: - Made timeout()+read_frame() properly await the future - Added .await to all write_frame() calls (lines 108, 117, 132) - Removed unused AsyncReadExt/AsyncWriteExt imports The accept loop, batching writer queue, and oneshot acks now compile cleanly. Protocol layer (async read/write frame functions) from prior commit. - Accept loop: listener.accept(), spawn handle_client per connection - Batch writer: accumulates requests with 15ms timeout, flushes at 100+ size - Client handler: reads framed requests, enqueues via channel, awaits response Build: cargo build -p forge_dbd ✓ / cargo test -p forge_dbd ✓ Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Phenotype Agent <agent@phenotype.ai> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…d work (#38) Added 8 new tests covering: 1. Migration round-trip: verify all migrations apply cleanly on fresh in-memory DB - intent_state column exists with TEXT type - FTS5 external-content table (conversations_fts) created correctly - P4 indexes idx_conversations_intent_pending/verified exist 2. FTS5 external-content refresh and search: - Insert 3 test conversations - Call refresh_fts_index (rebuild) to populate FTS5 index - Verify FTS5 search returns correct results with BM25 ranking 3. IntentState transition guards (legal transitions): - Forward path: pending → extracting → extracted → verified → pruned - Idempotent transitions (state → same state) - Reversions on failure (extracting/extracted/verified → pending) - Forward skips (manual override: pending → extracted/verified) 4. IntentState transition guards (illegal transitions): - Cannot jump directly to pruned (must go through verified) - Pruned is final (no forward transitions from pruned) - No backwards skipping (extracting → verified forbidden) 5. Prune conversation gate: - Pruning denied when intent_state != 'verified' - Pruning succeeds when intent_state = 'verified' - Context set to NULL after successful prune 6. FTS5 external-content schema validation: - No synchronous FTS triggers remain (P2 removed them) - FTS5 uses porter tokenizer for stemming - FTS5 indexes title, context, cwd columns 7. Intent state indexing with mixed states: - Query idx_conversations_intent_pending (pending or extracting) - Query idx_conversations_intent_verified (verified only) - Correct counts for each state 8. Memory ID tracking for audit trail: - memory_id column stores MemoryPort UUID - extracted_at column tracks extraction completion time - Both queryable for audit trail and verification All 8 tests pass. Full cargo test --workspace: 708 passed. Validates merged work: P1a (checkpointer config), P2a (FTS refresh cadence), P2b (external-content FTS5), P4 (intent_state schema + prune gate). Co-authored-by: Phenotype Agent <agent@phenotype.ai> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…#39) Classifies forge conversations by first-user-message authorship to identify AI-subagent sessions. Safe operation by default with dry-run reporting. Core features: - Classifies sessions as AI (subagent/bootstrap) or human (user/terminal) - --apply mode with --tier 1/2 for selective deletion - --yes flag refuses unless no processes hold database (via lsof) - Backs up database before any deletion - Cascading soft-delete for session_tasks, session_attachments - Preserves terminal-paste sessions and human-authored entries - Verified refusing against live forge PIDs Usage: cargo run --quiet -- --db-path ~/forge/.forge.db --apply --tier 1 --yes Co-authored-by: Phenotype Agent <agent@phenotype.ai> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(forge_repo): transparent zstd compression of context blobs (lossless, dual-read) - Add zstd = 0.13 to workspace dependencies - Create codec module: compress() and decompress() with zstd level 3 (fast ~4x on JSON) - Migration 2026-06-26-000300: add context_zstd BLOB and is_compressed INTEGER columns - ConversationRecord: dual-path write — compress on write to context_zstd, is_compressed=1 - TryFrom: dual-path read — decompress if is_compressed=1, else fallback to plain context - Update schema.rs with new columns - Update upsert_conversation() to write both context_zstd and is_compressed - Fix query filters: check 'context IS NOT NULL OR is_compressed = 1' for get_all/get_last - Unit tests: 6 codec tests (round-trip, compression ratio, unicode, invalid data) - Backward compatible: old uncompressed rows continue to work - No backfill: new rows compress automatically; existing rows remain uncompressed (backfill is future tool) - Known limitation: FTS indexes need update for compressed rows (TODO for follow-up) Test results: 345 passing, codec tests green, build clean, clippy OK * fix(forge_repo): FTS indexes decompressed context so compressed rows are searchable PROBLEM: FTS5 external-content mode reads the `context` column by name. For compressed rows (context=NULL, is_compressed=1), external-content FTS5 silently misses them because context is NULL. Searches find uncompressed rows but NOT compressed rows. ROOT CAUSE: The dual-path read in ConversationRecord decompresses on the fly, but FTS5's external-content mode reads by column name before app-level decompression, missing NULL context entirely. SOLUTION: Revert to CONTENTFUL FTS5 (new migration 2026-06-26-000400) and populate the index in application code: - refresh_fts_index() now: (1) CLEARs FTS, (2) SELECTs all conversations, (3) for each row, decompresses context_zstd if is_compressed=1, else uses plain context, (4) INSERTs the decompressed text into FTS - This ensures BOTH compressed and uncompressed rows are indexed and searchable TRADEOFF: CONTENTFUL FTS5 stores a copy of indexed columns in _content table. However, the base conversations.context is still compressed (zstd), so the primary space savings remain. FTS's copy is a searchable index, not further compressed. TEST: New test test_search_finds_compressed_conversations() proves: - Compressed conversation (context_zstd, is_compressed=1, context=NULL) is inserted - refresh_fts_index() populates FTS with decompressed context - FTS search finds the compressed row by its content - This test FAILS before the fix (search returns empty for compressed rows) - This test PASSES after the fix (search finds all rows) VERIFICATION: ✓ cargo build --workspace: clean ✓ cargo test -p forge_repo: 347 passed, 0 failed ✓ cargo clippy -p forge_repo: no new warnings ✓ New test test_search_finds_compressed_conversations: PASS ✓ All FTS-related tests updated and pass Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(forge_repo): clippy linting (doc comment, module name, vec!) - Remove empty line after doc comment in compression.rs - Rename inner test module to integration_tests to avoid same-name warning - Replace vec![] with array literal for test data Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Phenotype Agent <agent@phenotype.ai> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Freed pages (P4 prune, zstd compression, deletes) return to the OS continuously via incremental_vacuum on the background checkpointer, instead of needing an exclusive-lock full VACUUM. **Changes:** 1. **pool.rs (SqliteCustomizer)**: Added `PRAGMA auto_vacuum=INCREMENTAL` to on_acquire. - For NEW databases: takes effect immediately at creation. - For EXISTING .forge.db: pragma is a no-op; requires one forge-vacuum full-VACUUM to convert, after which incremental_vacuum self-maintains. 2. **checkpoint.rs**: - After each successful WAL truncate checkpoint, run `PRAGMA incremental_vacuum` if FORGE_INCREMENTAL_VACUUM is enabled (default: yes). - Non-fatal errors are logged; the loop continues. - Env gate: set FORGE_INCREMENTAL_VACUUM to "0"/"false"/"no"/"off" to disable. **Testing:** All 340 forge_repo tests pass. cargo build green. cargo clippy clean. Co-authored-by: Phenotype Agent <agent@phenotype.ai> Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
…42) * feat(tooling): forge-context-backfill — batch-compress existing rows Batch zstd-compresses existing is_compressed=0 conversation rows, matching the forge_repo codec exactly (level 3). Per-row lossless round-trip verification before write; batched+resumable+idempotent (skips already-compressed rows). Safety features: - Preflight check: refuses if forge procs hold DB (lsof, lists PIDs, never kills) - Disk space check: refuses if < (db_size + 1GB) free - Automatic timestamped backup before any write (skippable with --skip-backup) - Batched transactions (resumable on failure, configurable batch size) - Per-row lossless verification: decompress and verify == original before write - Full VACUUM option (--vacuum): reclaims space + converts to incremental auto_vacuum - Idempotent: re-running skips already-compressed rows CLI: --db-path, --apply (dry-run default), --yes (required with --apply), --batch-size, --backup-dir, --skip-backup, --vacuum Dry-run default shows what would be compressed (~12.5k rows expected), estimated space savings without writing anything. Standalone crate (own [workspace] table) to avoid bloating main workspace build. Matches forge_repo codec compression format byte-for-byte. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(forge-context-backfill): dry-run should work read-only without lsof gate The dry-run (default, no --apply) is a read-only operation and should not require forge processes to be idle. It only needs to query the database and report row counts and estimated compression savings. The lsof gate (SAFETY GATE 1) now applies only to --apply mode, not dry-run. Changes: - Wrapped SAFETY GATES 1-3 (process check, disk space, backup) in 'if args.apply' block - Added Database::open_readonly() that sets PRAGMA query_only = ON - Dry-run now opens DB in read-only mode, bypassing all safety gates - --apply mode still enforces all three safety gates before write operations Testing: - Dry-run on 2510 uncompressed rows: prints row counts without any safety gate refusal - --apply mode: enforces process check, disk space, and backup creation - Compression ratio verified: 96.7% reduction (2.54 MiB → 84.92 KiB) --------- Co-authored-by: Phenotype Agent <agent@phenotype.ai> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…elector + version bump 2.10.0 (#43) ## Changes ### Conv-View Render Improvements (conversation_selector.rs:45→57) - **Remove fork-added ID prefix**: Deleted `[{short_id}]` from display format (upstream-clean render) - **Truncate titles to fixed width**: Titles now max 50 chars with `…` ellipsis; prevents line wrapping - **Fixed-column date alignment**: Date/time_ago right-aligned in 10-char field for stable layout - Before: `[550e8400] Very long conversation title that pushes date off screen (2d ago)` - After: `Very long conversation title that pus… 2d ago ` (aligned) ### FTS Search Wiring (conversation_selector.rs:86→106) - Query parameter now honored: when query is provided, filters conversations by title substring match - Enables search_conversations() backend to be reachable from UI - Fallback to all valid conversations if search returns no results ### Sorting Integration (conversation_selector.rs:73→181) - Added `sort: ConversationSort` parameter to `select_conversation()` - Selector now renders items in UIState.sort order (updated/created/turns/title/cwd) - Updated all 6 call sites in ui.rs to pass `self.state.sort` - Sort is applied AFTER filtering so search results are sorted correctly ### Version Bump - forge_main Cargo.toml: 0.1.1 → 2.10.0 (signals feature wave beyond fork baseline) - Aligns with workspace parent version 2.9.9 ## Quality - cargo clippy: clean (0 warnings) - cargo check: ✓ forge_main compiles - Tests pass: test_select_conversation_empty_list updated with sort parameter ## Before/After **Conv-View Selector Header**: `ID Title Updated` → `Title Updated ` (fixed-width title column) **Visibility**: Backend search_conversations() + ConversationSort now reflected in UI selection flow Co-authored-by: Phenotype Agent <agent@phenotype.ai> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…a wiring (#44) Adds 3 Claude Code-parity commands that agents and users can invoke to manage the session: - **/clear** — clears the terminal screen (via Console::clear_screen using ANSI escape) - **/init** — writes an AGENTS.md bootstrap in the workspace root (via console prompt + fs write) - **/rewind** — rolls back the conversation to the most recent compaction point (via rewind_conversation API, which truncates the context JSON back to the last user message + surrounding tool calls) All 3 are registered as AppCommand variants with name(), parse-args patterns, and on_command match arms. rewind_conversation is wired through the full 6-layer chain: repo trait → repo impl → service → AgentService → API trait → API impl. Build: cargo build --bin forge clean in 27.22s. Binary at ~/.local/bin/forge-dev (178MB). Co-authored-by: Phenotype Agent <agent@phenotype.ai>
…napshot fix crates forge3d — daemon (UDS JSON-RPC + agent registry + pidfile) forge_drift — DriftDetector (T0-T3) with forward-dated lease forge_similarity — SimilarityProvider trait + Tier/ApprovalMode config forge_mux — MuxBridge + tmux + HarnessMuxBridge ghostty-kit — Config parser + IPC client (prior session re-sync) shell-plugin lib/drift.zsh — New: forge_drift_observe, _override, _subscribe forge.plugin.zsh — Wire drift hooks into precmd/preexec/fig_prezto workspace Cargo.toml — 5 crates added to members + internal deps Cargo.lock — Resolved fixes forge_repo — Compile fix (broken dependency ref) forge_app snaps — Accept 4 updated system-prompt snapshots
…eystroke subprocess spawn (#45) Adds a simple time-based debounce: before calling render_preview(), check if >=150ms has elapsed since the last render. Holding arrow for 2s went from ~60 subprocess spawns to ~2-3. Matches the 250ms event::poll interval. Co-authored-by: Phenotype Agent <agent@phenotype.ai>
…in (#46) Two surgical fixes that were blocking ALL PR auto-merges: 1. **cargo-deny.yml** — was using taiki-e/upload-rust-binary-action (wrong action — that uploads release binaries) instead of taiki-e/install-action (which installs Rust tools). Replaced with install-action@v2 with tool: cargo-deny. 2. **trufflehog.yml** — stale SHA pin on actions/setup-go (the SHA was deleted/broken). Updated to explicit @f111f3307d8850f501ac008e886eec1fd1932a34 (# v5) with proper validation. Both are 1-line changes that fix the root cause of the systemic 'required check CI failing' error that forced every PR to merge with --admin bypass. Co-authored-by: Phenotype Agent <agent@phenotype.ai>
… fix Remaining Claude Code parity gaps plus the debounce hotfix: 1. /review — opens a structured code review prompt 2. /test — triggers test creation workflow 3. /think — signals thinking before acting All 3 are registered as AppCommand variants with name() arms, on_command match arms, and handler stubs. 4. Debounce fix (preview.rs) — prevents per-keystroke subprocess spawn by checking if >=150ms elapsed since last render. Build: cargo build --bin forge clean. forge-dev installed.
…parent_id Adds a visual breadcrumb in the conversation selector: subagent sessions (conversations with a parent_id) now show with a '↳' prefix before their title, making it immediately visible which sessions are subagents spawned from a parent. Addresses the 'pollution of session history' complaint. Also verified: OrchestratorDropGuard already wired at orch.rs:268-272, preview debounce already at preview.rs:401-410, :search already wired to FTS5 snippets in 6 layers.
…header Shows a breadcrumb in both the conversation info panel (info.rs) and the on_show_current_message chat header (ui.rs) when the current conversation is a subagent (has a parent_id set). Displays: 'parent_id: <id>'. This addresses the 'pollution of session history' pain by making it instantly visible which sessions are subagents spawned from a parent, even when you're inside the conversation itself. Combined with the '↳' prefix in the conversation selector (previous commit), this gives a complete visual hierarchy across both surfaces.
Adds a slash command that manually invokes the FTS5 optimize_fts_index method to reclaim search index bloat. The optimize method was fully wired through 6 layers but never called from any UI path — this is the final 'wired but dead' item from the deep audit. :fts-optimize triggers an immediate FTS5 merge (not incremental). Uses writeln_info and writeln_help for output. Also adds the AppCommand::FtsOptimize variant + name() match arm + on_command dispatch + handler.
…okens)
Auth types that hold secret values derived `#[derive(Debug)]`, allowing
plaintext secrets to leak via `{:?}` into logs and the PostHog tracker.
Types fixed:
- `ApiKey` — custom Debug prints `ApiKey(<redacted>)`
- `AccessToken` — custom Debug prints `AccessToken(<redacted>)`
- `RefreshToken` — custom Debug prints `RefreshToken(<redacted>)`
- `AuthorizationCode` — custom Debug prints `AuthorizationCode(<redacted>)`
- `PkceVerifier` — custom Debug prints `PkceVerifier(<redacted>)`
- `State` — custom Debug prints `State(<redacted>)` (holds PKCE verifier in Anthropic flow)
- `OAuthTokenResponse` — hand-written Debug redacts access_token, refresh_token, id_token
- `McpOAuthTokens` — hand-written Debug redacts access_token, refresh_token
- `McpClientRegistration` — hand-written Debug redacts client_secret
Non-secret fields (provider id, expires_at, token_type, scope, client_id) remain
visible in Debug for usability. Display impls are unchanged (already redacted).
Tests added asserting `format!("{:?}", secret_value)` does not contain plaintext
and does contain `<redacted>`.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
) 5-cluster deep audit (W02/W03/W05/W07/W12, 87 findings) of weakest pillars (means 0.83-1.40) against current main. Root cause: leftover TS-evals fork scaffolding. Adds phased WBS+DAG (P0 de-fork docs -> P1 gates/stubs/security -> P2 hardening -> P3 perf/concurrency -> P4 ops -> P5 cross-repo crates) with agent-effort estimates, plus live-bug known-issues (P0 secret Debug-leak). Co-authored-by: Phenotype Agent <agent@phenotype.ai> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace leftover 'ForgeCode Evals' TypeScript scaffolding with accurate docs for the real 33-crate Rust workspace (root cause of W12/W05/W01 audit misscoring). - README: Rust product description, real crate architecture, cargo/just quick-start - docs/SSOT.md: kill 'Rust: N/A' + fictional ProviderPort/CsvAdapter; real workspace - Justfile: cargo-driven recipes (build/test/lint/fmt) replacing npm/eslint - .gitignore: ignore .credentials.json (local 0o600 credential store) cargo build exit 0; README/SSOT grep-clean of TS ghosts; just recipes parse. Co-authored-by: Phenotype Agent <agent@phenotype.ai> Co-authored-by: ForgeCode <noreply@forgecode.dev>
…#48) - Delete crates/ghostty-kit entirely: no workspace crate depended on it (confirmed via grep on Cargo.toml files and .rs imports). Removed from workspace members and workspace.dependencies in root Cargo.toml. - forge_dbd: add description field marking it WIP and add README.md explaining it is part of the SQLite-WAL/FTS epic, not yet wired into forge_app. No code deleted — crate preserved for the epic. Co-authored-by: Phenotype Agent <agent@phenotype.ai> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
… true no-op (#49) - forge_repo: MockHttpClient.http_delete in #[cfg(test)] block now calls self.client.delete(url).send() instead of unimplemented!() - forge_domain: NoopIntentExtractor.extract_intent returns Ok(ExtractedIntent { episodic: Null, identity: Null, project_knowledge: Null }) and verify_extraction returns Ok(false) instead of propagating errors Co-authored-by: Phenotype Agent <agent@phenotype.ai> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace hardcoded 5-attempt ExponentialBuilder in ForgeMcpClient with one driven by the global RetryConfig; zero observable behaviour change when config is default. - ForgeMcpClient gains a CircuitBreaker (opens after max_attempts failures, half-open probe, closes on success) and a Bulkhead (16-permit semaphore) wrapping every list/call invocation. - ForgeMcpServer now carries RetryConfig and threads it to each ForgeMcpClient it constructs; ForgeInfra seeds it from ForgeConfig.retry. - DatabasePool::retry_with_backoff now accepts &RetryConfig and maps its fields (max_attempts, min_delay_ms, backoff_factor) to the backon ExponentialBuilder; PoolConfig gains an optional retry_config field and a with_retry_config builder method. - New forge_infra::resilience module: CircuitBreaker + Bulkhead impls with 7 unit tests (trips/half-opens/closes, bulkhead capacity). - Fix two pre-existing clippy::collapsible_if / unused-variable / manual-char-comparison warnings in conversation_repo.rs and mcp_client.rs. Co-Authored-By: ForgeCode <noreply@forgecode.dev>
|
Phenotype Agent seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
Author
|
Wrong base (upstream); reopening on KooshaPari/forgecode. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ForgeMcpClient::attempt_with_retry(previously hardcodedmax_times(5)) andDatabasePool::retry_with_backoff(previously hardcodedmax_retries: 5,min_delay: 1s) now both consumeRetryConfigfields — the same struct already used byforge_app::retry::retry_with_config. All retry knobs (max_attempts,min_delay_ms,backoff_factor,max_delay_secs) are honoured; default behaviour is unchanged when config is zero-value.forge_infra::resilience::CircuitBreaker): wraps every MCPlist/callinvocation. Opens afterfailure_thresholdconsecutive failures (derived fromRetryConfig::max_attempts), enters half-open probe afterreset_timeout(derived fromRetryConfig::max_delay_secs), closes on probe success, re-opens on probe failure.forge_infra::resilience::Bulkhead): 16-permit semaphore around MCP calls; rejects immediately when saturated — no silent queue buildup.ForgeMcpServernow carriesRetryConfigand threads it to eachForgeMcpClient;ForgeInfra::newseeds it fromconfig.retry.PoolConfiggainsretry_config: Option<RetryConfig>andwith_retry_config()builder.conversation_repo.rs(unused variable, manual char comparison).Test plan
cargo build— exit 0 (verified)cargo test -p forge_infra— 88 passed, 0 failed (verified); includes 7 resilience unit tests: breaker opens/closes/reprobe, bulkhead limit/release/concurrentcargo test -p forge_repo— 347 passed, 0 failed (verified)cargo clippy -p forge_infra -p forge_repo -- -D warnings— clean (verified)🤖 Generated with Claude Code