Files
LorenEteval_Furious/Furious/Controllers/AGENTS.md
T
2026-10-11 18:55:26 +08:00

7.7 KiB

Controller guidance

Inherit the nearest parent guide. Controllers own shared transitions; services own execution and repositories own stored data. Published state, saved preferences and physical outcomes are distinct. Read Furious/Controllers/ConnectionController.py with tests/test_controllers.py; paths are relative to this source tree's root.

Shared state authorities

  • Controllers own process-lifetime shared state and transition policy. They coordinate injected repositories/services and publish structured Qt signals. New behavior delegates presentation and execution resources to their owners; existing host-setting prompts do not justify moving network replies, core processes, or pools into controllers. A default QObject service created by a controller is its native child; an injected service keeps its supplied owner. ConnectionController update-manager teardown and borrowed-owner cases in tests/test_service_runtime.py verify that a retained invalid controller wrapper cannot keep its default network requests alive.
  • ConnectionController is the sole connection state machine. A GUI start remains Connecting while one generation-checked ConnectionManager transaction acquires readiness/TUN resources. The selected live profile is exposed during Connecting; successful runtime commit precedes System Proxy setup and Connected. Failure resets the active profile. Disconnect/reconnect cancels the exact in-flight generation and ignores stale completion.
  • Preserve state and signal ordering, interaction gating, the exact selected ServerProfile, runtime snapshots, reconnect preference, and rollback after validation, runtime, TUN, System Proxy exceptions, cancellation, or unexpected-exit failure. Worker/native callbacks cross to the controller's Qt thread before transition. Signal listeners and queued actions can synchronously disconnect or replace a start. Required invariant: revalidate current state/operation after invoking them before acquiring a runtime, completing the connection, or applying further host effects. This includes the initial profile/state/progress notifications, before a start operation exists to cancel. Verify that a listener which disconnects during Connecting prevents subsequent launch admission, not merely that the final state label is Disconnected. A pre-emission check cannot establish freshness afterward.
  • The active live profile is not the prepared document used by an already-started runtime. Resolve identity and generation before changing state or host effects, and preserve typed runtime failures; cancellation and supersession are not generic connection errors. Runtime commit, host-effect success, and visible connection state are distinct observations. Progress completion only ends the presentation interval; consumers and tests use connection state, structured errors/notifications, and host results to establish the outcome.
  • RoutingController owns available capability options plus selected/persisted routing. Distinguish a newly selected repository profile from the active-profile reference and the independent runtime document; changes use controlled reconnect, not mutation of the running document. Capability refresh prefers the active profile and otherwise the repository's activated profile. Normalizing the displayed option does not itself persist a new preference; explicit selection or invalidation owns that mutation. Disabling the selected custom route persists its supported fallback without reconnecting by itself; re-enabling restores availability, not selection. If reconnect is declined, selected/persisted routing may differ from the running document. Menu checkmarks prove selection only; exercise fallback persistence separately from reconnect acceptance and runtime preparation. RoutingControllerTest in tests/test_controllers.py anchors explicit invalidation versus observational refresh. User-defined routing labels are not translatable UI literals. Selecting a preference, requesting reconnect, and committing a runtime are separate events; verify the active runtime document when a test claims that routing or TUN changed, rather than inferring it from a selected control or persisted setting.
  • SettingsController is the shared policy path used by Home, Settings, tray, and platform integration. Startup registration persists only after host success; other preferences may apply immediately or on the next connection. Preserve each setting's actual application timing instead of imposing one transaction order on all preferences. Presentation can queue a checkbox request to let Qt's native setter unwind. The controller remains authoritative when that request is delivered; queued UI timing is not a second preference state or a delayed persistence policy. TUN mode, application-engine preference, and the active runtime's captured choice are separate values. A preference change affects a subsequent attempt; it neither replaces a live engine nor overrides proxy-core native TUN. Registration/defaults and selection signals belong here, while customization storage and host work keep their owners. Application-engine reconnect notices consult committed application-TUN ownership. Native-TUN and proxy-only connections still save and publish the engine preference without requesting reconnection; see the preference-notice cases in tests/test_sing_tun.py. Persist stable choice identifiers, not translated labels or combo positions. Relabeling/reordering controls must preserve an existing preference and the running connection's ownership. Selector retranslation and committed-TUN ownership tests challenge those separate claims; a translated label alone proves neither.
  • A completed disconnect restores usable UI state even if runtime cleanup failed. Disconnected and an empty active-runtime snapshot therefore do not prove physical release: the service retains failed leases and blocks new acquisition while they remain. Final controller shutdown surfaces unresolved cleanup and preserves the next-start preference. Check these outcomes independently in tests/test_controllers.py and tests/test_runtime_lifecycle.py. Pending asynchronous cleanup stays Disconnecting, with runtime ownership in the service. Reconnect waits for successful release; a genuine cleanup failure returns to usable UI without admitting replacement resources. Shutdown publishes its result only after this stop operation finishes and preserves the startup preference. An unsuccessful result must remain retryable by the application; the controller does not own the final process deadline. Test a second shutdown against retained leases, not just a second call after successful cleanup. tests/test_sing_tun.py covers delayed authorization, cancellation, reconnect and shutdown with real Qt delivery.
  • System Proxy configuration is best effort. Its helper logs host failures; an explicit False return does not roll back an otherwise usable committed runtime or prevent Connected. Preserve exception-path recovery separately. Exercise actual helper results with mocked OS boundaries; Connected does not guarantee the OS proxy was applied.

Verification and evolution

  • Test exact states and signal counts for async success, invalid input, supersession, cancellation, partial acquisition, System Proxy failure, unexpected exit, routing refresh/reconnect, startup restoration, failed host settings, missing partial-startup dependencies, and repeated shutdown. Ownership changes must retire the former state authority rather than leave two controllers. Start with tests/test_controllers.py, tests/test_connection_startup_async.py, and the shared-state cases in tests/test_qt_interactions.py.