文件历史

15 次代码提交

作者 SHA1 备注 提交日期
Ali Khokhar 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 -->
2026-07-20 23:53:55 -07:00
Ali Khokhar 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 -->
2026-07-19 22:43:29 -07:00
Ali Khokhar 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 -->
2026-07-15 01:27:36 -07:00
Ali Khokhar 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. |
2026-07-12 16:58:33 -07:00
Ali Khokhar 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 -->
2026-07-12 15:05:54 -07:00
Ali Khokhar 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 -->
2026-07-11 22:26:16 -07:00
Ali Khokhar 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 -->
2026-07-11 19:55:19 -07:00
Ali Khokhar 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 -->
2026-07-11 18:25:20 -07:00
Ali Khokhar 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 -->
2026-07-11 16:27:58 -07:00
Ali Khokhar 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 -->
2026-07-11 15:23:26 -07:00
Ali Khokhar 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 -->
2026-07-11 07:27:28 -07:00
Ali Khokhar 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 -->
2026-07-10 21:33:50 -07:00
Ali Khokhar 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 -->
2026-07-10 20:06:57 -07:00
Ali Khokhar 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 -->
2026-07-10 18:46:50 -07:00
Ali Khokhar 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 -->
2026-07-10 10:52:54 -07:00