文件历史

5 次代码提交

作者 SHA1 备注 提交日期
Ali Khokhar abaee5bd38 Fix messaging task launch on uvloop (#1117)
## Problem

uvloop 0.22.1 rejects the Python 3.14-only `eager_start` task keyword,
so every Telegram and Discord message fails on Unix. Removing the
keyword alone would let eager task factories execute claims before FCC
publishes task ownership. Fixes #1108 and fixes #1115.

## Changes

| Before | After |
| --- | --- |
| Claim tasks depend on `eager_start=False`, which uvloop does not
accept. | Claim tasks use the portable event-loop contract behind an
explicit ownership gate. |
| Task execution ordering depends on event-loop keyword support. | Task
execution begins only after FCC attaches the task and completion
callback. |
| Regression coverage exercises native asyncio only. | Regression
coverage exercises a restricted task contract, eager task factories, and
real uvloop. |
| The package version is 4.5.0. | The package version is 4.5.1 with a
refreshed lockfile. |

<!-- greptile_comment -->

<details open><summary><h3>Greptile Summary</h3></summary>

This PR fixes messaging claim launch on event loops that reject
Python-specific task keywords. The main changes are:

- Removes `eager_start=False` from claim task creation.
- Adds an explicit ownership gate before claim processing starts.
- Adds tests for portable task creation, eager task factories, and
uvloop.
- Bumps the package version and lockfile entry to `4.5.1`.
</details>

<h3>Confidence Score: 5/5</h3>

This looks safe to merge after a small test hardening cleanup.

The runtime task ownership change preserves the intended launch
ordering, and no blocking issues were found in the changed runtime code.

tests/messaging/test_tree_ownership_concurrency.py needs a small
hardening update so the uvloop case skips cleanly when the optional
package is absent.

<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 executed the messaging ownership regression pytest suite to
capture runtime proof for the messaging concurrency changes.
- During the run, the first shell wrapper used PIPESTATUS\[0\] under
/bin/sh, producing a harmless Bad substitution during version evidence
collection.
- The pytest command was rerun after the substitution issue, and the run
completed with EXIT\_CODE: 0.
- The regression suite provides focused runtime evidence for the
messaging concurrency behavior, avoiding unrelated suites or external
services.

<a
href="https://app.greptile.com/trex/runs/14465005/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 | Replaces the
uvloop-incompatible task keyword with an ownership event that opens
after task registration. |
| tests/messaging/test_tree_ownership_concurrency.py | Adds task-launch
tests, with one uvloop test that can fail instead of skip when uvloop is
absent. |
| pyproject.toml | Bumps the package version to `4.5.1`. |
| uv.lock | Updates the editable package version to match
`pyproject.toml`. |

</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%2Ffix-messaging-event-loop-compatibility%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%2Ffix-messaging-event-loop-compatibility%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%0Atests%2Fmessaging%2Ftest_tree_ownership_concurrency.py%3A485%0A**Optional%20Uvloop%20Becomes%20Required**%0A%0AWhen%20tests%20run%20on%20Linux%20or%20macOS%20without%20the%20optional%20%60uvloop%60%20package%20installed%2C%20this%20import%20raises%20%60ModuleNotFoundError%60%20instead%20of%20skipping%20the%20uvloop-only%20case.%20A%20contributor%20or%20CI%20job%20that%20installs%20only%20the%20test%20dependencies%20can%20fail%20this%20test%20even%20though%20the%20platform%20skip%20condition%20passes.%0A%0A&repo=alishahryar1%2Ffree-claude-code&pr=1117&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: ["Fix messaging task launch on
uvloop"](https://github.com/alishahryar1/free-claude-code/commit/180a7255df0a43a20706cdde2f1cd2d6830921d7)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=44296382)</sub>

> Greptile also left **1 inline comment** on this PR.

<!-- /greptile_comment -->
2026-07-14 15:28:15 -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 2bcaf3ac74 Make messaging trees atomic ownership boundaries (#1048)
## Problem

Messaging tree state, queue coordination, task ownership, and
persistence were jointly mutated across several classes. Raw message IDs
were treated as globally unique, allowing cross-chat collisions and
unsafe cancellation or clear ordering.

## Changes

| Before | After |
| --- | --- |
| Managers and processors coordinated partial tree mutations through
exposed locks and mutable nodes. | MessageTree owns atomic transitions
and returns detached effects to task, UI, and persistence owners. |
| Raw message IDs and task identities could collide across chats or
detached generations. | Scoped tree identities and opaque claim IDs
isolate chats and reject stale task writes. |
| Session snapshots duplicated graph links and retained ingress
payloads. | Lean scoped snapshots rebuild validated indexes and continue
reading existing session files. |
| Stop, clear, and runner persistence could interleave across commit
boundaries. | Admission epochs, cancellation-safe detach, and
authoritative writes make committed cleanup durable. |
| Tree implementation classes leaked through messaging package exports.
| Adapter-facing values and ports remain supported while tree internals
stay internal. |

<!-- greptile_comment -->

<details open><summary><h3>Greptile Summary</h3></summary>

This PR makes messaging trees own their state transitions and scoped
identities. The main changes are:

- Scoped tree and voice ownership by platform and chat.
- Opaque claim IDs for queued task execution.
- Manager-owned atomic cancellation, clear, and successor task launch
paths.
- Lean scoped snapshots with legacy session restore support.
- Messaging tree internals removed from the public package surface.
</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**
- Executed the messaging and contract-boundary test suite with pytest
across all specified test modules.
- Observed the run completed with 96 tests passing in 4.10 seconds and
an exit code of 0.
- The exact pytest command used for the run is documented in the proof
to enable reproducibility of the test scope.
- The run log is available as an artifact for reviewers to inspect test
output and details.

<a
href="https://app.greptile.com/trex/runs/14083891/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/runtime.py | Adds claim-scoped
state transitions and cancellation tombstones for late runner writes. |
| src/free_claude_code/messaging/trees/manager.py | Centralizes
aggregate publication, branch removal, global clear, and successor task
launch decisions. |
| src/free_claude_code/messaging/trees/processor.py | Runs tasks by
opaque claim ID and routes cleanup through manager-owned finish
handling. |
| src/free_claude_code/messaging/trees/snapshot.py | Serializes scoped
tree snapshots and restores supported legacy tree shapes. |
| src/free_claude_code/messaging/voice.py | Scopes pending voice
registrations by message scope and message ID. |

</details>


<!-- greptile_failed_comments -->
<h3>Comments Outside Diff (1)</h3>

1. `src/free_claude_code/messaging/commands.py`, line 161-164
([link](https://github.com/alishahryar1/free-claude-code/blob/a4e49f082a9d3ffc1f6cdf3ab6a065b0f22a759d/src/free_claude_code/messaging/commands.py#L161-L164))

<a href="#"><img alt="P1"
src="https://greptile-static-assets.s3.amazonaws.com/badges/p1.svg?v=9"
align="top"></a> **Voice Cancellation Is Unscoped**

The tree reply path is now scoped by `incoming.scope`, but the voice
fallback still cancels by only `chat_id` and `reply_id`. If Discord and
Telegram both have the same raw chat/message IDs, a reply `/clear` from
one platform can cancel a pending voice note from the other platform,
crossing the ownership boundary this PR adds for message trees.

   <details><summary><strong>Artifacts</strong></summary><br />

**[Repro: focused pytest harness that models cross-platform raw ID
collision for voice
cancellation](https://app.greptile.com/trex/artifacts/0ad480a9-4e20-4b4e-970e-63aebc4577b3)**

- Contains supporting evidence from the run (text/x-python;
charset=utf-8).

**[Repro: verbose pytest output showing unscoped cancel\_pending\_voice
call and Discord-owned voice cancellation from Telegram
/clear](https://app.greptile.com/trex/artifacts/ead824f3-2f41-4028-a8e3-8359c5f6d3a9)**

- Keeps the command output available without making the summary
code-heavy.

<a
href="https://app.greptile.com/trex/runs/14082218/artifacts?artifact=0ad480a9-4e20-4b4e-970e-63aebc4577b3"><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%22architecture%2Fmessage-tree-ownership%22.%20Checkout%20that%20branch%20%E2%80%94%20do%20NOT%20create%20a%20new%20branch%20or%20open%20a%20new%20PR.%20Push%20your%20changes%20to%20%22architecture%2Fmessage-tree-ownership%22.%0A%0AThis%20is%20a%20comment%20left%20during%20a%20code%20review.%0APath%3A%20src%2Ffree_claude_code%2Fmessaging%2Fcommands.py%0ALine%3A%20161-164%0A%0AComment%3A%0A**Voice%20Cancellation%20Is%20Unscoped**%0A%0AThe%20tree%20reply%20path%20is%20now%20scoped%20by%20%60incoming.scope%60%2C%20but%20the%20voice%20fallback%20still%20cancels%20by%20only%20%60chat_id%60%20and%20%60reply_id%60.%20If%20Discord%20and%20Telegram%20both%20have%20the%20same%20raw%20chat%2Fmessage%20IDs%2C%20a%20reply%20%60%2Fclear%60%20from%20one%20platform%20can%20cancel%20a%20pending%20voice%20note%20from%20the%20other%20platform%2C%20crossing%20the%20ownership%20boundary%20this%20PR%20adds%20for%20message%20trees.%0A%0AHow%20can%20I%20resolve%20this%3F%20If%20you%20propose%20a%20fix%2C%20please%20make%20it%20concise.&repo=alishahryar1%2Ffree-claude-code&pr=1048&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 (4): Last reviewed commit: ["Serialize successor task
publication
wit..."](https://github.com/alishahryar1/free-claude-code/commit/ec8744a8882c1ebc38d5dc7e87aaf2e31b08f653)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=43492190)</sub>

<!-- /greptile_comment -->
2026-07-11 02:51:23 -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 71a78a0c5a Move runtime packages under src namespace (#1029)
## Problem

Runtime modules were published as generic top-level packages like `api`,
`cli`, and `providers`. That shape is fragile for PyPI packaging and
weakens explicit ownership boundaries.

## Changes

| Before | After |
| --- | --- |
| Runtime code lived in root-level packages. | Runtime code lives under
`src/free_claude_code/`. |
| Console scripts targeted top-level modules. | Console scripts target
namespaced modules. |
| Tests and smoke helpers imported old package roots. | Tests and smoke
helpers import `free_claude_code.*`. |
| Packaging listed six root packages. | Packaging builds the single
namespaced package. |
| Contracts allowed old root package directories. | Contracts require
the src namespace and reject old root imports. |

<!-- greptile_comment -->

<details open><summary><h3>Greptile Summary</h3></summary>

This PR moves the runtime packages into the `src/free_claude_code`
namespace. The main changes are:

- Console scripts now point to `free_claude_code.*` entrypoints.
- Runtime imports, tests, and smoke helpers now use the namespaced
package.
- Packaging now builds the single `src/free_claude_code` package.
- Contract tests now reject old top-level runtime package roots and
imports.
</details>

<h3>Confidence Score: 5/5</h3>

This PR is safe to merge with minimal risk.

The changes are a broad but mostly mechanical namespace and
package-layout migration with updated packaging, tests, and contract
coverage.

No files require special 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**
- Reviewed the primary contract validation by examining the namespace
validation log, which documents the exact commands executed, the working
directory, exit codes, pytest output, wheel build output, install
output, and import/entrypoint resolution.
- Verified the wheel listing by inspecting the wheel listing artifact,
confirming the available wheel filenames for the namespace validation.
- Ran and inspected the isolated import/entrypoint validation harness
saved as package-installed-import-check.py to validate import resolution
and entrypoint exposure.
- Captured and noted the wheel filename record in
package-wheel-filename.txt to enable traceability of the observed
artifact.

<a
href="https://app.greptile.com/trex/runs/13810533/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 |
|----------|----------|
| pyproject.toml | Updates packaging to build the single
`src/free_claude_code` package and retargets console scripts to
namespaced modules. |
| src/free_claude_code/config/env_template.py | Loads `.env.example`
from packaged resources with a source-checkout fallback after the
runtime package move. |
| src/free_claude_code/cli/entrypoints.py | Updates CLI entrypoint
imports to `free_claude_code.*` and continues to use the shared env
template loader. |
| src/free_claude_code/api/routes.py | Retargets API route dependencies
and handlers to the namespaced package without changing route behavior.
|
| src/free_claude_code/api/app.py | Updates app factory imports to the
namespaced package while preserving middleware, routers, and exception
handling. |
| src/free_claude_code/providers/runtime/factory.py | Updates lazy
provider factory imports to `free_claude_code.providers.*` under the new
package layout. |
| tests/contracts/test_import_boundaries.py | Adds contract coverage
requiring runtime packages to live under `src/free_claude_code` and
rejecting old top-level imports. |
| smoke/lib/child_process.py | Updates smoke child-process helpers to
import CLI entrypoints from the namespaced package. |
| README.md | Updates the project layout and extension guidance to refer
to `src/free_claude_code` and importable `free_claude_code.*` modules. |
| uv.lock | Reflects the package version bump associated with the
runtime packaging move. |

</details>

<details open><summary><h3>Sequence Diagram</h3></summary>

<a href="#gh-light-mode-only">

```mermaid
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant User as User / CLI
participant Script as Console script
participant Pkg as free_claude_code package
participant API as free_claude_code.api
participant Runtime as free_claude_code.providers.runtime
participant Provider as Provider adapter

User->>Script: run fcc-server / free-claude-code
Script->>Pkg: load free_claude_code.cli.entrypoints:serve
Pkg->>API: create FastAPI app and routes
API->>Runtime: resolve configured provider
Runtime->>Provider: instantiate namespaced adapter
Provider-->>Runtime: stream/model responses
Runtime-->>API: provider result
API-->>User: Anthropic/OpenAI-compatible response
```

</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 User as User / CLI
participant Script as Console script
participant Pkg as free_claude_code package
participant API as free_claude_code.api
participant Runtime as free_claude_code.providers.runtime
participant Provider as Provider adapter

User->>Script: run fcc-server / free-claude-code
Script->>Pkg: load free_claude_code.cli.entrypoints:serve
Pkg->>API: create FastAPI app and routes
API->>Runtime: resolve configured provider
Runtime->>Provider: instantiate namespaced adapter
Provider-->>Runtime: stream/model responses
Runtime-->>API: provider result
API-->>User: Anthropic/OpenAI-compatible response
```

</a>
</details>

<sub>Reviews (2): Last reviewed commit: ["Fix documented package import
paths"](https://github.com/alishahryar1/free-claude-code/commit/bfa9f2704c45f3684da39657d5e13f3814e5d450)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=42950471)</sub>

<!-- /greptile_comment -->
2026-07-09 01:19:05 -07:00