main
15 次代码提交
| 作者 | SHA1 | 备注 | 提交日期 | |
|---|---|---|---|---|
|
|
1a476562fc |
Make provider model discovery the sole catalog owner (#1221)
## Problem Startup model-list I/O was split between a non-enforcing configured-model validator and the real discovery path. Both queried providers and populated the same cache even though synchronous cache warm-up was the validator's only required effect. ## Changes | Before | After | | --- | --- | | Validation and discovery independently resolved providers, queried model lists, and cached results. | `ProviderModelDiscovery` solely owns model-list queries, failure reporting, and cache population. | | Startup ran configured-model validation and then launched discovery. | Startup synchronously warms referenced providers through discovery, then launches the existing missing-provider background pass. | | Absence from a provider catalog produced a non-enforcing missing-model warning. | Provider catalogs remain discovery metadata and provider execution remains authoritative. | | Configured model references retained environment-source metadata for validator diagnostics. | Configured model references retain only routing data. | | Successful startup providers could be represented by two separate subsystems. | Focused tests enforce concurrent warm-up, single successful queries, failed-query eligibility, and warm-before-background ordering. | | The package version was `4.11.5`. | The package version is `4.11.6` with an updated lockfile. | <!-- greptile_comment --> <details open><summary><h3>Greptile Summary</h3></summary> This PR makes provider discovery the sole owner of model catalogs. The main changes are: - Warms routed provider catalogs before background discovery starts. - Reuses successful warm results while retrying failed providers. - Removes configured-model catalog validation and source metadata. - Moves query-failure reporting into the discovery module. - Updates focused tests, architecture docs, and package version. </details> <h3>Confidence Score: 5/5</h3> This looks safe to merge. No blocking issues found in the changed code. Provider failures remain isolated and eligible for background retry. Successful warm results are not queried again by the missing-provider pass. Lease release and startup cleanup remain protected by existing control flow. None. <details><summary><h3><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="20" align="absmiddle"></a> T-Rex Logs</h3></summary> **What T-Rex did** - \`test\_runtime\_warm\_queries\_referenced\_providers\_concurrently\`, \`test\_startup\_discovery\_queries\_each\_successful\_provider\_once\`, \`test\_failed\_startup\_warm\_remains\_eligible\_for\_background\_refresh\`, \`test\_runtime\_warm\_caches\_all\_referenced\_provider\_models\`, and \`test\_runtime\_startup\_warms\_catalog\_before\_background\_refresh\` all passed. - The complete verbose HEAD run, including command, working directory, exit code, test nodes, and summary, is preserved in \`trex-artifacts/provider-discovery-startup-validation.log\` and the paired after artifact. <a href="https://app.greptile.com/trex/runs/15203372/artifacts"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img alt="View all artifacts" src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a> <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> </details> <details open><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | src/free_claude_code/providers/runtime/discovery.py | Centralizes catalog queries, failure reporting, referenced-provider warming, and cache population. | | src/free_claude_code/runtime/provider_manager.py | Replaces validation with discovery-based warming under a generation lease. | | src/free_claude_code/runtime/application.py | Warms referenced catalogs before launching the missing-provider background pass. | | src/free_claude_code/config/model_refs.py | Removes validation-only source metadata while preserving deterministic deduplication. | | tests/providers/test_model_discovery.py | Covers concurrent warming, partial failures, retry eligibility, and single successful queries. | </details> <details open><summary><h3>Sequence Diagram</h3></summary> <a href="#gh-light-mode-only"> ```mermaid %%{init: {'theme': 'neutral'}}%% sequenceDiagram participant App as ApplicationRuntime participant Manager as ProviderRuntimeManager participant Discovery as ProviderModelDiscovery participant Provider participant Cache as ProviderModelCache App->>Manager: warm_referenced_model_cache() Manager->>Discovery: warm referenced providers par Provider queries Discovery->>Provider: list_model_infos() end Provider-->>Discovery: metadata or failure Discovery->>Cache: cache successful results Discovery-->>Manager: refresh result Manager-->>App: warm complete App->>Manager: start_model_list_refresh() Manager->>Discovery: refresh only missing providers Discovery->>Cache: cache remaining catalogs ``` </a> <a href="#gh-dark-mode-only"> ```mermaid %%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% sequenceDiagram participant App as ApplicationRuntime participant Manager as ProviderRuntimeManager participant Discovery as ProviderModelDiscovery participant Provider participant Cache as ProviderModelCache App->>Manager: warm_referenced_model_cache() Manager->>Discovery: warm referenced providers par Provider queries Discovery->>Provider: list_model_infos() end Provider-->>Discovery: metadata or failure Discovery->>Cache: cache successful results Discovery-->>Manager: refresh result Manager-->>App: warm complete App->>Manager: start_model_list_refresh() Manager->>Discovery: refresh only missing providers Discovery->>Cache: cache remaining catalogs ``` </a> </details> <sub>Reviews (1): Last reviewed commit: ["Unify provider model discovery ownership"](https://github.com/alishahryar1/free-claude-code/commit/33f68e4338fd2326a7d8fd3e7e28246b47fa4210) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=45862060)</sub> <!-- /greptile_comment --> |
||
|
|
af12e7b2bb |
Coordinate provider recovery under concurrent load (#1205)
## Problem Concurrent transient failures could start independent retry, replay, continuation, and repair loops while holding provider concurrency slots. This multiplied upstream attempts and could delay or strand terminal errors under fan-out. ## Changes | Before | After | | --- | --- | | Retry paths owned separate attempt budgets. | One logical-execution session caps all upstream work at five attempts. | | Concurrent failures backed off independently. | One provider-owned recovery episode elects a single half-open probe while followers coalesce. | | Backoff occupied stream concurrency. | Concurrency is held only while an upstream operation or stream is active. | | Provider catalog calls and stream creation used separate admission paths. | Every upstream operation uses one provider-generation admission controller. | | Cancellation could leave recovery ownership or follower state unresolved. | Cancellation releases permits, transfers probe ownership, and unregisters waiting followers. | | Late in-flight failures could cross an exhausted episode boundary. | Every coalesced execution retains that generation's terminal outcome. | | Replay tests allowed loose lifecycle assertions. | Exact SSE contracts prove retries and continuations emit one unduplicated response. | | Recovery wrappers could mask final diagnostics. | Final responses and traces retain the raw provider failure and request ID. | <!-- greptile_comment --> <details open><summary><h3>Greptile Summary</h3></summary> This PR coordinates provider recovery and retry work under concurrent load. The main changes are: - One five-attempt budget for each logical execution. - Provider-wide recovery episodes with one elected probe. - Shared admission for streams, catalog calls, rate limits, and concurrency. - Concurrency permits held only during active upstream work. - Cancellation-safe probe ownership and preserved final diagnostics. </details> <h3>Confidence Score: 5/5</h3> This looks safe to merge. No blocking issues found in the changed code. <details><summary><h3><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="20" align="absmiddle"></a> T-Rex Logs</h3></summary> **What T-Rex did** - Reviewed the coordinated-recovery-01-before.log to understand how the exhausted generation outcome was not preserved in a late in-flight failure. - Reviewed the coordinated-recovery-02-after.log to confirm that the updated implementation preserves the exhausted generation outcome for the same focused contract set. - Validated that the provider-admission-full-current.log shows the complete requested test file passed under Python 3.14 with uv run pytest -n 0. <a href="https://app.greptile.com/trex/runs/15050270/artifacts"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img alt="View all artifacts" src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a> <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> </details> <details open><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | src/free_claude_code/providers/admission.py | Adds shared admission, retry budgets, recovery episodes, probe election, and cancellation handling. | | src/free_claude_code/providers/openai_chat/provider.py | Moves stream creation, replay, continuation, and repair onto one admission-owned retry session. | | src/free_claude_code/providers/stream_recovery.py | Selects replay, continuation, repair, or final failure using the remaining shared attempt budget. | | src/free_claude_code/providers/failure_policy.py | Adds recovery exhaustion handling and preserves the underlying provider error for final classification. | | src/free_claude_code/providers/runtime/factory.py | Creates one admission controller per provider generation and passes it through provider factories. | </details> <details open><summary><h3>Sequence Diagram</h3></summary> <a href="#gh-light-mode-only"> ```mermaid %%{init: {'theme': 'neutral'}}%% sequenceDiagram participant E as Execution participant A as Admission controller participant P as Provider participant F as Concurrent follower E->>A: Open attempt A->>P: Send upstream request P-->>E: Retryable failure E->>A: Open recovery episode F->>A: Request admission A-->>F: Coalesce and wait E->>A: Claim probe A->>P: Send half-open probe alt Probe succeeds P-->>E: Valid response E->>A: Close recovery episode A-->>F: Release waiter else Probe fails P-->>E: Retryable failure E->>A: Schedule next probe or finalize error end ``` </a> <a href="#gh-dark-mode-only"> ```mermaid %%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% sequenceDiagram participant E as Execution participant A as Admission controller participant P as Provider participant F as Concurrent follower E->>A: Open attempt A->>P: Send upstream request P-->>E: Retryable failure E->>A: Open recovery episode F->>A: Request admission A-->>F: Coalesce and wait E->>A: Claim probe A->>P: Send half-open probe alt Probe succeeds P-->>E: Valid response E->>A: Close recovery episode A-->>F: Release waiter else Probe fails P-->>E: Retryable failure E->>A: Schedule next probe or finalize error end ``` </a> </details> <sub>Reviews (2): Last reviewed commit: ["Harden coordinated retry lifecycle invar..."](https://github.com/alishahryar1/free-claude-code/commit/2e871c8649d148b5eb71d21f80bf870ae2d11708) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=45554917)</sub> <!-- /greptile_comment --> |
||
|
|
a092455b54 |
Add searchable model selection to the Admin UI (#1121)
## Problem Admin model routing fields required users to construct provider-prefixed model slugs. Optional tier overrides represented inheritance as an unexplained blank value. ## Changes | Before | After | | --- | --- | | Model inputs only gained suggestions after an individual provider refresh. | Model inputs load configured and discovered canonical slugs from one Admin catalog. | | Model routing looked like unrestricted text entry. | Model routing uses the browser's searchable model dropdown while retaining manual entry. | | Tier overrides displayed an empty value for fallback routing. | Tier overrides display **None** and persist it as an unset override. | | Model refresh returned provider-shaped cache internals. | Model refresh returns the same canonical catalog consumed by the Admin UI. | | Users inferred the provider/model slug format from examples. | The Admin UI and README define and present complete provider/model slugs. | <!-- greptile_comment --> <details open><summary><h3>Greptile Summary</h3></summary> This PR adds searchable model selection to the Admin UI. The main changes are: - Adds a canonical catalog of configured and discovered model slugs. - Adds searchable model inputs while preserving manual entry. - Represents unset tier overrides as **None**. - Reports provider-specific model refresh failures. - Reconciles cached models when provider settings change. - Updates documentation, package metadata, and tests. </details> <h3>Confidence Score: 5/5</h3> This looks safe to merge. Catalog failures no longer stop the rest of the Admin UI from loading. Partial provider refreshes now produce a visible warning. Removed credential-backed providers are pruned from the shared cache, and later stale writes are rejected. No blocking issues remain in the changed code. <details><summary><h3><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="20" align="absmiddle"></a> T-Rex Logs</h3></summary> **What T-Rex did** - T-Rex ran the requested verification for the pull request checks. - The verification completed, but local artifact references were not uploaded. <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> </details> <details open><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | src/free_claude_code/api/admin_static/admin.js | Adds searchable model fields, optional catalog hydration, refresh warnings, and None-to-unset conversion. | | src/free_claude_code/api/admin_routes.py | Adds canonical model catalog endpoints and provider refresh failure metadata. | | src/free_claude_code/providers/runtime/discovery.py | Tracks provider refresh outcomes and separates cache eligibility from discovery eligibility. | | src/free_claude_code/providers/runtime/model_cache.py | Scopes cached model metadata to currently available providers and removes stale remote entries. | | src/free_claude_code/runtime/provider_manager.py | Reconciles cache scope during runtime replacement and returns explicit refresh results. | </details> <sub>Reviews (2): Last reviewed commit: ["Fix model catalog refresh lifecycle"](https://github.com/alishahryar1/free-claude-code/commit/d16e170055f5389e538e18dece269a5f7a8c599d) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=44373795)</sub> <!-- /greptile_comment --> |
||
|
|
12621477c9 |
Fix cross-drive messaging startup with native Claude plan storage (#1085)
## Problem Messaging startup computed a relative path from `ALLOWED_DIR` to FCC's plan directory. Windows raises before Telegram or Discord polling starts when those paths are on different drives, while Claude requires custom plan directories to remain inside the project root. Fixes #1077. ## Changes | Before | After | | --- | --- | | Runtime startup converted FCC's plan path relative to the customer workspace. | Runtime startup no longer computes or transports a custom plan path. | | Managed Claude configuration carried `plans_directory` through four ownership layers. | Managed Claude relies on its native user-level plan storage. | | `~/.fcc/agent_workspace` was described as a Claude workspace. | The existing path is explicitly owned as messaging state for `sessions.json`. | | Cross-volume `ALLOWED_DIR` values could disable messaging startup. | `ALLOWED_DIR` remains the project workspace regardless of filesystem volume. | |
||
|
|
aba94d5c3c |
Disable nonessential Anthropic traffic for FCC Claude sessions (#1083)
## Problem Claude proxy environment policy was duplicated between `fcc-claude`, managed messaging, and live smoke drivers. Managed messaging preserved a legacy endpoint variable, inherited more Anthropic state, and did not disable nonessential traffic, so FCC-launched Claude sessions could drift apart. ## Changes | Before | After | | --- | --- | | `fcc-claude` and managed messaging assembled proxy environments independently. | One shared owner strips inherited Anthropic variables and configures proxy URL, auth, discovery, compaction, and nonessential-traffic policy. | | Managed messaging carried a `/v1` API URL and converted it back to a proxy root while also setting a legacy endpoint variable. | Managed messaging receives the loopback-safe proxy root and uses the supported `ANTHROPIC_BASE_URL` contract directly. | | Managed and interactive policy could diverge while smoke drivers duplicated both shapes. | `fcc-claude`, messaging, and Claude smoke drivers use the same canonical environment builder. | | Managed execution concerns were mixed with shared proxy policy. | Messaging adds only noninteractive process settings and keeps `--model opus` plus stream-JSON flags in command construction. | | IDE examples left nonessential Anthropic traffic enabled. | VS Code and JetBrains examples disable nonessential Anthropic traffic. | <!-- greptile_comment --> <details open><summary><h3>Greptile Summary</h3></summary> This PR centralizes Claude Code proxy environment setup for FCC-launched sessions. The main changes are: - Adds one shared builder for Claude proxy environment variables. - Routes managed messaging sessions through the same proxy policy as `fcc-claude`. - Strips inherited `ANTHROPIC_*` state before launching Claude. - Sets the nonessential-traffic disable flag for managed and interactive Claude launches. - Updates smoke tests, docs, and version metadata for the new proxy-root URL shape. </details> <h3>Confidence Score: 5/5</h3> This looks safe to merge. No blocking issues were found in the changed code. Managed Claude launches now use the shared environment builder, and the managed path now sets the nonessential-traffic disable flag. No files need attention. <details><summary><h3><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="20" align="absmiddle"></a> T-Rex Logs</h3></summary> **What T-Rex did** - The executable harness claude\_env\_policy\_harness.py was generated to enable direct module-level runtime proof without requiring a real Claude binary or live Anthropic credentials. - A focused pytest run was executed, and it completed with 14 tests passing and exit code 0, validating the harness workflow. <a href="https://app.greptile.com/trex/runs/14173386/artifacts"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img alt="View all artifacts" src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a> <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> </details> <details open><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | src/free_claude_code/cli/claude_env.py | Adds the shared Claude proxy environment builder and canonical traffic-disable policy. | | src/free_claude_code/cli/managed/claude.py | Delegates managed Claude environment construction to the shared proxy builder. | | src/free_claude_code/runtime/application.py | Passes the loopback-safe proxy root into the managed Claude session manager. | | src/free_claude_code/cli/managed/session.py | Renames managed session URL state to use the proxy-root contract. | | src/free_claude_code/cli/managed/manager.py | Carries the proxy-root URL through manager-created managed sessions. | </details> <sub>Reviews (2): Last reviewed commit: ["Keep README focused on client setup"](https://github.com/alishahryar1/free-claude-code/commit/83739a2f8953771ec9e447fc83a2057b08708891) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=43684511)</sub> <!-- /greptile_comment --> |
||
|
|
3fba1c6fc3 |
Give messaging clear exact subtree semantics (#1072)
## Problem Messaging `/clear` did not follow the selected platform message boundary. Reply clears preserved the selected user prompt, while standalone clears preserved user-authored messages and could reset FCC state outside the invoking chat. ## Changes | Before | After | | --- | --- | | Reply `/clear` removed a logical conversation branch but retained the selected message. | Reply `/clear` deletes the selected message and its literal reply subtree, including the clear command. | | Standalone `/clear` retained user prompts and voice notes while resetting global messaging state. | Standalone `/clear` deletes every tracked message and resets FCC state only in the invoking platform and chat. | | Trees recorded only logical execution parentage. | Trees separately persist logical execution ancestry and exact prompt/status reply ownership. | | Clear coordination used one global admission boundary. | Per-chat clear generations coordinate admission, voice cancellation, persistence, and best-effort platform deletion. | | Persistence tracked only FCC-authored clearable output. | Persistence tracks managed inbound and outbound messages and migrates legacy entries. | <!-- greptile_comment --> <details open><summary><h3>Greptile Summary</h3></summary> This PR gives messaging `/clear` exact per-chat and reply-subtree behavior. The main changes are: - Per-chat clear generations for admission and startup-notice cleanup. - Managed inbound and outbound message tracking for deletion. - Exact prompt/status reply ownership in message trees. - Scoped voice cancellation and clear persistence updates. - Updated docs, smoke coverage, and messaging tests. </details> <h3>Confidence Score: 4/5</h3> The clear flow is mostly well-contained, with one upgrade-path issue in legacy tree restoration. Newly created prompt/status subtrees use the new exact reference fields consistently, and legacy snapshots can map old status replies to prompt references. However, reply `/clear` on an upgraded status can miss descendants and leave stale state/messages. src/free_claude_code/messaging/trees/snapshot.py <details><summary><h3><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="20" align="absmiddle"></a> T-Rex Logs</h3></summary> **What T-Rex did** - T-Rex attempted to prepare and run a focused legacy snapshot reproduction harness for legacy status replies detach, but tool access was blocked before execution. - A messaging clear smoke test harness was executed and reported a passing result: 20 items collected and 20 passed in 1.69 seconds, with traces for test\_reply\_clear\_uses\_literal and related paths shown in the log. <a href="https://app.greptile.com/trex/runs/14131337/artifacts"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img alt="View all artifacts" src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a> <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> </details> <details open><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | src/free_claude_code/messaging/workflow.py | Adds per-chat clear generations, managed inbound recording, scoped clears, and startup-notice invalidation. | | src/free_claude_code/messaging/trees/graph.py | Adds exact prompt/status reference resolution and literal reply-subtree traversal. | | src/free_claude_code/messaging/trees/runtime.py | Adds exact message-subtree removal and status-only clearing behavior. | | src/free_claude_code/messaging/trees/snapshot.py | Adds parent_reference_id persistence and legacy fallback; the fallback can miss legacy status-reply descendants. | | src/free_claude_code/messaging/session/managed_message_log.py | Replaces the clearable output log with managed inbound and outbound message tracking. | | src/free_claude_code/messaging/commands.py | Routes reply and standalone `/clear` through the new exact deletion ID flows. | </details> <details open><summary><h3>Flowchart</h3></summary> <a href="#gh-light-mode-only"> ```mermaid %%{init: {'theme': 'neutral'}}%% flowchart TD A[Incoming message] --> B{Standalone /clear?} B -- yes --> C[Clear invoking chat] C --> D[Cancel scoped voice work] C --> E[Collect managed and tree message IDs] C --> F[Advance chat clear generation] F --> G[Detach scoped trees] G --> H[Clear scoped session store] H --> I[Best-effort platform deletes] B -- no --> J[Record managed inbound message] J --> K[Admit with stop and clear token] K --> L{Reply /clear?} L -- yes --> M[Resolve exact prompt or status reference] M --> N[Remove literal reference subtree] N --> I L -- no --> O[Queue or run tree node] ``` </a> <a href="#gh-dark-mode-only"> ```mermaid %%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% flowchart TD A[Incoming message] --> B{Standalone /clear?} B -- yes --> C[Clear invoking chat] C --> D[Cancel scoped voice work] C --> E[Collect managed and tree message IDs] C --> F[Advance chat clear generation] F --> G[Detach scoped trees] G --> H[Clear scoped session store] H --> I[Best-effort platform deletes] B -- no --> J[Record managed inbound message] J --> K[Admit with stop and clear token] K --> L{Reply /clear?} L -- yes --> M[Resolve exact prompt or status reference] M --> N[Remove literal reference subtree] N --> I L -- no --> O[Queue or run tree node] ``` </a> </details> <a href="https://app.greptile.com/api/ide/codex?prompt=IMPORTANT%3A%20Work%20in%20the%20repository%20%22alishahryar1%2Ffree-claude-code%22%20on%20the%20existing%20branch%20%22ali%2Fclear-message-subtree%22.%20Checkout%20that%20branch%20%E2%80%94%20do%20NOT%20create%20a%20new%20branch%20or%20open%20a%20new%20PR.%20Push%20your%20changes%20to%20%22ali%2Fclear-message-subtree%22.%0A%0AFix%20the%20following%201%20code%20review%20issue.%20Work%20through%20them%20one%20at%20a%20time%2C%20proposing%20concise%20fixes.%0A%0A---%0A%0A%23%23%23%20Issue%201%20of%201%0Asrc%2Ffree_claude_code%2Fmessaging%2Ftrees%2Fsnapshot.py%3A192-193%0A**Legacy%20Status%20Replies%20Detach**%0A%0AWhen%20an%20upgraded%20legacy%20snapshot%20contains%20a%20child%20that%20originally%20replied%20to%20its%20parent%20status%2C%20this%20fallback%20rewrites%20the%20missing%20exact%20reference%20to%20the%20parent%20prompt.%20A%20later%20reply%20%60%2Fclear%60%20on%20that%20status%20traverses%20from%20the%20status%20ID%2C%20finds%20no%20migrated%20child%20edge%2C%20and%20leaves%20the%20old%20status-reply%20descendants%20and%20their%20managed%20messages%20behind.%0A%0A&repo=alishahryar1%2Ffree-claude-code&pr=1072&platform=github"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodexDark.svg?v=6"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=6"><img alt="Fix All in Codex" src="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=6"></picture></a> <sub>Reviews (1): Last reviewed commit: ["Give messaging clear exact subtree seman..."](https://github.com/alishahryar1/free-claude-code/commit/0a3baecf9470da4bb866c864c1d1f01abd517085) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=43593126)</sub> > Greptile also left **1 inline comment** on this PR. <!-- /greptile_comment --> |
||
|
|
f3ea35a777 |
Consolidate OpenAI-compatible provider adapters (#1069)
## Problem Sixteen OpenAI-compatible providers were represented by configuration-only subclasses and one factory function each. The sole provider family also lived under a multi-transport namespace that no longer described the codebase, while provider defaults, IDs, and instance caching passed through redundant forwarding layers. ## Changes | Before | After | | --- | --- | | Sixteen provider IDs used configuration-only subclasses. | Immutable OpenAI-chat profiles configure one concrete provider while preserving each provider's request policy. | | Every provider required a dedicated factory function. | Generic profile construction is the default; only eight adapters with real state or algorithms retain factories. | | The sole provider family lived under `providers/transports/`. | `providers/openai_chat/` directly owns shared request, stream, recovery, tool, and usage behavior. | | Provider defaults and IDs passed through forwarding modules, and constructors repeated fallback resolution. | The neutral catalog resolves complete immutable provider configuration once. | | `ProviderRuntime` wrapped a pass-through provider cache. | `ProviderRuntime` directly owns lazy provider instances and cleanup. | | Production carried the duplicated adapter structure. | The final shape removes a net 873 production lines with no compatibility shim or customer-facing provider change. | <!-- greptile_comment --> <details open><summary><h3>Greptile Summary</h3></summary> This PR consolidates OpenAI-compatible providers behind shared profiles and runtime construction. The main changes are: - OpenAI-chat behavior moved into `providers/openai_chat`. - Configuration-only provider subclasses replaced by immutable profiles. - Provider runtime now owns lazy instance caching and cleanup directly. - Provider defaults and IDs now resolve through the neutral catalog. - Tests and smoke helpers updated for the new provider shape. </details> <h3>Confidence Score: 5/5</h3> This looks safe to merge. No blocking issues found in the changed code. No files need attention. <details><summary><h3><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="20" align="absmiddle"></a> T-Rex Logs</h3></summary> **What T-Rex did** - T-Rex produced a proof for a posted P1 finding and linked it to the corresponding review comment for details. - T-Rex saved contract-validation logs for provider consolidation and preserved four log files that show the test hanging boundary and the timeout exit code 124. <a href="https://app.greptile.com/trex/runs/14125428/artifacts"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img alt="View all artifacts" src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a> <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> </details> <details open><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | src/free_claude_code/providers/runtime/config.py | Builds resolved provider configuration from catalog descriptors, including static credentials for local providers. | | src/free_claude_code/providers/openai_chat/provider.py | Creates shared OpenAI-chat clients from resolved configuration and immutable provider profiles. | | src/free_claude_code/providers/openai_chat/profiles.py | Defines declarative profiles for formerly configuration-only OpenAI-compatible adapters. | </details> <!-- greptile_failed_comments --> <h3>Comments Outside Diff (2)</h3> 1. General comment <a href="#"><img alt="P1" src="https://greptile-static-assets.s3.amazonaws.com/badges/p1.svg?v=9" align="top"></a> **NIM OpenAI-compatible 504 exhausted streaming retry path hangs instead of completing with the expected user-facing error** - **Bug** - The broader provider consolidation suite failed all parametrized `test_nim_stream_openai_5xx_exhausted_emits_user_message` cases under xdist with worker crashes. A serial isolation run narrowed this to the same OpenAI-compatible NIM exhausted 5xx streaming path: 500, 502, and 503 completed, but the 504 case did not finish before the explicit 120s timeout, producing exit code 124. This contradicts the expected streaming retry contract that exhausted transient provider failures terminate and emit the configured user-facing error. - **Cause** - The OpenAI-compatible streaming retry/exhaustion path for NIM 504 responses appears to wait indefinitely or otherwise fail to terminate after retries are exhausted. The exact code-level loop/await point was not isolated within the validation budget, but the failure is anchored to the provider streaming retry contract exercised by `tests/providers/test_openai_compat_5xx_retry.py::test_nim_stream_openai_5xx_exhausted_emits_user_message[504-temporarily unavailable]`. - **Fix** - Inspect the OpenAI-compatible/NIM streaming retry exhaustion handling for 504 responses and ensure retry limits are enforced, the async stream is closed/cancelled on exhaustion, and the provider raises/emits the same terminal user-facing error contract as the 500/502/503 cases. Add or keep a serial regression test for the 504 exhausted stream path to prevent hangs. <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> 2. General comment <a href="#"><img alt="P1" src="https://greptile-static-assets.s3.amazonaws.com/badges/p1.svg?v=9" align="top"></a> **OpenAI-compatible 5xx retry suite hangs on exhausted 502 retry path** - **Bug** - The recommended non-xdist retry validation does not complete. A verbose rerun with a 60 second timeout shows the suite passes tests through the exhausted 500 case, then times out while running `test_nim_stream_openai_5xx_exhausted_emits_user_message[502-temporarily unavailable]`. This indicates the consolidated OpenAI-compatible provider retry/error path can hang for at least the 502 exhausted-error scenario, preventing reliable validation and potentially blocking callers from receiving the expected `ExecutionFailure`. - **Cause** - The OpenAI-compatible provider's exhausted 502 retry/error handling path appears not to terminate promptly under the mocked repeated `openai.InternalServerError` scenario. The exact code location was not changed during validation, but the failure is isolated to the shared OpenAI-chat 5xx retry behavior exercised by `NvidiaNimProvider.stream_response`. - **Fix** - Debug the shared OpenAI-chat retry loop/error classification for 502 responses. Ensure retries are bounded, patched `asyncio.sleep` is awaited without real backoff during tests, and exhausted 502/503/504 errors consistently raise `ExecutionFailure` with the expected temporary-unavailable message instead of continuing work indefinitely. <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> <!-- /greptile_failed_comments --> <sub>Reviews (2): Last reviewed commit: ["Prove local provider credential resoluti..."](https://github.com/alishahryar1/free-claude-code/commit/ba226d97efdae1889bea0372c7bacf268122d65c) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=43580983)</sub> <!-- /greptile_comment --> |
||
|
|
e37b504636 |
Preserve user messages during messaging clear (#1068)
## Problem Messaging `/clear` used one untyped collection for both internal reply references and platform deletion targets. Clearing a branch or cancelling a voice task could therefore delete the customer's prompt or voice note along with FCC's own status and reply messages. ## Changes | Before | After | | --- | --- | | Tree transitions exposed one message-ID set for repository unindexing and platform deletion. | Tree transitions now separate internal `reference_ids` from FCC-owned `clearable_message_ids`. | | Reply and global clear deleted user prompts and voice notes with FCC output. | Clear removes FCC statuses, replies, notices, and the explicit `/clear` command while preserving user-authored messages. | | The persisted message log accepted ordinary inbound content. | The clearable-message log accepts only FCC output and explicit clear commands, and drops legacy user-content entries when loading. | | Tests treated user-message deletion as successful cleanup. | Deterministic and live messaging coverage enforce preservation across branch, global, and voice clear paths. | <!-- greptile_comment --> <details open><summary><h3>Greptile Summary</h3></summary> This PR narrows messaging clear behavior so user-authored messages are preserved. The main changes are: - Clearable platform IDs are separated from internal tree reference IDs. - The session message log now tracks FCC-owned output and explicit clear commands. - Branch, global, and voice clear paths now avoid deleting user prompts and voice notes. - Tests, smoke coverage, docs, and the package version were updated. </details> <h3>Confidence Score: 5/5</h3> This looks safe to merge after a small migration cleanup. The clear paths now preserve user-authored messages, and current clearable-log writers use the new shape consistently. src/free_claude_code/messaging/session/clearable_message_log.py needs a migration cleanup for old retained clear-command IDs during session reload. <details><summary><h3><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="20" align="absmiddle"></a> T-Rex Logs</h3></summary> **What T-Rex did** - Validated the messaging-clear-preservation contract by reviewing the foreground pytest run log, which captured the exact command, working directory, full test output, and exit code, and by examining the artifact note and its capture log that summarize the command, test count, exit code, and scope. <a href="https://app.greptile.com/trex/runs/14123111/artifacts"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img alt="View all artifacts" src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a> <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> </details> <details open><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | src/free_claude_code/messaging/session/clearable_message_log.py | Replaces the broad message log with a clearable-message log, but the migration filter can drop old retained clear-command IDs. | | src/free_claude_code/messaging/session/store.py | Renames the session-store API around clearable message IDs while keeping the same persisted `message_log` key. | | src/free_claude_code/messaging/trees/runtime.py | Returns internal reference IDs separately from FCC-owned deletion IDs during branch removal and chat-wide enumeration. | | src/free_claude_code/messaging/trees/manager.py | Uses reference IDs for repository cleanup and returns clearable IDs for platform deletion. | | src/free_claude_code/messaging/turn_intake.py | Stops recording ordinary inbound content and records clear commands only when needed for cleanup. | | src/free_claude_code/messaging/commands.py | Deletes clearable IDs plus the invoking clear command instead of deleting all branch reference IDs. | | src/free_claude_code/messaging/workflow.py | Aggregates clearable IDs from tree state, the session log, and voice cancellation results. | | src/free_claude_code/messaging/voice.py | Changes voice cancellation deletion ownership so only FCC-authored status messages are clearable. | </details> <details open><summary><h3>Flowchart</h3></summary> <a href="#gh-light-mode-only"> ```mermaid %%{init: {'theme': 'neutral'}}%% flowchart TD A[Incoming message] --> B{Clear command?} B -- No --> C[Handle normal turn] C --> D[Record FCC outbound status] B -- Reply clear --> E[Record clear command] E --> F[Clear branch or voice task] F --> G[Delete FCC-owned IDs plus clear command] B -- Global clear --> H[Collect clearable IDs] H --> I[Reset conversation state] I --> J[Delete FCC-owned IDs plus clear command] D --> K[Clearable-message log] K --> H ``` </a> <a href="#gh-dark-mode-only"> ```mermaid %%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% flowchart TD A[Incoming message] --> B{Clear command?} B -- No --> C[Handle normal turn] C --> D[Record FCC outbound status] B -- Reply clear --> E[Record clear command] E --> F[Clear branch or voice task] F --> G[Delete FCC-owned IDs plus clear command] B -- Global clear --> H[Collect clearable IDs] H --> I[Reset conversation state] I --> J[Delete FCC-owned IDs plus clear command] D --> K[Clearable-message log] K --> H ``` </a> </details> <a href="https://app.greptile.com/api/ide/codex?prompt=IMPORTANT%3A%20Work%20in%20the%20repository%20%22alishahryar1%2Ffree-claude-code%22%20on%20the%20existing%20branch%20%22ali%2Fpreserve-user-messages-on-clear%22.%20Checkout%20that%20branch%20%E2%80%94%20do%20NOT%20create%20a%20new%20branch%20or%20open%20a%20new%20PR.%20Push%20your%20changes%20to%20%22ali%2Fpreserve-user-messages-on-clear%22.%0A%0AFix%20the%20following%201%20code%20review%20issue.%20Work%20through%20them%20one%20at%20a%20time%2C%20proposing%20concise%20fixes.%0A%0A---%0A%0A%23%23%23%20Issue%201%20of%201%0Asrc%2Ffree_claude_code%2Fmessaging%2Fsession%2Fclearable_message_log.py%3A36-37%0A**Legacy%20Clear%20Commands%20Are%20Dropped**%0A%0AWhen%20an%20existing%20session%20file%20contains%20a%20previously%20retained%20clear%20command%20from%20the%20old%20log%2C%20it%20is%20stored%20as%20%60direction%3D%22in%22%60%20and%20%60kind%3D%22command%22%60.%20This%20new%20load%20filter%20drops%20that%20entry%20because%20it%20only%20keeps%20%60kind%3D%22clear_command%22%60%2C%20so%20a%20failed%20or%20cancelled%20%60%2Fclear%60%20command%20recorded%20before%20the%20upgrade%20is%20no%20longer%20retried%20by%20the%20next%20clear%20and%20remains%20on%20the%20platform.%0A%0A&repo=alishahryar1%2Ffree-claude-code&pr=1068&platform=github"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodexDark.svg?v=6"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=6"><img alt="Fix All in Codex" src="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=6"></picture></a> <sub>Reviews (1): Last reviewed commit: ["Preserve user messages during clear"](https://github.com/alishahryar1/free-claude-code/commit/af7a910facaa29a5ca8f7fb95ec2faae28798e62) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=43576921)</sub> > Greptile also left **1 inline comment** on this PR. **Context used:** - Context used - CLAUDE.md ([source](https://app.greptile.com/alishahryar1/github/Alishahryar1/free-claude-code/-/custom-context?memory=d2fd24d8-0dec-4faf-8ee4-e085e215a2f8)) <!-- /greptile_comment --> |
||
|
|
ef76bddd58 |
Make Telegram startup notices clearable (#1066)
## Problem Telegram's online notice was sent directly by the SDK runtime and its message ID was discarded, so `/clear` could not delete it. Moving delivery into the workflow also needs to keep slow sends from blocking commands and prevent acknowledged notices from losing clear ownership. ## Changes | Before | After | | --- | --- | | The Telegram runtime sent a transport-specific startup side effect. | The platform declares a semantic notice intent that the application gives to the workflow after transport readiness. | | Startup delivery bypassed the persisted message log. | The workflow renders and records each acknowledged notice in the same bounded log used by `/clear`. | | Serializing send and record held workflow state across platform I/O. | A dedicated clear generation reserves publication, delivery runs outside the state lock, and a short receipt finalizer commits or compensates. | | Concurrent clear, cancellation, or record failure could leave a delivered notice unowned. | Clear or cancellation deletes a late receipt; record failure deletes it; failed deletion restores tracking for a later `/clear`. | | A standalone `/clear` command could evict an older target at the log cap. | Successful standalone clear owns its command ID directly, while failed or cancelled clear records it for the next attempt. | | Startup ownership races were implicit. | Deterministic race, failure, cap, restart, and product-smoke coverage enforce the final state machine in version 3.5.10. | <!-- greptile_comment --> <details open><summary><h3>Greptile Summary</h3></summary> This PR makes Telegram startup notices clearable through the messaging workflow. The main changes are: - Moves the Telegram online notice out of the SDK runtime and into workflow-owned publication. - Adds a startup-notice intent to platform composition and publishes it after runtime start and restored-status repair. - Records delivered startup notice IDs for later `/clear` ownership, with delete compensation on interrupted ownership transfer. - Defers standalone `/clear` command ID recording so it cannot evict older deletion targets at the log cap. - Adds tests and smoke coverage for startup notice clearing, cancellation, failures, cap pressure, persistence, and startup ordering. - Bumps the package version and lockfile to 3.5.10. </details> <h3>Confidence Score: 5/5</h3> This looks safe to merge. No blocking issues were found in the changed code. No files need attention. <details><summary><h3><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="20" align="absmiddle"></a> T-Rex Logs</h3></summary> **What T-Rex did** - A focused proof run for the telegram startup tests completed, showing 121 tests passed in 2.94 seconds with EXIT\_CODE 0. - A smoke proof run for the same flow completed, showing 18 tests skipped in 0.97 seconds with EXIT\_CODE 0. - The shell wrapper issue was addressed by re-running with bash -lc, producing a clean result with EXIT\_CODE 0 in the final artifact. <a href="https://app.greptile.com/trex/runs/14118230/artifacts"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img alt="View all artifacts" src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a> <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> </details> <details open><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | src/free_claude_code/messaging/workflow.py | Adds workflow-owned startup notice sending, tracking, compensation, and clear-generation ordering. | | src/free_claude_code/messaging/turn_intake.py | Defers standalone `/clear` command ID recording until failure or cancellation paths need it. | | src/free_claude_code/runtime/application.py | Publishes optional startup notices after messaging runtime start and restored-status repair. | | src/free_claude_code/messaging/platforms/factory.py | Creates a Telegram startup-notice intent when an allowed Telegram user is configured. | | src/free_claude_code/messaging/platforms/telegram.py | Removes the direct Telegram runtime startup-message side effect. | </details> <sub>Reviews (2): Last reviewed commit: ["Make Telegram startup notices clearable"](https://github.com/alishahryar1/free-claude-code/commit/6e779006e0cdaf1df24c27a8d04784e2d7220a66) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=43565904)</sub> <!-- /greptile_comment --> |
||
|
|
eda8ea3060 |
Use task status as the sole stop confirmation (#1065)
## Problem Messaging `/stop` edited each affected task status to `Stopped` and also posted a second success message. The duplicate confirmation added noise even though the existing status already represented the terminal result. ## Changes | Before | After | | --- | --- | | Successful active, queued, global, and bound-voice stops posted a second confirmation. | Successful stops use the affected task status as their sole success UI. | | Stop commands returned an ambiguous integer or `None`. | A typed `StopOutcome` carries the cancelled count and terminal status ownership. | | Statusless voice cancellation could become silent if confirmations were removed unconditionally. | Statusless voice cancellation receives one fallback confirmation. | | A global stop could under-report work when any affected status was in another chat. | The invoking chat receives one summary whenever any affected status is outside its scope. | | Zero-work global stops reported that zero requests were cancelled. | No-op global and reply stops report that there was nothing to stop. | | Runtime and product coverage encoded the duplicate message. | Runtime mapping and Discord/Telegram product smokes cover active, queued, voice, no-op, and fallback behavior. | | The package version was 3.5.8. | The package version is 3.5.9 with an updated lockfile. | <!-- greptile_comment --> <details open><summary><h3>Greptile Summary</h3></summary> This PR changes `/stop` so task statuses become the main success feedback. The main changes are: - Added a typed stop outcome for cancelled counts and status feedback ownership. - Suppressed duplicate stop confirmations when the invoking chat already has complete status feedback. - Kept explicit fallback messages for no-op stops, statusless voice cancellations, and cross-chat stop results. - Updated runtime mapping, docs, product smokes, tests, and the package version. </details> <h3>Confidence Score: 5/5</h3> This looks safe to merge. No blocking issues found in the changed code. None. <details><summary><h3><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="20" align="absmiddle"></a> T-Rex Logs</h3></summary> **What T-Rex did** - Ran the live messaging product smoke test with FCC\_LIVE\_SMOKE=1 and FCC\_SMOKE\_TARGETS=messaging, executing the command uv run pytest under /home/user/repo; the run exited with code 0 and 17 tests passed in 1.34s. - Reviewed the test run log to verify proper shutdown behavior, noting Discord and Telegram stop statuses, a queued trace, and cancellation messages indicating shutdown tasks were being canceled. - Linked the stop-product-smoke-20260711.log artifact for review of the run's stop behavior. <a href="https://app.greptile.com/trex/runs/14115543/artifacts"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img alt="View all artifacts" src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a> <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> </details> <details open><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | src/free_claude_code/messaging/command_context.py | Adds `StopOutcome` and updates stop command context signatures. | | src/free_claude_code/messaging/commands.py | Routes stop confirmations through the new outcome and fallback rules. | | src/free_claude_code/messaging/workflow.py | Builds stop outcomes from voice cancellations and tree cancellation effects. | | src/free_claude_code/runtime/application.py | Maps the messaging stop outcome back to the runtime stop result count. | | tests/messaging/test_handler.py | Covers same-chat status feedback, cross-chat fallback, no-op stops, and voice fallback behavior. | | smoke/product/test_messaging_product_live.py | Updates product smokes for status-only stop success feedback. | </details> <sub>Reviews (2): Last reviewed commit: ["Use task status as the sole stop confirm..."](https://github.com/alishahryar1/free-claude-code/commit/98f0acb01b3405c2afd918539eff49b9410bf487) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=43560341)</sub> <!-- /greptile_comment --> |
||
|
|
3081a72f41 |
Make application shutdown completion-driven (#1056)
## Problem\n\nShutdown could report success after bounded messaging cleanup, hidden persistence failures, or failed managed-process stops. An Admin restart could then construct a replacement while the old runtime still owned work.\n\n## Changes\n\n| Before | After |\n| --- | --- |\n| Runtime reused bounded interactive stop semantics for terminal messaging cleanup. | Workflow close cancels work, stops managed sessions, awaits every claim and recovery task, then flushes persistence. |\n| Explicit persistence failures were logged and treated as successful writes. | Explicit flushes and authoritative writes propagate failure and stay dirty for retry; timer writes remain best effort. |\n| Managed sessions and aliases were removed before subprocess termination was confirmed. | Manager and session terminal states prevent reuse, retain failed owners and PIDs, reject ID collisions, and retry exact sessions. |\n| Admin restart followed the restart request even after incomplete shutdown. | Supervisor restarts only when the prior runtime reports its entire ownership graph closed. |\n| Partial messaging startup cleanup could fail while application startup continued. | Incomplete partial cleanup fails startup and retains the exact graph for a later close attempt. |\n| Messaging task failures read process-global settings. | Runtime injects diagnostic policy and the messaging package depends only on core. |\n| Lifecycle edge cases were verified only in isolated components. | Deterministic and live product coverage proves composed retry, drain, privacy, and customer command behavior. | <!-- greptile_comment --> <details open><summary><h3>Greptile Summary</h3></summary> This PR makes application shutdown wait for owned work to finish before restart or exit. The main changes are: - Runtime close now waits for messaging work, managed sessions, and persistence flushes. - Admin restart now requires the previous runtime to report full closure. - Managed Claude sessions now keep aliases and PIDs until stop is confirmed. - Session persistence now propagates explicit write failures and keeps dirty state for retry. - Messaging no longer reads global config from task-failure callbacks. </details> <h3>Confidence Score: 5/5</h3> This looks safe to merge. No blocking issues found in the changed code. No files need attention. <details><summary><h3><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="20" align="absmiddle"></a> T-Rex Logs</h3></summary> **What T-Rex did** - I reviewed the general contract validation proof and confirmed that the lifecycle\_session pytest run completed with 73 passed in 2.68s (EXIT\_CODE: 0) and the messaging pytest run completed with 95 passed in 1.96s (EXIT\_CODE: 0). <a href="https://app.greptile.com/trex/runs/14094038/artifacts"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img alt="View all artifacts" src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a> <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> </details> <details open><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | src/free_claude_code/messaging/trees/processor.py | Completion callback failures now release task ownership and surface through the idle waiter. | | src/free_claude_code/messaging/workflow.py | Terminal workflow close now cancels tasks, waits for processor cleanup, and flushes persistence. | | src/free_claude_code/runtime/application.py | Runtime shutdown now keeps incomplete ownership cleanup retryable and exposes closure state. | | src/free_claude_code/cli/entrypoints.py | The supervisor now restarts only after the old runtime reports full closure. | | src/free_claude_code/cli/managed/manager.py | Managed session shutdown now blocks reuse and retains failed owners for retry. | | src/free_claude_code/cli/managed/session.py | Managed sessions now mark terminal state under a lifecycle lock and retain PID ownership until exit. | </details> <!-- greptile_failed_comments --> <h3>Comments Outside Diff (1)</h3> 1. `src/free_claude_code/messaging/trees/processor.py`, line 194-202 ([link](https://github.com/alishahryar1/free-claude-code/blob/b736bad1aacb66784e7b4d1e09d27a32b2a380c7/src/free_claude_code/messaging/trees/processor.py#L194-L202)) <a href="#"><img alt="P1" src="https://greptile-static-assets.s3.amazonaws.com/badges/p1.svg?v=9" align="top"></a> **Idle Event Stays Cleared** When `_claim_finished_callback` raises a non-cancellation exception, `_finish_and_continue` exits before `slot.transitioned` is set, before the slot is removed from `_tasks`, and before `_idle` is set. `MessagingWorkflow.close()` now waits on `wait_idle()`, so a finish-path error can leave shutdown waiting forever instead of returning a failed close. <details><summary><strong>Artifacts</strong></summary><br /> **[Repro: standalone async harness that drives TreeQueueManager and forces a finish callback RuntimeError](https://app.greptile.com/trex/artifacts/da413ba3-7f69-459d-b8c8-3141c2bb6c41)** - Contains supporting evidence from the run (text/x-python; charset=utf-8). **[Repro: uv run output showing finish callback RuntimeError, retained task\_count, cleared idle event, and wait\_idle timeout](https://app.greptile.com/trex/artifacts/35b2beaa-ec52-47bd-b36b-2735b3ebc62c)** - Keeps the command output available without making the summary code-heavy. <a href="https://app.greptile.com/trex/runs/14093236/artifacts?artifact=da413ba3-7f69-459d-b8c8-3141c2bb6c41"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewArtifactsDark.svg?v=4"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewArtifacts.svg?v=4"><img alt="View artifacts" src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewArtifacts.svg?v=4"></picture></a> </details> <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> <a href="https://app.greptile.com/api/ide/codex?prompt=IMPORTANT%3A%20Work%20in%20the%20repository%20%22alishahryar1%2Ffree-claude-code%22%20on%20the%20existing%20branch%20%22ali%2Fcompletion-driven-shutdown%22.%20Checkout%20that%20branch%20%E2%80%94%20do%20NOT%20create%20a%20new%20branch%20or%20open%20a%20new%20PR.%20Push%20your%20changes%20to%20%22ali%2Fcompletion-driven-shutdown%22.%0A%0AThis%20is%20a%20comment%20left%20during%20a%20code%20review.%0APath%3A%20src%2Ffree_claude_code%2Fmessaging%2Ftrees%2Fprocessor.py%0ALine%3A%20194-202%0A%0AComment%3A%0A**Idle%20Event%20Stays%20Cleared**%0A%0AWhen%20%60_claim_finished_callback%60%20raises%20a%20non-cancellation%20exception%2C%20%60_finish_and_continue%60%20exits%20before%20%60slot.transitioned%60%20is%20set%2C%20before%20the%20slot%20is%20removed%20from%20%60_tasks%60%2C%20and%20before%20%60_idle%60%20is%20set.%20%60MessagingWorkflow.close%28%29%60%20now%20waits%20on%20%60wait_idle%28%29%60%2C%20so%20a%20finish-path%20error%20can%20leave%20shutdown%20waiting%20forever%20instead%20of%20returning%20a%20failed%20close.%0A%0AHow%20can%20I%20resolve%20this%3F%20If%20you%20propose%20a%20fix%2C%20please%20make%20it%20concise.&repo=alishahryar1%2Ffree-claude-code&pr=1056&platform=github"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInCodexDark.svg?v=6"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInCodex.svg?v=6"><img alt="Fix in Codex" src="https://greptile-static-assets.s3.amazonaws.com/badges/FixInCodex.svg?v=6"></picture></a> <!-- /greptile_failed_comments --> <sub>Reviews (2): Last reviewed commit: ["Surface messaging completion failures"](https://github.com/alishahryar1/free-claude-code/commit/39a874c8f0e0614847d80321dd51c9654707a7ff) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=43514893)</sub> <!-- /greptile_comment --> |
||
|
|
e22a38b2c2 |
Canonicalize provider failure and retry ownership (#1046)
## Problem Provider SDK classification, retry policy, canonical failures, and downstream wire errors shared exception types across layers. That blurred ownership and let cleanup or provisional Responses tool failures mask the real provider diagnostic. ## Changes | Before | After | | --- | --- | | Provider failures carried Anthropic wire types and core code classified OpenAI/httpx errors. | Protocol-neutral `ExecutionFailure` values cross layers, providers classify SDK errors, and protocol packages map wire types. | | Provider adapters could author terminal wire events. | The HTTP commit boundary selects non-2xx JSON or a protocol terminal event with one ingress request ID. | | Retry policy and diagnostic handling were spread across core and provider modules. | Providers own the unchanged retry budgets while neutral core utilities own bounded credential redaction. | | Stream cleanup could replace an already-mapped provider failure. | Cleanup records safe metadata and preserves the canonical failure, status, and diagnostic. | | An incomplete Responses tool could preempt a later provider failure. | Tool-finalization errors remain provisional so canonical provider failures take precedence. | | Readiness failures reused provider exception types. | Application-owned errors represent deterministic validation and availability phases without terminal retry headers. | | Legacy exception and recovery owners remained importable. | Obsolete modules are deleted without shims, architecture rules enforce the boundaries, and package version is 3.4.21. | <!-- greptile_comment --> <details open><summary><h3>Greptile Summary</h3></summary> This PR canonicalizes provider failure handling across the API boundary. The main changes are: - Adds protocol-neutral execution failure values and safe diagnostics. - Moves SDK and HTTP failure classification into provider-owned policy. - Lets Messages and Responses choose their own wire error payloads. - Preserves canonical failures across stream cleanup and committed stream failures. - Makes incomplete Responses tool errors provisional until finalization. </details> <h3>Confidence Score: 5/5</h3> This looks safe to merge. No blocking issues found in the changed code. No files need attention. <details><summary><h3><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="20" align="absmiddle"></a> T-Rex Logs</h3></summary> **What T-Rex did** - Ran the API failure contract suite and related tests (tests/api/test\_execution\_failure\_contract.py, tests/core/test\_failure\_protocol\_mapping.py, tests/providers/test\_execution\_failure\_boundary.py, tests/providers/test\_failure\_policy.py); 48 passed in 3.36s. - Ran the streaming boundaries tests including response streams, stream recovery, and streaming errors; 70 passed in 5.36s. - Ran the OpenAI responses tests; 20 passed in 4.42s. <a href="https://app.greptile.com/trex/runs/14071383/artifacts"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img alt="View all artifacts" src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a> <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> </details> <details open><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | src/free_claude_code/providers/transports/http.py | Adds cleanup-safe stream closing that preserves established outcomes. | | src/free_claude_code/core/openai_responses/stream.py | Preserves canonical execution failures when committed Responses streams fail. | | src/free_claude_code/core/openai_responses/streaming/assembler.py | Keeps malformed tool-call errors provisional so later provider failures can win. | | src/free_claude_code/core/failures.py | Defines neutral failure kinds and exception-group lookup for execution failures. | </details> <sub>Reviews (2): Last reviewed commit: ["Preserve canonical outcomes in grouped a..."](https://github.com/alishahryar1/free-claude-code/commit/f57f21241dbe582985627ed4fb40734b2c656809) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=43468297)</sub> <!-- /greptile_comment --> |
||
|
|
f8c21a48f2 |
Introduce a typed application boundary for provider execution (#1045)
## Problem The HTTP adapter owned model routing, provider execution, and runtime-facing contracts, so API handlers depended on provider implementation types. Provider preflight also discovered private request builders dynamically, obscuring the boundary that must fail before streaming begins. ## Changes | Before | After | | --- | --- | | `api/` owned model routing and shared provider execution. | `application/` owns routing and a settings-independent `ProviderExecutor`. | | API handlers accepted `BaseProvider` callbacks. | API handlers consume the narrow structural `ProviderPort`. | | `BaseProvider` discovered `_build_request_body` dynamically. | Both transport families implement explicit abstract preflight, with LM Studio composing context validation. | | Request leases, task control, and provider model metadata had adapter/provider owners. | Application-owned ports and immutable values define those cross-package contracts. | | Boundary direction was implicit. | Architecture contracts and documentation enforce the final dependency direction. | | Package version was `3.4.19`. | Package version is `3.4.20`, with the lockfile updated. | | Coverage followed the old module layout. | Deterministic boundary/preflight regressions and live Messages/Responses smokes cover the new shape. | <!-- greptile_comment --> <details open><summary><h3>Greptile Summary</h3></summary> This PR adds a typed application boundary for provider execution. The main changes are: - New `application` package for routing, execution, ports, and model metadata. - API handlers now call application-owned routing and provider execution. - Provider preflight is now explicit on the transport families. - Runtime API composition now uses a task-control port for `/stop`. - Import-boundary tests, smoke references, docs, version, and lockfile were updated. </details> <h3>Confidence Score: 5/5</h3> This looks safe to merge. No blocking issues found in the changed code. None. <details><summary><h3><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="20" align="absmiddle"></a> T-Rex Logs</h3></summary> **What T-Rex did** - Ran a deterministic pytest run for the provider boundary preflight, which completed with exit code 0 and 102 tests passing in 4.77 seconds. - Launched the environment presence check as part of the preflight, which completed with exit code 0 and confirmed OPENCODE\_API\_KEY=\[REDACTED\] matched. - Attempted the live provider smoke test, which completed with exit code 0 and 2 tests skipped due to incomplete smoke configuration. <a href="https://app.greptile.com/trex/runs/14067905/artifacts"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img alt="View all artifacts" src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a> <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> </details> <details open><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | src/free_claude_code/application/execution.py | Moves shared provider execution into the application layer and keeps eager preflight before token counting and streaming. | | src/free_claude_code/application/ports.py | Adds structural provider, request-runtime, and task-control protocols used across the new boundary. | | src/free_claude_code/api/routes.py | Updates route composition to use the application provider resolver and task-control stop path. | | src/free_claude_code/providers/base.py | Makes provider preflight explicit by requiring subclasses or transport bases to implement it. | | src/free_claude_code/providers/transports/openai_chat/transport.py | Adds OpenAI-chat preflight through the same request-body builder used by streaming. | | src/free_claude_code/providers/transports/anthropic_messages/transport.py | Adds native Messages preflight through the native request-body builder. | | src/free_claude_code/providers/model_listing.py | Keeps provider model-list parsing while moving `ProviderModelInfo` ownership to the application layer. | | src/free_claude_code/runtime/bootstrap.py | Passes the runtime object through the new `tasks` service slot. | | tests/contracts/test_import_boundaries.py | Extends import-boundary tests for the new application package. | </details> <details open><summary><h3>Flowchart</h3></summary> <a href="#gh-light-mode-only"> ```mermaid %%{init: {'theme': 'neutral'}}%% flowchart LR API[api handlers and routes] --> Routing[application.routing] API --> Executor[application.execution] API --> Ports[application.ports] Executor --> ProviderPort[ProviderPort] ProviderPort --> Preflight[preflight_stream] ProviderPort --> Stream[stream_response] Runtime[runtime bootstrap and provider manager] --> Ports Providers[providers] --> Metadata[application.model_metadata] Executor --> Core[core anthropic and trace] Routing --> Config[config settings and model refs] ``` </a> <a href="#gh-dark-mode-only"> ```mermaid %%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% flowchart LR API[api handlers and routes] --> Routing[application.routing] API --> Executor[application.execution] API --> Ports[application.ports] Executor --> ProviderPort[ProviderPort] ProviderPort --> Preflight[preflight_stream] ProviderPort --> Stream[stream_response] Runtime[runtime bootstrap and provider manager] --> Ports Providers[providers] --> Metadata[application.model_metadata] Executor --> Core[core anthropic and trace] Routing --> Config[config settings and model refs] ``` </a> </details> <sub>Reviews (1): Last reviewed commit: ["Introduce typed application boundary"](https://github.com/alishahryar1/free-claude-code/commit/4cdcf97231c812c6f568ca3d74af7ce759d7f2dc) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=43462788)</sub> <!-- /greptile_comment --> |
||
|
|
4951983b5e |
Replace global runtime resources with explicit ownership (#1042)
## Problem Provider, messaging, and transcription resources relied on process-global state, leaving replacement, cancellation, and shutdown ownership ambiguous. Separate server lifetimes could share event-loop-bound resources or retain failed cleanup work. ## Changes | Before | After | | --- | --- | | Provider clients found limiters through global singleton and scoped registries. | Each provider instance receives and owns one explicitly constructed limiter. | | Messaging queues and voice pipelines relied on singleton or module-global state. | Each platform owns its limiter and outbox, while the application owns one injected transcriber. | | Messaging shutdown mixed ingress, active work, delivery, and SDK cleanup. | Application shutdown quiesces ingress, drains work, closes delivery, then releases transcription and providers. | | Cancelled or failed provider cleanup could be forgotten or treated as complete. | The provider manager retains shielded generation and unpublished-runtime cleanup until it succeeds. | | Discord and Telegram startup tasks could outlive or poison runtime readiness. | Platform runtimes observe long-lived tasks and retry only independently repeatable lifecycle steps. | | Constructor-captured security and diagnostic settings appeared hot-applicable. | Admin marks those settings restart-required so applied policy matches the running resource graph. | | Lifecycle races lacked direct ownership coverage. | Deterministic cancellation, retry, isolation, teardown, and live smoke contracts protect the final ownership model. | <!-- greptile_comment --> <details open><summary><h3>Greptile Summary</h3></summary> This PR moves runtime resources from global state into explicitly owned application objects. The main changes are: - Provider generations own their rate limiters and cleanup tasks. - Messaging platforms own their limiter, outbox, ingress, and delivery lifecycle. - Application shutdown now runs through ordered cleanup gates. - Voice transcription is injected as an owned runtime resource. - Admin config marks constructor-captured settings as restart-required. </details> <h3>Confidence Score: 4/5</h3> The shutdown path needs a bounded cleanup result before merging. Cleanup steps that hang never reach the retryable incomplete-shutdown path. ASGI shutdown can remain stuck while waiting for an external SDK, transcriber, workflow, or provider cleanup. The retry ownership model works only after cleanup returns or raises. src/free_claude_code/runtime/application.py <details><summary><h3><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="20" align="absmiddle"></a> T-Rex Logs</h3></summary> **What T-Rex did** - T-Rex ran the requested verification, but its local artifact references were not uploaded. - The validation run completed successfully with EXIT\_CODE: 0 and 62 tests passed in 3.91 seconds, using the command uv run pytest -vv tests/runtime/test\_application\_runtime.py tests/runtime/test\_provider\_manager.py tests/providers/test\_provider\_runtime.py. <a href="https://app.greptile.com/trex/runs/14064214/artifacts"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img alt="View all artifacts" src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a> <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> </details> <details open><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | src/free_claude_code/runtime/application.py | Refactors shutdown into ordered retryable cleanup gates, but cleanup awaitables can still block shutdown forever. | | src/free_claude_code/runtime/asgi.py | Reports incomplete runtime shutdown when `close()` returns false. | | src/free_claude_code/runtime/provider_manager.py | Adds owned provider cleanup retry state and shielded generation cleanup. | </details> <a href="https://app.greptile.com/api/ide/codex?prompt=IMPORTANT%3A%20Work%20in%20the%20repository%20%22alishahryar1%2Ffree-claude-code%22%20on%20the%20existing%20branch%20%22refactor%2Fruntime-owned-resources%22.%20Checkout%20that%20branch%20%E2%80%94%20do%20NOT%20create%20a%20new%20branch%20or%20open%20a%20new%20PR.%20Push%20your%20changes%20to%20%22refactor%2Fruntime-owned-resources%22.%0A%0AFix%20the%20following%201%20code%20review%20issue.%20Work%20through%20them%20one%20at%20a%20time%2C%20proposing%20concise%20fixes.%0A%0A---%0A%0A%23%23%23%20Issue%201%20of%201%0Asrc%2Ffree_claude_code%2Fruntime%2Fapplication.py%3A59%0A**Cleanup%20Await%20Blocks%20Shutdown**%0A%0AWhen%20a%20platform%20SDK%20stop%2C%20workflow%20drain%2C%20transcriber%20close%2C%20or%20provider%20cleanup%20hangs%2C%20this%20helper%20waits%20forever%20and%20never%20returns%20%60False%60.%20ASGI%20shutdown%20stays%20stuck%20in%20%60runtime.close%28%29%60%20instead%20of%20reporting%20an%20incomplete%20shutdown%2C%20so%20the%20retained%20resource%20graph%20cannot%20be%20retried%20cleanly.%0A%0A&repo=alishahryar1%2Ffree-claude-code&pr=1042&platform=github"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodexDark.svg?v=6"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=6"><img alt="Fix All in Codex" src="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=6"></picture></a> <sub>Reviews (2): Last reviewed commit: ["Report incomplete runtime shutdown to AS..."](https://github.com/alishahryar1/free-claude-code/commit/338b2bd179c3875b15bbd52818dd04c780e5d46d) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=43454593)</sub> > Greptile also left **1 inline comment** on this PR. **Context used:** - Context used - CLAUDE.md ([source](https://app.greptile.com/alishahryar1/github/Alishahryar1/free-claude-code/-/custom-context?memory=d2fd24d8-0dec-4faf-8ee4-e085e215a2f8)) <!-- /greptile_comment --> |
||
|
|
160d63370b |
Establish single-owner runtime with stream-safe provider hot swaps (#1036)
## Problem Provider runtime ownership was split between lifecycle code and mutable FastAPI state, so Admin replacements could leak the new runtime, double-close the old runtime, or close providers still serving active streams. The API package also owned concrete process composition, obscuring subsystem boundaries. ## Changes | Before | After | | --- | --- | | FastAPI routes inspected several concrete `app.state` resources. | FastAPI receives one explicit `ApiServices` boundary and stores only `app.state.services`. | | Admin Apply persisted config and directly replaced one runtime reference. | Admin Apply validates a candidate, commits atomically, and publishes it through the single runtime owner. | | Provider replacement could close clients used by active streams. | Generation leases retain old providers until each streaming or non-streaming response finishes. | | Provider generations owned discovery state and model metadata. | `ProviderRuntimeManager` owns one application-lifetime catalog and one discovery task across replacements. | | API modules composed provider, messaging, and managed CLI resources. | `runtime.bootstrap` composes concrete subsystems and `ApplicationRuntime` owns their lifecycle. | | Admin config, server URLs, and gateway model IDs lived under the API package. | Admin config lives under `config`, server URLs live under `config`, and gateway IDs live under `core`. | | Messaging restoration and shutdown persistence were coordinated externally. | `MessagingWorkflow` owns snapshot restoration and final persistence flushing. | | Hot-swap behavior lacked a real process-level race scenario. | Deterministic ownership tests and a credential-free subprocess smoke hold provider A while new requests switch to provider B. | | Package version was `3.4.16`. | Package version is `3.4.17` with an updated lockfile. | <!-- greptile_comment --> <details open><summary><h3>Greptile Summary</h3></summary> This PR centralizes server runtime ownership and provider hot swaps. The main changes are: - Adds `ApplicationRuntime` and `ProviderRuntimeManager` as the process owners. - Moves FastAPI to an explicit `ApiServices` boundary. - Retains provider generations until request and stream responses finish. - Moves admin config, server URL, and gateway model ID modules to neutral package owners. - Updates admin apply to validate, persist, and publish provider-only changes through the runtime owner. - Adds runtime ownership tests and a credential-free smoke scenario. </details> <h3>Confidence Score: 5/5</h3> This looks safe to merge. No blocking issues found in the changed code. The provider lease path releases resources on normal completion, stream close, and cancellation. The admin apply path keeps restart-required changes separate from provider-only hot swaps, and repository import paths appear updated for the moved modules. No files need follow-up attention. <details><summary><h3><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="20" align="absmiddle"></a> T-Rex Logs</h3></summary> **What T-Rex did** - Ran the T-Rex smoke test command and confirmed it completed with exit code 0 and pytest passing. - Monitored runtime ownership activity during the run, including a provider A stream request on model-a generation 1, an admin publish to generation 2, a new request on model-b generation 2, and the completion of the original generation 1 stream. - Collected smoke result artifacts from the .smoke-results area for gateway 1, gateway 0, and main, and made them available for review. - Opened the smoke report JSON artifacts to review the summarized outcomes for each target environment. <a href="https://app.greptile.com/trex/runs/14008910/artifacts"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img alt="View all artifacts" src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a> <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> </details> <details open><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | src/free_claude_code/runtime/provider_manager.py | Adds provider generation ownership, request leases, replacement, discovery refresh, and shutdown cleanup. | | src/free_claude_code/runtime/application.py | Adds the process-level owner for startup, shutdown, admin operations, messaging, and session control. | | src/free_claude_code/api/routes.py | Routes now acquire provider generation leases and bind them to response lifetime. | | src/free_claude_code/api/response_streams.py | Adds response lifetime binding so retained resources release after stream completion, cancellation, or close. | | src/free_claude_code/config/admin/persistence.py | Moves admin config persistence into `config` and adds prepared validation plus atomic managed-env commits. | | src/free_claude_code/runtime/bootstrap.py | Adds the production composition root for logging, runtime owners, services, and ASGI wiring. | | src/free_claude_code/api/__init__.py | Removes package-level API re-exports as part of the HTTP adapter boundary cleanup. | </details> <details open><summary><h3>Sequence Diagram</h3></summary> <a href="#gh-light-mode-only"> ```mermaid %%{init: {'theme': 'neutral'}}%% sequenceDiagram participant Client participant API as FastAPI Route participant Manager as ProviderRuntimeManager participant Lease as Generation Lease participant Runtime as Provider Generation participant Admin as Admin Apply Client->>API: Request /v1/messages or /v1/responses API->>Manager: acquire() Manager-->>API: lease for current generation API->>Lease: resolve_provider() Lease->>Runtime: use provider instance Runtime-->>Client: response body or stream Admin->>Manager: replace(candidate settings) Manager->>Manager: publish new generation Manager->>Manager: retire old generation Client-->>API: response completes or disconnects API->>Lease: release() Lease->>Manager: decrement active leases Manager->>Runtime: cleanup retired generation when drained ``` </a> <a href="#gh-dark-mode-only"> ```mermaid %%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% sequenceDiagram participant Client participant API as FastAPI Route participant Manager as ProviderRuntimeManager participant Lease as Generation Lease participant Runtime as Provider Generation participant Admin as Admin Apply Client->>API: Request /v1/messages or /v1/responses API->>Manager: acquire() Manager-->>API: lease for current generation API->>Lease: resolve_provider() Lease->>Runtime: use provider instance Runtime-->>Client: response body or stream Admin->>Manager: replace(candidate settings) Manager->>Manager: publish new generation Manager->>Manager: retire old generation Client-->>API: response completes or disconnects API->>Lease: release() Lease->>Manager: decrement active leases Manager->>Runtime: cleanup retired generation when drained ``` </a> </details> <sub>Reviews (1): Last reviewed commit: ["refactor: establish single-owner applica..."](https://github.com/alishahryar1/free-claude-code/commit/92fc06aa733b7acc34ad6ea50de8b6b4ce5cfac1) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=43349002)</sub> <!-- /greptile_comment --> |