From 74cbd6f4f92c9165ebdf0018c0fb93a96dd95bfd Mon Sep 17 00:00:00 2001 From: Loren Eteval Date: Thu, 27 Aug 2026 15:34:37 +0800 Subject: [PATCH] docs: evolve agent guidance hierarchy Signed-off-by: Loren Eteval --- AGENTS.md | 86 ++++++++++++++++--------- Furious/AGENTS.md | 47 +++++++------- Furious/Actions/AGENTS.md | 29 +++++---- Furious/Application/AGENTS.md | 40 ++++++++---- Furious/Backends/AGENTS.md | 53 ++++++++------- Furious/Backends/ExternalCore/AGENTS.md | 21 ++++-- Furious/Backends/Hysteria1/AGENTS.md | 18 ++++-- Furious/Backends/Hysteria2/AGENTS.md | 25 ++++--- Furious/Backends/Xray/AGENTS.md | 26 +++++--- Furious/Controllers/AGENTS.md | 28 ++++---- Furious/Core/AGENTS.md | 29 +++++---- Furious/Data/AGENTS.md | 14 ++++ Furious/Extensions/AGENTS.md | 12 ++++ Furious/Externals/AGENTS.md | 23 ++++--- Furious/Frozenlib/AGENTS.md | 32 +++++---- Furious/Interface/AGENTS.md | 27 ++++---- Furious/Models/AGENTS.md | 27 +++++--- Furious/Plugins/AGENTS.md | 40 ++++++++---- Furious/Qt/AGENTS.md | 59 +++++++++-------- Furious/Repository/AGENTS.md | 26 ++++---- Furious/Service/AGENTS.md | 55 ++++++++-------- Furious/Utility/AGENTS.md | 20 +++--- Furious/Widget/AGENTS.md | 26 ++++---- Furious/Window/AGENTS.md | 33 +++++----- Icons/AGENTS.md | 11 ++-- tests/AGENTS.md | 55 ++++++++-------- 26 files changed, 513 insertions(+), 349 deletions(-) create mode 100644 Furious/Data/AGENTS.md create mode 100644 Furious/Extensions/AGENTS.md diff --git a/AGENTS.md b/AGENTS.md index 877ad06..b60d4e8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,5 +1,21 @@ # Furious repository guidance +## Product and runtime map + +- Furious is a cross-platform PySide6 desktop proxy client. Persisted server profiles and subscriptions are interpreted + by protocol/plugin capabilities, then `ConnectionController` and `ConnectionManager` prepare an attempt-scoped + configuration and own the selected proxy runtime, optional native or application TUN, system proxy, DNS, and routes. +- `Main.py`, `Furious-GUI.py`, and the installed `Furious` command enter `Furious.__main__`. `AppMainProcess` runs the Qt + application in an exact child process so the parent can translate crashes and show a fallback report. + `DesktopApplication` then performs singleton election, composes process-lifetime owners, restores the requested + connection, runs the event loop, and unwinds acquired stages in reverse order. +- The main dependency direction is: models describe data; repositories persist domain collections; `AppSettings` + registers QSettings-backed preferences/blobs; services own workflows and temporary resources; controllers own shared + state machines; plugins/backends own protocol and runtime variation; `Application` composes the process; windows, + widgets, and actions adapt those APIs for presentation. +- Official backends are Xray, Hysteria 1, Hysteria 2, and a structured external-core process. New backend variation + belongs behind plugin capabilities, not core-name conditionals in shared orchestration. + ## Work from the current tree - Treat the checked-out tree, including unstaged work, as authoritative. Preserve unrelated changes and do not revive @@ -8,43 +24,39 @@ - Before Python work, inspect `.venv*`/`venv*` at the repository root and prefer its interpreter when usable. Do not create or alter an environment without need. - Keep edits focused. Preserve GPL headers, `from __future__` placement, import grouping, and established naming. Search - consumers before changing curated package exports, plugin contracts, persisted keys, serialized values, IDs, aliases, + consumers before changing curated exports, plugin contracts, persisted keys, serialized values, IDs, aliases, migrations, or semantic exit codes. -## Design and boundaries +## Engineering boundaries -- Make each state authority, resource owner, mutation, and failure path explicit. Prefer one readable canonical path - over parallel implementations or clever indirection. -- Put policy in its owning layer: models describe data; repositories persist domain collections; `AppSettings` persists - preferences; services own workflows and temporary resources; controllers own shared state machines; plugins/backends - own protocol variation; `Application` composes the process; UI adapts those APIs. +- Make each state authority, resource owner, mutation, commit point, and failure path explicit. Prefer one readable + canonical path over a compatibility shim plus a second implementation. - Existing global accessors and live repository collections are compatibility mechanisms, not invitations to add hidden - ownership. Prefer a narrow injected dependency or named operation for new code when practical. -- Treat persisted configuration as input. Build runtime, routing, testing, logging, TUN, and statistics state from - explicit copies unless an API is documented as mutating storage. -- Prefer plugin capabilities/factories to protocol conditionals in shared orchestration. Registries may own - process-lifetime plugins and metadata, never transient UI or active runtimes. -- Keep platform mutation behind `Frozenlib` and runtime boundaries. Own exact processes, threads, replies, timers, and - handles; cleanup is bounded where responsiveness requires it, idempotent, and never based on process-name searches. + ownership. Prefer a narrow injected dependency or named operation for new code when practical, and migrate callers + incrementally without creating a competing cache. +- Treat persisted configuration as input. Build runtime, routing, probing, logging, TUN, and statistics state from + explicit copies unless an API is documented as mutating storage. A failed preparatory stage must not change the + persisted profile; a post-commit side-effect failure must be reported without pretending the commit rolled back. +- Keep platform mutation behind `Frozenlib` and runtime boundaries. Own exact processes, threads, replies, timers, files, + and handles; cleanup is bounded where responsiveness requires it, idempotent, and never based on process-name searches. +- Internal invariant failures remain visible. Validate user/plugin/network input and return controlled failures with + useful context at boundaries. Cleanup may continue after one failure, but log the failed owner or stage. +- Treat secrets, subscription payloads, paths, URLs, plugin data, and complete core documents as untrusted. Do not log + credentials or secret-bearing configurations. -## Errors and external input +## Generated, curated, and packaged artifacts -- Internal invariant failures remain visible. Validate user/plugin/network input and return a controlled failure with - useful diagnostics. At OS/network/plugin boundaries, translate expected failures without discarding their cause. -- Cleanup may continue after one failure, but log the failed resource/stage. Narrow best-effort suppression is - acceptable only when the caller cannot act and the primary outcome remains observable. -- Treat secrets, subscription payloads, paths, URLs, and plugin data as untrusted. Do not log credentials or complete - secret-bearing configurations. - -## Generated and packaged artifacts - -- `Furious/Frozenlib/AppResources.py` and `Furious/Externals/GenTranslation.py` are generated. Modify their source inputs - and run the existing generator; do not hand-edit generated output. -- `_()` normally receives a static literal. Its only dynamic exception is an f-string composed solely of bare names - from `Furious.Frozenlib.Constants`; ordinary placeholders, attributes, calls, format specs, and `.format()` are not - extractable. Curly braces are reserved for constant substitution. -- Keep both source execution and `Deploy.py`/Nuitka builds viable. Runtime data belongs under `Furious/Data`; plugin and - optional imports must remain discoverable without constructing application or UI objects at import time. +- `Furious/Frozenlib/AppResources.py` is generated from `Resources.qrc` and its icon inputs; never hand-edit it. Regenerate + it with the compatible PySide6 resource compiler after changing the manifest or resources. +- `Furious/Externals/GenTranslation.py` is generator-managed but also retains curated language values and review flags. + Follow `Furious/Externals/AGENTS.md`; do not treat it as an opaque disposable output. +- `_()` normally receives a static literal. Its only extractable dynamic form is an f-string composed solely of bare + names from `Furious.Frozenlib.Constants`; ordinary placeholders, attributes, calls, conversions, format specs, and + `.format()` are not extractable. Curly braces are reserved for constant substitution. +- Keep source execution, wheel/sdist installation, and `Deploy.py`/Nuitka builds viable. Runtime data belongs under + `Furious/Data`; lazy/plugin/optional imports must remain discoverable without constructing application or UI objects + at import time. The release workflow builds multiple OS/architecture artifacts, so host-local success is not proof of + packaged correctness. ## Verification @@ -53,6 +65,16 @@ - Format only touched Python files with the repository Black configuration and check those files afterward. - Match verification to the boundary: round trips/migrations for models and persistence; transitions/signal counts for controllers; stale/cancel/cleanup paths for async services; partial startup and bounded cleanup for runtimes; repeated - destruction for Qt lifetimes; fully mocked host operations for platform helpers. + destruction for Qt lifetimes; fully mocked host operations for platform helpers; source plus packaged checks for + import/discovery/compiler-sensitive changes. - Review for duplicated state authorities, persisted-data mutation during preparation, swallowed diagnostics, unowned resources, unbounded external-input caches, and shared-manager branches that belong in a capability. + +## Keep this guidance useful + +- `AGENTS.md` records durable intent, invariants, boundaries, pitfalls, and validation—not a frozen inventory of classes. + When implementation and guidance diverge, investigate the current code and tests, then update the narrowest applicable + guide in the same change when the architectural truth has moved. +- Let child guides specialize inherited rules instead of repeating them. Remove obsolete constraints, distinguish a + compatibility path from the preferred direction, and avoid turning incidental implementation details into permanent + policy. A new rule should help a future agent make a concrete engineering decision. diff --git a/Furious/AGENTS.md b/Furious/AGENTS.md index 4d52b0a..2b09f7c 100644 --- a/Furious/AGENTS.md +++ b/Furious/AGENTS.md @@ -1,30 +1,29 @@ # Furious package guidance -## Architecture +## Package architecture -- `Application` is the broad composition root. Elsewhere, depend on the narrowest owning model, repository, service, - controller, or plugin capability. Do not create a second authority merely to avoid an existing boundary. -- Process-lifetime accessors in `Frozenlib.Globals` must tolerate partial startup and shutdown. They may expose deliberate - application authorities, never transient dialogs, replies, workers, editors, or runtimes. -- Compatibility code still reaches global accessors and live collections. Do not extend that coupling when a narrow - API can be added without a competing cache or state path. +- `Application` is the composition root. Elsewhere depend on the narrowest owning model, repository, service, + controller, or plugin capability; do not create a second authority merely to avoid an existing boundary. +- Keep lower layers importable without application construction. `Interface` and `Models` cannot depend on UI, + controllers, services, repositories, or concrete backends. Backend imports remain lazy enough that importing + `Furious` does not initialize Qt resources, plugins, runtimes, or windows. +- Process-lifetime accessors in `Frozenlib.Globals` expose compatibility paths to deliberate application owners only. + They may be unavailable during tests, partial startup, or shutdown; new code should prefer explicit dependencies and + callers using globals must tolerate that boundary rather than installing fallback owners. +- The current UI tree deliberately shares a few owners: `MainWindow` owns persistent pages, the server table owns the + subscription workflow used by both server and subscription views, and page-owned services live with their page. + Refactors may move those owners, but must leave exactly one durable owner and one state path. - Keep GUI-thread work short. Workers publish result data through the established Qt boundary and never mutate widgets - directly. + or live repositories directly from a worker thread. -## Qt ownership +## Qt ownership and presentation -- Classify Qt objects as application-lifetime, reusable, or transient. Give each one a durable Python owner, compatible - QObject parent, and explicit reuse or destruction path. -- Use the canonical `AppQ*` controls. One-shot dialogs use `AppQTransientDialog` or `AppQMessageBox`; managed `open()` is - safe for local variables, while `exec()` is reserved for genuinely synchronous flow. Reusable windows retain one - explicit owner. -- Parent or explicitly dispose timers, models, delegates, replies, event filters, menus, and actions. Never cache a - transient QObject or an instance method in a global/unbounded cache. -- For lifetime-sensitive changes, follow `Furious/Qt/AGENTS.md` and the `manage-qt-pyside6-lifetimes` skill, then run the - relevant native and packaged lifecycle checks. - -## Presentation - -- Reuse translation/theme-aware construction instead of parallel manual retranslation or one-off styling. Preserve - focus, keyboard, shortcut, accessibility, resize, high-DPI, and light/dark behavior. -- UI presents failures at the interaction boundary; owning services/controllers provide structured, testable results. +- Classify Qt objects as process/application-lifetime, reusable, or transient. Give each a durable Python owner, + compatible QObject parent, and explicit reuse or destruction path; audit every signal, timer, filter, cache, action, + model, delegate, reply, and callback that can extend that path. +- Use the canonical `Furious.Qt` `AppQ*` controls. One-shot dialogs use `AppQTransientDialog` or `AppQMessageBox`; + reusable windows retain one explicit owner. For lifetime-sensitive changes, follow `Furious/Qt/AGENTS.md` and the + `manage-qt-pyside6-lifetimes` skill. +- Reuse translation/theme-aware construction and layout behavior rather than parallel registries or call-site styling. + Preserve focus, keyboard, shortcut, accessibility, resize, high-DPI, and light/dark behavior. +- UI presents failures at the interaction boundary; owning services/controllers expose structured, testable outcomes. diff --git a/Furious/Actions/AGENTS.md b/Furious/Actions/AGENTS.md index 1859f57..cfb3817 100644 --- a/Furious/Actions/AGENTS.md +++ b/Furious/Actions/AGENTS.md @@ -1,17 +1,22 @@ # Action guidance -## Scope and contracts +## Role and boundaries -- Actions are thin presentation commands. Resolve current state when triggered, call its owning controller/service, and - present the result; do not own connection, routing, import, or persistence state. -- Share one `QAction` command between menus/buttons so callback, enabled/check state, shortcut, and translation cannot - diverge. `AppQAction.callback` is a strong reference, so its QObject owner must not outlive a captured receiver. -- Import through `profileFromAny` and plugin capabilities. Treat clipboard, file, URI, QR, and subscription content as - untrusted and avoid logging secret-bearing payloads. -- Managed asynchronous dialogs connect completion before `open()`. Long or batched work must yield or use one owned - worker/progress dialog; never sleep the GUI thread. +- Actions are presentation commands: resolve current state when triggered, invoke its owning controller/service, and + present the outcome. They do not become authorities for connection, routing, subscription, or persistence state. +- Some existing import actions still perform parsing, repository insertion, screen capture, and a cooperative batch + dialog directly. Treat that as a compatibility path, not a template: new reusable or fallible workflows belong in an + injected service/controller and may be migrated there without preserving action-local orchestration. If touching its + timer-driven dialog, also remove transient bound-method `QTimer.singleShot()` callbacks per `Furious/Qt/AGENTS.md`. +- Share one `QAction` command between menus/buttons when they represent the same operation so callback, enabled/check + state, shortcut, and translation cannot diverge. `AppQAction.callback` is a strong reference; the action owner must not + outlive a captured receiver, and dynamic menus must release obsolete actions and callbacks. +- Import through `profileFromAny` and plugin capabilities. Clipboard, file, URI, QR, and subscription content is + untrusted and may contain secrets; bound diagnostics and never log the complete payload. -## Verification +## Asynchronous UI and verification -- Verify command state and results plus repeated triggering: dialogs, dynamic menus, callbacks, and workers must return - to baseline. +- Connect dialog completion before managed `open()`. Long or batched work must yield between bounded units or use one + owned worker/progress dialog; do not sleep or perform unbounded capture/file/network work on the GUI thread. +- Verify command state, result/error presentation, cancellation, and repeated triggering. Dialogs, dynamic menus, + callbacks, native capture handles, and workers must return to their intended baseline. diff --git a/Furious/Application/AGENTS.md b/Furious/Application/AGENTS.md index 72005b6..66bceec 100644 --- a/Furious/Application/AGENTS.md +++ b/Furious/Application/AGENTS.md @@ -1,20 +1,32 @@ # Application composition guidance -## Ownership and lifecycle +## Composition and lifecycle -- `DesktopApplication` is the composition root. It owns process-lifetime controllers, plugin-registry lifecycle, - storage, singleton IPC, logging, platform integration, the main window/tray, and the cleanup stack. `MainWindow` owns - the persistent page tree through Qt parentage. -- Keep startup order explicit: settings/storage and plugins before consumers; controllers/services before UI; - restoration after dependencies exist. Register exact cleanup immediately after each successful acquisition. -- Partial startup and normal exit use the same reverse-order, failure-isolating, idempotent cleanup path. Keep graceful - resource cleanup separate from final Qt event-loop exit. -- Single-instance election is an atomic host operation: serialize stale-endpoint recovery and do not delete an endpoint - that another launch may have claimed. -- The tray owns its long-lived actions/menus. Dynamic rebuilds release obsolete objects, and absence of a system tray is - a supported desktop condition rather than an OS-support verdict. +- `Furious.__main__` and `AppMainProcess` own the outer process/crash boundary; `DesktopApplication` is the inner Qt + composition root. It owns singleton IPC, the plugin-registry lifecycle, repository owners, controllers, logging, + theme/system integration, the main window/tray, the thread pool, and the cleanup stack. +- Keep startup dependencies explicit: win singleton election; configure plugins/environment; restore repository owners; + construct controllers; configure logging/theme/system integration; build UI; then restore the requested connection. + Register cleanup immediately after each acquisition that succeeded. +- Partial startup, normal exit, and event-loop failure converge on the same reverse-order, failure-isolating, idempotent + cleanup stack. `exit()` requests termination; `aboutToQuit`/`finally` perform cleanup. Keep graceful cleanup separate + from the final Qt exit-code decision. +- `ConnectionController.shutdown()` preserves the reconnect-on-next-start preference while releasing the live runtime. + Repository cleanup persists live collections only after successful restoration or explicit replacement. + +## Desktop integration and UI ownership + +- Single-instance election is an atomic host operation. Serialize candidates, re-probe after waiting, recover only a + confirmed stale endpoint, and fail closed when ownership remains uncertain—especially during `RunAs` handoff. +- Native Windows session callbacks are not Qt-thread callbacks; queue shutdown into the GUI thread. System proxy daemon, + dock visibility, tray availability, and Flatpak/AppImage behavior are platform capabilities, not assumptions inferred + from the OS name alone. +- `MainWindow` owns the persistent page tree through Qt parentage. The application owns top-level window/tray wrappers; + the tray owns its long-lived actions and menus. Dynamic rebuilds dispose obsolete objects, and an unavailable tray + falls back to showing the main window and quitting on the last window. ## Verification -- Test partial initialization, singleton commands/races, tray rebuilding, restoration, and repeated cleanup with host - integration mocked. +- Test success and failure at every acquisition boundary, reverse cleanup, repeated cleanup/exit, singleton races and + commands, queued session shutdown, tray-present/absent behavior, startup restoration, and worker-pool bounds with all + host integration mocked. Process-wrapper behavior belongs to `tests/test_application_process.py`. diff --git a/Furious/Backends/AGENTS.md b/Furious/Backends/AGENTS.md index 92c01d5..057037b 100644 --- a/Furious/Backends/AGENTS.md +++ b/Furious/Backends/AGENTS.md @@ -1,32 +1,41 @@ # Backend guidance -## Scope and extension +## Responsibility and extension -- A backend owns its protocol parsing/export, structured editors, runtime factory, validation, statistics, routing, and - native-TUN behavior. Expose variation through plugin capabilities instead of shared-manager core-name branches. -- The full document submitted to the core is the runtime authority. Derive it from a copy and preserve the persisted - profile plus unknown supported fields; fail visibly when a lossless representation is impossible. -- Runtime modules remain importable without constructing Qt editors. Registrations/imports must be literal enough for - plugin discovery and Nuitka inclusion; factories create fresh widgets/runtimes and registries never retain them. +- A backend plugin owns the protocol/document types, parsing/export, structured editor factories, runtime factory, + validation, and any supported routing, native-TUN, statistics, settings, actions, or assets. Shared orchestration asks + capabilities; it does not branch on `coreName()`. +- `Backends.Configuration` and URI codec modules contain compatibility-era shared document implementations. They may be + refactored, but protocol behavior must remain owned and dispatched by capabilities, with model/API layers free of + concrete backend imports. +- The full core document is the runtime authority. Prepare routing, logging, probes, local endpoints, and TUN on an + explicit copy and preserve the persisted profile plus unknown supported fields. Fail visibly when a lossless mapping + or valid runtime document cannot be produced. +- Keep runtime/configuration modules importable without constructing Qt editors. Official plugin type imports remain + lazy, registrations literal enough for Nuitka discovery, and every editor/runtime factory returns a fresh object that + the registry does not retain. -## Structured editors +## Structured editor contract -- Loading is observational except for a documented compatibility normalization. Saving writes only represented fields - that changed, preserves unknown siblings, and does not materialize absent effective defaults. -- Unknown future string values remain visible and survive an untouched round trip. A deliberate switch to a known - tagged variant may replace only the incompatible variant data that control owns. +- Loading is observational except for a narrowly documented compatibility migration. Saving changes only represented + fields the user changed, preserves unknown siblings/top-level data, and does not materialize absent effective defaults. +- Unknown future enum/tag/string values remain visible and survive an untouched round trip. An explicit switch to a + known tagged variant may replace only the incompatible variant data owned by that control. +- Validation separates malformed external/persisted input from internal invariant failure and retains backend/core + context without logging credentials or full configuration documents. -## Native TUN and runtime ownership +## TUN and runtime ownership -- Normal connection preparation operates on a runtime copy. With the backend native-TUN option enabled, generated TUN - replaces runtime native TUN and suppresses application tun2socks. With it disabled, an existing user native TUN is - preserved and also suppresses tun2socks. Without either, global TUN mode may use tun2socks. -- Proxy-only tests explicitly strip native TUN from their own copy. Never run two TUN implementations or silently turn - malformed explicit TUN into another networking mode. -- A `CoreRuntime` owns exact resources, reports an actionable `startError()`, and has bounded, idempotent startup failure - and shutdown cleanup. Process-backed implementations additionally reap their exact child. +- Global TUN mode first asks the selected factory to prepare native TUN on the runtime copy. When managed native TUN is + enabled it replaces runtime native-TUN configuration; when disabled, any explicit user native TUN is preserved. Either + native case suppresses application tun2socks. Only a configuration with no native TUN may opt into the fallback. +- Treat presence of malformed explicit native TUN as authoritative so the core reports it; never silently change the + networking mode or run two TUN implementations. Proxy/download probes explicitly strip TUN from their own copy. +- A `CoreRuntime` owns exact resources, publishes an actionable `startError()`, and has bounded idempotent cleanup after + success or partial failure. Process-backed implementations additionally terminate/kill/reap only their exact child. ## Verification -- Test mapping/URI round trips, malformed and unknown input, original-document immutability, runtime document equality, - TUN matrices/proxy-only stripping, failed startup, and cleanup. Editor changes also require lifetime tests. +- Test document/mapping/URI round trips, legacy/malformed/unknown input, untouched editor observation, persisted + immutability, exact runtime document, the complete TUN matrix and probe stripping, start failure/rollback/cleanup, + import/discovery, and repeated editor/dialog destruction. diff --git a/Furious/Backends/ExternalCore/AGENTS.md b/Furious/Backends/ExternalCore/AGENTS.md index e70d45c..53d517e 100644 --- a/Furious/Backends/ExternalCore/AGENTS.md +++ b/Furious/Backends/ExternalCore/AGENTS.md @@ -1,9 +1,16 @@ # External Core guidance -- This backend is protocol-agnostic. Keep executable, working directory, argument vector, environment, proxy endpoints, - shutdown timeout, and application-tun2socks choice as distinct fields; preserve unknown top-level fields. -- Execute with `shell=False`, own the exact `Popen` plus readers/watchers, and terminate/kill/reap it within the configured - shutdown contract. Never concatenate a shell command or log environment secrets. -- Application tun2socks is explicit and requires a valid remote address; this backend never invents a native core TUN. -- Verify mapping/unknown-field round trips, mocked spawn/start/stop failures, output-reader cleanup, TUN validation, and - repeated editor/validation-dialog destruction. +- This backend models a user-selected local executable, not a protocol-specific embedded binding. Keep executable path, + optional working directory, argument vector, environment overrides, HTTP/SOCKS endpoints, shutdown timeout, + TUN remote address, and application-tun2socks opt-in as distinct fields while preserving unknown top-level fields. +- Path normalization is an explicit editor/user operation; loading a document does not silently rewrite relative paths. + Validate absolute executable/working-directory paths, argument/environment types and NULs, endpoint requirements, and + the bounded shutdown timeout before spawn. +- Execute an argument vector with `shell=False`. The runtime owns one exact `Popen`, stdout/stderr readers, watcher, and + line buffer; terminate, platform-escalate when required, kill, join readers, and reap within the configured shutdown + contract. Never concatenate a shell command, search by process name, or log the inherited/overridden environment. +- Application tun2socks is an explicit profile capability requiring a valid SOCKS endpoint and remote address for bypass + routing. This backend never invents a native core TUN. Subscription decoders must not import executable profiles. +- Verify unknown-field/mapping round trips, path/argument/environment validation, spaces in paths, mocked spawn and + immediate-exit failure, complete plus partial-line output, bounded termination escalation, unexpected exit callbacks, + exact reader/watcher cleanup, TUN validation/resolution, subscription rejection, and repeated editor/dialog destruction. diff --git a/Furious/Backends/Hysteria1/AGENTS.md b/Furious/Backends/Hysteria1/AGENTS.md index 382f00d..161daf4 100644 --- a/Furious/Backends/Hysteria1/AGENTS.md +++ b/Furious/Backends/Hysteria1/AGENTS.md @@ -1,8 +1,12 @@ -# Hysteria1 guidance +# Hysteria 1 guidance -- Hysteria1 is a distinct legacy flat schema with `hysteria://` links. Do not import Hysteria2 nested fields, - obfuscation rules, or native-TUN assumptions into it. -- Loading preserves tolerated legacy types and unknown combo values; an untouched editor round trip does not normalize - valid user data. -- This backend owns its MMDB, geosite, and rule assets. Proxy-only tests alter a copy and never the stored profile. -- Verify legacy URI/mapping compatibility, unknown values, asset/startup failure cleanup, and editor destruction. +- Hysteria 1 is a legacy backend with its own flat client schema and `hysteria://` share links. Do not import Hysteria 2 + nested-document, obfuscation, statistics, or native-TUN semantics merely because the core names are related. +- Preserve tolerated legacy types, upstream field names, and unknown combo values. Loading is observational and an + untouched editor/URI/mapping round trip does not normalize otherwise accepted user data. +- Subscription import is permitted for supported Hysteria 1 links; metadata still belongs in `ServerProfile`, not the + flat core document. Validation failure must not expose credentials in logs. +- This backend uses application tun2socks when global TUN mode requires it and owns the MMDB/ACL assets consumed by its + runtime. Proxy/download preparation alters only an independent copy. +- Verify legacy URI and mapping compatibility, unknown/tolerated values, persisted-copy isolation, missing/malformed + assets, startup and rollback cleanup, core exit translation, and repeated editor destruction. diff --git a/Furious/Backends/Hysteria2/AGENTS.md b/Furious/Backends/Hysteria2/AGENTS.md index 88637e3..1f89b4f 100644 --- a/Furious/Backends/Hysteria2/AGENTS.md +++ b/Furious/Backends/Hysteria2/AGENTS.md @@ -1,11 +1,16 @@ -# Hysteria2 guidance +# Hysteria 2 guidance -- The persisted native client document is submitted to the embedded core; the GUI editor is a partial projection, not - an Xray-shaped compiler or schema normalizer. -- Preserve upstream names/values. `realm.ipMode` uses `dual`, `v4`, or `v6`; absent effective defaults and unknown future - strings survive untouched. Optional controls update only their leaf and preserve unknown siblings. -- `obfs.type` selects a tagged subtype. Display an unknown subtype without rewriting it; an explicit switch to a known - type may replace only the incompatible subtype data. -- Runtime preparation and native-TUN behavior follow the parent backend contract; probe/download copies omit TUN. -- Verify nested sibling/default preservation, known/unknown values, subtype switching, runtime document equality, TUN - matrices, statistics cleanup, and transient editor destruction. +- The persisted native Hysteria 2 client document is submitted to the embedded core. The GUI is a partial projection, + not an Xray-shaped compiler or a general upstream-schema normalizer. +- Preserve upstream names and values. `realm.ipMode` currently uses `dual`, `v4`, or `v6`; absent effective defaults and + unknown future strings remain absent/visible and survive untouched. Optional controls update only their leaf and keep + unknown siblings. +- `obfs.type` selects tagged subtype data. Display an unknown subtype without rewriting it; an explicit switch to a + known subtype may replace only incompatible obfuscation data, not unrelated document branches. +- Managed native TUN replaces the runtime copy's `tun`; disabled management preserves any explicit `tun`, including a + malformed block so the core can reject it, and only absence permits application tun2socks. Native Linux TUN requires + the privilege and route-exclusion invariants enforced by the factory. Probe/download copies omit `tun`. +- Statistics target/settings/actions are plugin capabilities with process-lifetime descriptors/providers and + request-lifetime monitors/dialogs. Do not store live monitors, runtimes, or TUN dialogs in the registry. +- Verify nested sibling/default preservation, known/unknown strings, subtype switching, runtime document equality, every + TUN/privilege/resolution case, probe stripping, statistics cleanup, and transient editor/settings-dialog destruction. diff --git a/Furious/Backends/Xray/AGENTS.md b/Furious/Backends/Xray/AGENTS.md index faa112e..591800c 100644 --- a/Furious/Backends/Xray/AGENTS.md +++ b/Furious/Backends/Xray/AGENTS.md @@ -1,12 +1,18 @@ # Xray guidance -- The full Xray JSON document is authoritative. Editors project selected outbound/transport/TLS fields and preserve - unrelated inbounds, outbounds, routing, extensions, and unknown transport/security values. -- Loading may intentionally normalize legacy transport aliases `http`, `gun`, and `mkcp` to `h2`, `grpc`, and `kcp`; - keep this sole compatibility mutation explicit and tested. -- Routing, logging, testing, and native TUN are prepared on copies. Native-TUN replacement/preservation and proxy-only - stripping follow the parent backend contract, including multiple user TUN inbounds. -- This backend owns API statistics, routing assets, and its transient asset/routing UI; retain owners explicitly and - never register live windows/editors globally. -- Verify document/URI round trips, unknown and alias behavior, routing/TUN copies, asset failures, process cleanup, and - transient window destruction. +- The full Xray JSON object is authoritative. Editors are partial projections of the tagged proxy outbound, + transport/TLS, and local endpoint fields; preserve unrelated inbounds/outbounds, routing, logging, extensions, and + unknown transport/security data. +- Loading may intentionally migrate legacy transport aliases `http`, `gun`, and `mkcp` to `h2`, `grpc`, and `kcp`. + Keep compatibility mutations few, explicit, backend-owned, and covered by a test that distinguishes them from normal + observational loading. +- Protocol URI codecs must round-trip the supported Xray projection without erasing the source document. Preserve + Shadowsocks plugin metadata and SOCKS/VMess/VLESS/Trojan field semantics; malformed input returns controlled validation + rather than a plausible but different profile. +- Routing, logging, testing, local proxy endpoints, and native TUN are prepared on copies. Managed native TUN replaces + all runtime TUN inbounds; disabled management preserves existing valid or malformed TUN inbounds and suppresses + tun2socks. Proxy/download tests remove all TUN inbounds from their own copy. +- Xray owns API traffic statistics, routing/asset behavior, the `XRAY_LOCATION_ASSET` environment contract, and transient + asset/routing UI. Retain active windows/tasks only through their intended owner and never register live instances. +- Verify document/URI preservation, alias and unknown-value behavior, routing/log/TUN copy isolation, multiple TUN + inbounds, asset integrity/download failure, statistics/process cleanup, and transient editor/window destruction. diff --git a/Furious/Controllers/AGENTS.md b/Furious/Controllers/AGENTS.md index a36b060..daac3e5 100644 --- a/Furious/Controllers/AGENTS.md +++ b/Furious/Controllers/AGENTS.md @@ -1,17 +1,23 @@ # Controller guidance -## Scope and ownership +## State authorities -- Controllers are process-lifetime authorities for shared state and transitions. They orchestrate repositories/services - and publish results; they do not own transient widgets or duplicate service resources. -- `ConnectionController` owns connection state, interaction gating, active profile, failure/exit transitions, and signal - ordering. `RoutingController` distinguishes repository selection from routing applied to a live connection. -- `SettingsController` validates, persists, and applies preferences. A setting whose host side effect fails must not be - persisted as successful; UI pages do not reimplement that policy. -- Protocol-specific behavior goes through capabilities. Long work belongs in an owned service/worker, not an unbounded - GUI-thread controller call. Global dependencies may be absent during partial startup and shutdown. +- Controllers are process-lifetime authorities for shared application state and transitions. They orchestrate + repositories/services and publish structured signals; they do not own transient widgets or duplicate service + resources. +- `ConnectionController` is the sole connection state machine. Preserve atomic state/signal ordering, interaction gating, + the exact active `ServerProfile`, runtime snapshots, persisted reconnect preference, and cleanup after validation, + start, system-proxy, or unexpected-core-exit failure. Worker/core callbacks queue work back to its Qt thread. +- `RoutingController` owns displayed/persisted selection and capability-provided options. Distinguish the selected + repository profile from the profile already owned by a live connection; changing routing may require a controlled + reconnect rather than mutating the running document. +- `SettingsController` validates preferences and applies host/UI effects. When a host effect such as startup registration + fails, do not persist the requested state as successful. Keep UI pages declarative and do not duplicate this policy. +- Protocol/core-specific behavior goes through plugin capabilities. Long work belongs in an owned service/worker, and + global compatibility dependencies may be absent during partial startup, teardown, or isolated tests. ## Verification -- Test transitions and signal counts for success, validation/start failure, cancellation, unexpected exit, restoration, - failed settings effects, and repeatable shutdown. +- Test exact transitions and signal counts for success, invalid input, runtime/system-proxy failure, cancellation, + unexpected exit, routing refresh/reconnect, startup restoration, failed settings effects, partial dependencies, and + repeatable shutdown. diff --git a/Furious/Core/AGENTS.md b/Furious/Core/AGENTS.md index 00ee6ee..8ca0f0a 100644 --- a/Furious/Core/AGENTS.md +++ b/Furious/Core/AGENTS.md @@ -1,17 +1,24 @@ -# Process-backed runtime guidance +# Embedded process-runtime guidance ## Scope and ownership -- This package supplies low-level process-backed `CoreRuntime`, output transport, and tun2socks primitives. It does not - own controller, repository, page, or backend protocol policy. -- Own and reap exact child/handle objects. Startup validates launch/readiness; shutdown performs bounded - terminate/join/kill escalation, stops queues/timers, clears callbacks, and is idempotent. -- Preserve semantic exit codes and actionable startup diagnostics. Child targets never touch GUI objects; callbacks - cross through the established Qt timer/signal boundary. -- Bound producer queues/messages and drain independently of page visibility. Presentation may be lazy; process pipes - cannot be. Parentless timers require a durable Python owner and explicit `dispose()` path. +- This package supplies the shared `CoreRuntime` machinery for embedded-core multiprocessing workers, bounded log + transport, and application tun2socks. The separate External Core backend owns its direct `subprocess.Popen`; neither + package owns controller, repository, page, or protocol-preparation policy. +- `CoreLaunchSpec` is the launch transaction boundary. Validate target and serialization before spawn, distinguish + starting/running/stopping/failed/exited states, preserve shared semantic exit codes, and keep an actionable + `startError()` when launch cannot proceed. +- Own and reap the exact multiprocessing child/handle. Shutdown stops monitoring/draining, terminates and joins with a + bound, escalates to kill and joins again, closes the handle, clears callbacks, and remains safe when repeated or when + startup only partially succeeded. +- Child targets never touch GUI objects. Output crosses the bounded non-blocking `MsgQueue`; truncate oversized messages, + cap pending work and per-tick draining, and keep draining independently of page visibility. Do not delay a core launch + merely to wait for an output reader. +- Parentless queue/monitor timers are intentional only because their runtime owns a durable Python reference and an + explicit `dispose()` path. Preserve that ownership or replace it with equally clear Qt parentage; never leave a timer, + queue feeder, callback, or process handle after the runtime leaves the manager pool. ## Verification -- Test invalid launch, failed spawn, early exit, burst output bounds, normal stop, forced escalation, repeated disposal, - and absence of residual children, timers, queues, or handles. +- Test invalid serialization/target, failed spawn, early exit, burst output bounds/truncation/backoff, normal stop, + forced escalation, repeated disposal, and absence of residual children, timers, queues, callbacks, or handles. diff --git a/Furious/Data/AGENTS.md b/Furious/Data/AGENTS.md new file mode 100644 index 0000000..874c79e --- /dev/null +++ b/Furious/Data/AGENTS.md @@ -0,0 +1,14 @@ +# Bundled runtime data guidance + +- This directory contains shipped runtime assets, not application state: Xray GeoIP/geosite databases, Hysteria + MMDB/ACL rules, the vendored MapLibre endpoint map, and the bundled font. Keep user settings and downloaded temporary + files outside this tree. +- Preserve upstream licenses, provenance, binary/text format, filenames, and paths referenced by constants, backends, + tests, `package_data`, and Nuitka packaging. Do not reformat large generated ACLs or replace binary assets incidentally. +- `Deploy.py --download` refreshes Xray data and generated Hysteria assets from network sources. Such updates may be large + and nondeterministic over time: make them only when explicitly in scope, review checksums/provenance and the exact diff, + and never overwrite unrelated user changes already present in these files. +- MapLibre HTML/JS/CSS is a local privacy and offline boundary for endpoint presentation. Keep runtime requests neutral, + local paths/package inclusion intact, and the vendored license alongside it. Avoid adding remote scripts or trackers. +- Verify the consuming backend/widget, package-data inclusion, source and packaged path resolution, asset integrity/error + handling, licensing, and that tests use fixtures/mocks rather than live downloads. diff --git a/Furious/Extensions/AGENTS.md b/Furious/Extensions/AGENTS.md new file mode 100644 index 0000000..175047f --- /dev/null +++ b/Furious/Extensions/AGENTS.md @@ -0,0 +1,12 @@ +# Bundled extension guidance + +- `Extensions` contains host-shipped plugins that are not proxy backends. They implement the same public plugin API and + lifecycle as entry-point plugins; do not give them privileged side channels into repositories, controllers, or UI. +- `StandardSubscriptionPlugin` owns standard subscription decoding only. Decoders convert untrusted bytes into neutral + `SubscriptionResult` items; `SubscriptionImportService` owns profile construction/metadata and + `SubscriptionManager` owns reconciliation and persistence. +- Decoder probing is ordered and failure-isolated. Return `None` when a format does not match, validate sizes/types and + bound nesting/work for matched input, preserve useful names/upstream IDs, and never log complete payloads or links. +- Keep bundled extension imports deterministic, side-effect-light, and discoverable by source and Nuitka builds. Test + format selection/fallback, malformed and secret-bearing payloads, stable duplicate identity inputs, plugin rollback, + and absence of repository/UI mutation during decoding. diff --git a/Furious/Externals/AGENTS.md b/Furious/Externals/AGENTS.md index 95aeff6..4f9cd87 100644 --- a/Furious/Externals/AGENTS.md +++ b/Furious/Externals/AGENTS.md @@ -1,11 +1,14 @@ -# Generated translation catalog guidance +# Translation catalog guidance -- `GenTranslation.py` is generated by repository-root `Translation.py`; edit source strings or translation data and - regenerate. Never hand-edit the catalog. -- Entry key order is `source`, language keys in generator order, then `isReviewed`. `source` contains deduplicated fully - qualified modules and stale sources disappear only through regeneration. -- Extraction accepts static `_()`/`gettext()` literals and the root-documented constants-only f-string exception. Keep - runtime formatting outside the translatable expression. -- Preserve HTML/newline semantics and natural RU/ZH meaning. New or changed wording remains unreviewed until a human - verifies it. -- Run affected language generation, inspect ordering/stale entries, and verify a second run is stable. +- `GenTranslation.py` is a generator-managed catalog written by repository-root `Translation.py`, but it is also the + current source of curated language values and `isReviewed` state. Do not discard or blindly regenerate those values; + intentional translation/review edits may be made in the catalog before running the generator. +- `Translation.py --target ` re-extracts source membership, removes stale keys, preserves reviewed target text, + initializes missing/unreviewed target text, checks target collisions, and rewrites the catalog deterministically. + Run it with the repository environment and inspect the complete catalog diff. +- Entry key order is `source`, language keys retained by the generator, then `isReviewed`. `source` contains deduplicated + fully qualified modules; do not curate that list manually because extraction rebuilds it. +- Extraction accepts direct static `_()`/`gettext()` literals and the root-documented constants-only f-string form. + Keep runtime interpolation outside the translatable expression. +- Preserve HTML/newline semantics and natural RU/ZH meaning. Mark new or changed wording reviewed only after a human has + verified it. Verify collision output, stale-key removal, ordering, runtime lookup, and a stable second generator run. diff --git a/Furious/Frozenlib/AGENTS.md b/Furious/Frozenlib/AGENTS.md index a964956..c1ad6c5 100644 --- a/Furious/Frozenlib/AGENTS.md +++ b/Furious/Frozenlib/AGENTS.md @@ -2,25 +2,31 @@ ## Foundation and compatibility -- `Frozenlib` is the low-level platform/compatibility foundation and a broad import surface. Keep imports cheap, - cross-platform, and free of application/UI construction. Preserve exports unless all wildcard consumers migrate. +- `Frozenlib` is the low-level platform/compatibility foundation and a broad wildcard import surface. Keep imports cheap, + cross-platform, and free of application/UI construction. Preserve curated exports until all consumers migrate. - `Constants`, enums, and pure helpers remain dependency-light. `Globals` exposes only deliberate application-lifetime - owners and returns safely during partial startup/shutdown. -- `AppSettings` is the preference boundary. Preserve key/value/default/migration compatibility and write a requested - state only after any required host side effect succeeds. + owners; its accessors are compatibility shims and may yield `None` or raise attribute/runtime errors during isolated + tests, partial startup, and teardown. Callers at those boundaries must tolerate absence without inventing new globals. +- `AppSettings` registers QSettings keys at import time and validates values. Keys back both preferences and encoded + repository blobs, so preserve names, string/binary encodings, defaults, migrations, and registration order where it + affects imports. Perform a requested host side effect before persisting its successful preference state. ## Host and resource ownership -- Isolate proxy, DNS, routing, TUN, startup, session, and process mutation here so callers can mock it completely. - Return/log actionable failure and never test against real host state. -- `runExternalCommand()` deliberately has no universal timeout; each caller supplies one when responsiveness requires - it or documents a non-GUI execution context. +- Isolate proxy, DNS, routing, TUN, startup registration, session callbacks, external commands, and process mutation + here or behind the runtime boundary so tests can mock them completely. Preserve the OS-specific Windows/macOS/Linux + and Flatpak/AppImage semantics instead of forcing one platform path onto another. +- `runExternalCommand()` deliberately has no universal timeout; each caller supplies a bound when responsiveness or + cleanup requires it, or documents a non-GUI build/setup context. Avoid shell strings when an argument vector works. - Own exact threads/processes/handles, clear dead daemon references, and make cleanup bounded and repeatable. Externally keyed caches are bounded; finite metadata caches never capture QObject instances or bound methods. -- Context managers restore prior state when nested. Weak pools/callbacks must not have a parallel strong owner. -- `AppResources.py` is generated from resource inputs; update inputs and regenerate. +- Context managers restore the previous state when nested. Weak pools/callbacks must not have another strong owner and + must prune invalid PySide wrappers. +- `AppResources.py` is generated from `Resources.qrc` and its resource files. Update those inputs and regenerate with the + compatible PySide6 toolchain; do not edit the generated module. ## Verification -- Use direct mocked helper tests plus affected controller/service tests. Review GUI blocking, persisted success after - host failure, process-name cleanup, sensitive logs, stale daemon references, and unbounded external-input caches. +- Use direct mocked helper tests plus affected controller/service tests. Cover every supported platform branch where + practical and review GUI blocking, persisted success after host failure, process-name cleanup, sensitive logs, stale + daemon references, import-time construction, and unbounded external-input caches. diff --git a/Furious/Interface/AGENTS.md b/Furious/Interface/AGENTS.md index 9e96f27..e94da3e 100644 --- a/Furious/Interface/AGENTS.md +++ b/Furious/Interface/AGENTS.md @@ -1,13 +1,18 @@ # Interface guidance -- This package defines small contracts shared by lower layers. Keep it cheap to import and independent of UI, - controllers, services, repositories, and concrete backends. -- Contracts state observable lifecycle, ownership, mutation, and failure semantics; implementations may strengthen but - not weaken them. -- `CoreRuntime` is mechanism-neutral. Preserve semantic exit codes, `startError()`, callback order, serialization, and - bounded stop expectations; use process terminology only for actual OS processes. -- Storage contracts intentionally expose live mutable collections. Editor bindings translate widget/configuration - values but do not own runtime policy. -- Shared encoders support `CoreConfiguration` and compatible mappings; serialization failures retain useful context. -- Verify import independence and representative implementations for lifecycle/error, serialization, and live-storage - semantics. +- This package defines small contracts shared across architectural layers. Keep it cheap to import and independent of + UI, controllers, services, repositories, and concrete backends; dependency-light models/constants used by a contract + are acceptable when they do not pull in application construction. +- Contracts state observable lifecycle, ownership, mutation, serialization, and failure semantics. Implementations may + strengthen those guarantees but cannot silently weaken them; search representative implementations and tests before + changing a base contract or semantic exit code. +- `CoreRuntime` is mechanism-neutral: an implementation may use multiprocessing, `subprocess`, or an in-process binding. + Preserve `startError()`, callback/exit semantics, serialization diagnostics, and an idempotent bounded stop policy; + use child-process terminology only for implementations that actually own one. +- `StorageBackend.data()` intentionally exposes a live mutable collection for compatibility. Do not reinterpret it as a + snapshot or add a second cache. Editor bindings map widget values to configuration but do not own runtime policy. +- Shared encoders support `CoreConfiguration` and compatible mappings. Constructors may record diagnostics instead of + raising; callers at import/runtime boundaries must preserve useful context rather than treating every empty result as + equivalent. +- Verify import independence and representative implementations for lifecycle/error, serialization, live-storage, and + editor-binding semantics. diff --git a/Furious/Models/AGENTS.md b/Furious/Models/AGENTS.md index 49ef6b0..5bf16c6 100644 --- a/Furious/Models/AGENTS.md +++ b/Furious/Models/AGENTS.md @@ -1,13 +1,20 @@ # Model guidance - Models are core-neutral Python data and transformations. Do not import Qt, globals, controllers, repositories, - services, or concrete backends. -- Preserve user connection data, unknown metadata, and legacy migrations across load/save/copy without duplicate - meanings. `ServerProfile` keeps metadata and connection documents distinct. -- Stable profile IDs, subscription ownership/profile keys, display data, and connection fingerprints have separate - semantics; row position and display text are not identity. -- Fingerprints are deterministic for supported JSON-compatible documents. Export dispatch belongs to plugin protocol - capabilities; model methods may be compatibility shims but gain no new protocol branches. -- Use the shared JSON/ujson encoders and surface serialization context at the caller boundary. -- Verify legacy/current/unknown-field round trips, copy identity, subscription migration, deterministic fingerprints, - malformed serialization, and capability-based export. + services, plugins, or concrete backends into this layer. +- `CoreConfiguration` is a dict-like connection document whose construction is deliberately non-throwing; unsupported + or malformed input is represented by an empty object plus `constructionError()`. Keep serialization failure context + separate in `serializationError()`. +- `ServerProfile` composes an independent connection copy with `ProfileMetadata`. Keep metadata and connection documents + distinct through load/save/copy: display labels, subscription ownership, latency/speed, and annotations are not core + configuration fields. +- Preserve unknown metadata and legacy aliases across migrations. A manual independent copy receives a new `profileId` + and loses subscription ownership; a runtime `deepcopy()` preserves identity metadata because it is not a new domain + profile. +- `profileId`, subscription source, `subscriptionProfileKey`, connection fingerprint, row position, and display text are + separate identities. Fingerprints are deterministic only for supported JSON-compatible documents and failures remain + explicit. +- Protocol construction/export dispatch belongs to plugin capabilities. Model/backend compatibility shims may remain + while callers migrate, but do not add new protocol branches to the model layer. +- Verify malformed plus legacy/current/unknown-field round trips, copy and identity semantics, deterministic + fingerprints, metadata/connection separation, serialization diagnostics, and capability-based construction/export. diff --git a/Furious/Plugins/AGENTS.md b/Furious/Plugins/AGENTS.md index 0a7398f..c5f078b 100644 --- a/Furious/Plugins/AGENTS.md +++ b/Furious/Plugins/AGENTS.md @@ -1,20 +1,32 @@ # Plugin guidance -## Registry and extension contract +## API, registry, and discovery -- `Plugins.API` defines capability contracts; `Registry` owns validation, deterministic registration/selection, - initialization rollback, and reverse-order idempotent shutdown. -- Use current `CoreRuntimeFactory`, `CoreRuntimeRequest`, and `CoreRuntimeLaunch` vocabulary. Add protocol, runtime, - routing/TUN, statistics, subscription, or navigation variation through its capability family rather than central - conditionals. -- Registries strongly own process-lifetime plugins, providers, factories, descriptors, and immutable metadata. They do - not own factory-created UI, active runtimes, or controller state; rejected QObject results are destroyed explicitly. -- Registration validates before committing index changes. A failed plugin is isolated with useful plugin/capability - context and cannot corrupt existing providers. Partial initialization is rolled back. -- Discovery imports stay literal, deterministic, and side-effect-light for source and Nuitka builds. API/model layers - never import concrete plugins. +- `Plugins.API` defines capability contracts; `Registry` owns normalization, validation, deterministic indexing and + selection, initialization rollback, failure isolation, and reverse-order idempotent shutdown. +- The process-wide manager registers host plugin types before trusted entry-point discovery, publishes a registry only + after successful construction, and may reconcile additional host types idempotently. Keep discovery literal, + deterministic, and side-effect-light for source, wheel, and Nuitka builds. +- Use current `CoreRuntimeFactory`, `CoreRuntimeRequest`, and `CoreRuntimeLaunch` vocabulary. Add protocol, editor, + runtime, subscription decoder, routing/TUN, traffic statistics, settings, action, or navigation variation through its + capability family rather than central core-name branches. +- Registration validates the complete plugin/capability contribution before committing indexes. Duplicate IDs/schemes, + incompatible API versions, invalid descriptors, and initialization failures cannot corrupt existing providers; log + plugin and capability identity without leaking configuration secrets. + +## Ownership and trust boundaries + +- Registries strongly own process-lifetime plugin instances, capabilities/factories, descriptors, and immutable metadata. + They do not own factory-created widgets, active runtimes, replies, or controller state. Factories return a fresh object + per request; invalid QObject results are explicitly destroyed. +- External entry points and plugin-returned data are a boundary even when plugins are trusted for execution. Validate + types and required fields, isolate optional provider failure where the operation can continue, and keep the primary + failure observable when it cannot. +- API/model layers never import concrete plugins. Bundled backends/extensions implement the same contracts and must not + rely on registration-time application or UI objects. ## Verification -- Test discovery/order, duplicates and invalid capabilities, dispatch, rollback/shutdown, compiled inclusion, and - factories returning invalid objects. +- Test discovery/order, API-version and duplicate rejection, every changed dispatch path, staged rollback, reverse + shutdown, provider failure isolation, compiled inclusion, and factories returning invalid or repeatedly created + objects without registry retention. diff --git a/Furious/Qt/AGENTS.md b/Furious/Qt/AGENTS.md index 79790d6..d9aceb9 100644 --- a/Furious/Qt/AGENTS.md +++ b/Furious/Qt/AGENTS.md @@ -4,37 +4,40 @@ Use the `manage-qt-pyside6-lifetimes` skill for Qt ownership or lifecycle work. ## Canonical UI surface -- Extend/reuse `Furious.Qt` `AppQ*` controls for Fluent styling, themes, translation, dialogs, menus/actions, inputs, - tables/lists, and top-level windows. Do not create call-site styling or parallel lifetime registries. +- Extend/reuse `Furious.Qt` `AppQ*` controls for Fluent styling, semantic themes, translation, dialogs, menus/actions, + inputs, views, and top-level windows. `AppStyleSheet` is the public style authority; application code does not import + internal `StyleSheets` fragments or create parallel style/lifetime registries. - Pass source text at construction when a control retains it for retranslation. Preserve focus, keyboard, shortcuts, accessibility, translated-text growth, high-DPI, responsive layout, and both themes. -- `AppQMessageBox.windowTitle` is native metadata. Visible hierarchy is `heading`, `text`, then `informativeText`. - `AppQMessageBox.open()` delegates asynchronous ownership to `AppQDialog.open()`; do not add a parallel message-box - registry or release a transient box before native destruction completes. -- `AppStyleSheet` is the public style authority. `StyleSheets` contains internal QSS fragments that consume the semantic - palette; application code does not import fragments directly. +- `AppQMessageBox.windowTitle` is native metadata; its visible hierarchy is heading, text, then informative text. Keep + message-box presentation on the shared `AppQDialog` lifecycle instead of adding a second async owner. -## Lifetime and threading +## Ownership, destruction, and signals -- Application-lifetime Qt objects have one durable owner; reusable windows retain one deliberate owner; transient - dialogs use `AppQTransientDialog`/`AppQMessageBox` and remain retained through deferred native destruction. -- Direct compiled bound-method signal connections can retain transient receivers in Nuitka builds. Use - `connectWeakly(signal, receiver, 'methodName', sender=...)` when the sender is independent/longer-lived; never replace - it with a closure or partial capturing the receiver. -- `AppQAction.callback` is a strong reference. Its owner cannot outlive the callback receiver. Parent or explicitly - dispose timers, models, delegates, replies, event filters, menus, animations, and effects. -- Only the GUI thread mutates widgets. Slots do not sleep or perform unbounded host/network/process work. Coalescing - timers are created once and do not multiply across show/hide cycles. -- Each `QNetworkReply` has one manager/context owner, one terminal cleanup, and a freshness rule when superseded. - Schedule deletion on every terminal path; do not attach ad-hoc application attributes to third-party Qt objects. +- Classify every object as application-lifetime, reusable, or transient. Application owners construct long-lived objects + once; reusable windows retain one explicit strong owner; transient dialogs use `AppQTransientDialog`/`AppQMessageBox` + and are deleted after the interaction. +- `AppQDialog.open()` retains a reusable dialog through `finished`; delete-on-close transients remain retained through + deferred native destruction and release after `destroyed`. Registry cleanup captures only the opaque lifetime token, + never the dialog. `exec()` is for genuinely synchronous control flow. +- Native PySide and Nuitka can retain callbacks differently. Never pass a transient/repeated receiver's bound method to + `Signal.connect()` or `QTimer.singleShot()`, and do not hide that capture in a lambda/partial. Use + `connectWeakly(signal, receiver, 'methodName', sender=...)` when the sender is independent/longer-lived; use + `forwardSender=True` when the slot needs it. Direct bound methods are reserved for deliberately process-lifetime + receivers whose retention is intentional and documented. +- `AppQAction.callback` is deliberately strong; its owner cannot outlive the receiver it captures. Parent or explicitly + dispose timers, models, delegates, replies, event filters, menus, actions, shortcuts, watchers, animations, and effects. +- Only the GUI thread mutates widgets. Slots do not sleep or perform unbounded host/network/process work. Create + coalescing/render timers once and do not multiply them across show/hide cycles. +- Each `QNetworkReply` has one manager/context owner, one freshness rule, and one terminal deletion path. Shared slots use + `sender()`/stored context rather than per-reply closures; do not attach ad-hoc attributes to third-party Qt objects. -## Presentation and verification +## Geometry and verification -- Top-level subclasses use the canonical first-show geometry hooks and centering/retention behavior; do not call - overridable geometry hooks from constructors or alter private first-show state. -- Persistent top-level subclasses save geometry/state only after `hasPreparedInitialGeometry()` confirms first-show - preparation; never replace saved user geometry with a never-shown widget's native default. -- Use `exec()` only when synchronous control flow is required; otherwise connect completion before managed `open()`. -- Run focused behavior tests plus repeated native lifecycle cycles. For compiled-signal or transient-dialog changes, - also run the Nuitka probe and verify destroyed signals, weak references, registries, callbacks, timers, replies, and - native resources return to baseline. +- Top-level subclasses use the canonical first-show preparation and centering/retention hooks. Do not call overridable + geometry hooks from constructors or modify private first-show state. +- Persistent windows save geometry/state only after `hasPreparedInitialGeometry()` proves a native presentation occurred; + never overwrite saved user geometry with Qt's never-shown fallback. A valid `restoreGeometry()` is authoritative. +- Run focused behavior tests plus repeated native lifetime cycles. For transient signal/dialog infrastructure or a + packaged-only failure, run the Nuitka probe and verify destroyed counts, weak wrappers, dialog/context registries, + callbacks, timers, replies, and native resources return to baseline without per-cycle garbage collection. diff --git a/Furious/Repository/AGENTS.md b/Furious/Repository/AGENTS.md index e294d96..fcb28dc 100644 --- a/Furious/Repository/AGENTS.md +++ b/Furious/Repository/AGENTS.md @@ -1,13 +1,17 @@ # Repository guidance -- Repositories persist profiles, subscriptions, routings, and TUN settings. They do not own Qt presentation, network - workflows, or controller state; application preferences remain in `AppSettings` at their owning boundary. -- Preserve stable IDs, ordering, unknown fields, legacy schemas, and subscription ownership. Display names and row - indexes are not domain identity. -- Public access currently returns application-owned live mutable collections for compatibility. Do not create a second - cached copy; prefer named repository mutations for new behavior when practical. -- Decode failures remain observable and must not be overwritten with an empty fallback during automatic shutdown. - Prepare fallible transformations before committing deterministic assignments to live collections. -- Subscription reconciliation can change only profiles explicitly owned by that subscription and stable profile key. -- Verify old/current/unknown-field round trips, ordering/identity, subscription isolation, malformed persisted bytes, - failed pre-commit transforms, and temporary-settings isolation. +- Repositories restore and persist server profiles, subscription definitions, routings, and TUN settings. They currently + encode collections into registered `AppSettings`/QSettings values and synchronize at application cleanup; they do not + own network workflows, controller state, or presentation. +- `Storage` caches exactly one application-lifetime repository owner per collection and publicly exposes its live mutable + data for compatibility. Never create a competing cached copy. Prefer named repository mutations for new behavior so + validation and commit boundaries can move behind the repository over time. +- Preserve stable profile/subscription IDs, ordering, unknown fields, legacy schemas, and explicit subscription + ownership. Display names and active row indexes are derived/compatibility state, not domain identity. +- Restore failure is observable and an empty fallback must not overwrite unreadable persisted bytes during automatic + cleanup. An explicit successful mutation may intentionally replace that fallback. Prepare every fallible decode, + migration, or reconciliation before deterministic assignments to the live collection. +- Subscription reconciliation may change only profiles explicitly owned by that group and stable profile key. Preserve + local metadata and object/profile identity for matched profiles; mark removed profiles and reindex only at commit. +- Verify old/current/unknown-field round trips, ordering/identity, subscription isolation, malformed persisted roots, + failed pre-commit transforms, explicit replacement after restore failure, and temporary QSettings isolation. diff --git a/Furious/Service/AGENTS.md b/Furious/Service/AGENTS.md index e7c0955..8957869 100644 --- a/Furious/Service/AGENTS.md +++ b/Furious/Service/AGENTS.md @@ -1,34 +1,37 @@ # Service guidance -## Scope and ownership +## Workflow and ownership boundaries -- Services own workflows and temporary resources; controllers own shared application state. A service may use Qt for - async I/O/signals but does not own pages or presentation policy. -- Construct QObject services only after the application exists. Give each worker, reply, timer, executor, runtime, - process, and cache one explicit service/application owner with bounded, idempotent cleanup. -- Inject repositories, providers, clients, clocks, and runtime factories where practical. Services stage results and - commit through the owning repository/controller rather than creating a parallel state cache. +- Services own workflows and temporary resources; controllers own shared application state and UI owns presentation. + A service may use Qt for signals/networking, but it does not own pages or create message boxes. +- Construct QObject services only after a Qt application exists. Give every worker, reply, timer, executor, runtime, + process, cache, and callback context one durable service/application owner with bounded, idempotent cleanup. +- Inject repositories, providers, clocks, clients, and runtime factories where practical. Stage data and commit through + the owning repository/controller; do not create a parallel authoritative collection. -## Runtime and asynchronous work +## Runtime and asynchronous invariants -- `ConnectionManager` consumes runtime copies, coordinates capabilities and exact runtime ownership, and rolls back - only resources acquired by the failed staged attempt. It respects the backend TUN decision before any tun2socks - fallback. -- Every async workflow defines supersession: generation/version checks when stale completion is possible, or exact - object ownership when requests are independent. Terminal paths remove context, abort/finish once, and schedule Qt - replies for deletion once. -- Bound network/DNS/process/worker operations where their API permits it. Callbacks must not retain a shut-down manager; - GUI effects cross through signals. -- Collection continues independently of page visibility. Log/process transports and metric history remain bounded; - hidden pages may defer rendering but never draining. Raw metric samples remain immutable and display buckets derived. -- `SubscriptionManager` owns download, decode/filter/reconcile/persist, stale-request rejection, and stable-ID schedules. - Reconciliation commits current data before optional reconnect effects; one subscription failure does not cancel other - current results. Reapplying unchanged schedule policy does not restart timers or duplicate connections. -- Endpoint inspection enforces the active proxy, rejects stale connection results, uses neutral request metadata, and - discloses actual providers without profile secrets. +- `ConnectionManager` consumes an attempt-scoped copy, asks the selected factory for native-TUN/application-tun2socks + policy, and owns exact runtimes only after commit. On failure it rolls back only resources acquired by that attempt and + restores host routing/DNS state through those owners. +- Every async workflow defines supersession: generation/version checks for stale completion or exact-object identity for + independent requests. Terminal paths release context, finish/abort once, and schedule each Qt reply for deletion. + Callbacks cannot retain a shut-down manager; worker results cross into the manager's Qt thread before mutation. +- `HttpGetManager` supplies common reply ownership and transfer timeouts. DNS recursion is depth/time bounded; update and + connectivity requests own their active reply and cancellation. Never use page visibility as request or scheduler + ownership. +- Log/process transport and metric history stay bounded and continue collecting/draining independently of rendering. + Raw traffic samples are immutable; speed/usage baselines and graph buckets are derived state. Generation changes reject + stale statistics futures. +- `SubscriptionManager` owns download, decode/filter, group-scoped reconciliation, persistence metadata, stable-ID + schedules, cancellation, and reconnect/disconnect follow-up. Freshness is checked before commit; one group failure does + not cancel current peers; post-commit side-effect failure does not roll back reconciliation; unchanged scheduling + policy does not restart timers or duplicate connections. +- Endpoint inspection uses only the active proxy, neutral request metadata, bounded caches, and connection generations. + Reject stale results and disclose actual providers without exposing profile credentials or complete destinations. ## Verification -- Test success plus partial, stale, timeout, cancel, failure, hidden-page, and repeated-cleanup paths with fake - providers/host operations. Review retained callbacks, widgets owned by services, unbounded transports, and - persistence performed before final freshness checks. +- Test success plus invalid, partial, stale, timeout, cancel, failure, hidden-page, and repeated-cleanup paths with fake + providers/host operations. Review retained callbacks/replies, widgets owned by services, unbounded transports/caches, + repository mutation before final freshness checks, and commits incorrectly described as rolled back. diff --git a/Furious/Utility/AGENTS.md b/Furious/Utility/AGENTS.md index f4ac90c..e29de4d 100644 --- a/Furious/Utility/AGENTS.md +++ b/Furious/Utility/AGENTS.md @@ -1,9 +1,13 @@ -# Utility process guidance +# Outer process guidance -- This package owns the outer application-process wrapper and exit-code translation; it is not a miscellaneous helper - namespace and does not own application business/UI policy. -- `AppMainProcess` owns its exact child/runner, signal handlers, auxiliary manager resources, and best-effort crash-log - attempt. Signal and exception paths tolerate partial startup, preserve the original failure, avoid sensitive logs, and - perform bounded exact-resource cleanup. -- Verify normal/exception/signal exits, crash-log failure, pre/post application signals, cross-platform spawn, and no - orphaned child or manager resources. +- This package owns the parent-side application process wrapper and crash/exit translation; it is not a miscellaneous + helper namespace and does not own application business, repository, runtime, or UI policy. +- `AppMainProcess` owns one exact Qt child process plus a small synchronized crash-log result. Do not introduce a + `multiprocessing.Manager` or another auxiliary child merely to communicate status. +- Install signal and exception handling so it works before and after application construction. Preserve semantic + `ApplicationRunner.ExitCode` values, the original exception/traceback, and best-effort crash logging; a crash-log write + failure cannot replace the primary failure. +- The parent joins only its child, then creates the fallback Qt application/message box only for a nonzero result. Keep + source and platform spawn behavior viable and do not search for or terminate processes by name. +- Verify normal, exception, assertion, signal, and crash-log-failure paths; pre/post application signals; command-line + dispatch; cross-platform spawn; exact child joining; and absence of manager servers or orphaned resources. diff --git a/Furious/Widget/AGENTS.md b/Furious/Widget/AGENTS.md index d938472..d15e261 100644 --- a/Furious/Widget/AGENTS.md +++ b/Furious/Widget/AGENTS.md @@ -1,14 +1,18 @@ # Reusable widget guidance - Widgets are presentation components below pages/windows. Prefer explicit controller/service/repository dependencies; - do not add new `AppMainWindow()` reach-through or duplicate application state. -- Subscription views issue commands to `SubscriptionManager` and render repository-backed results; they do not own - download, decode, reconciliation, persistence, or scheduling workflows. -- Models, delegates, menus, actions, spinners, animations, timers, and network helpers have explicit owners. Persistent - widgets are constructed and connected once; refresh/show toggles state rather than accumulating objects. -- Resolve actions through the current model/proxy mapping after sort/filter. Use stable IDs across persistence/refresh - and keep model begin/end notifications synchronized with live repository ordering. -- Reuse `AppQ*` controls and keep expensive parsing, aggregation, mapping, and synchronization off blocking GUI paths. - Reject stale completions where work can be superseded. -- Verify behavior and repeated refresh/show/open/close lifetime, including sorted/filtered model actions and hidden-page - rendering. + do not add new `AppMainWindow()` reach-through or duplicate application state. Existing reach-through is compatibility + debt that may be migrated without preserving the coupling. +- Server/subscription views render the same live repositories and share the server table's `SubscriptionManager`. + Subscription views issue commands to that service; they do not duplicate download, decode, reconcile, persist, or + schedule workflows. +- Qt models must bracket live-collection mutations with matching begin/end notifications and keep stored row/index flags + synchronized. After sort/filter, map an action from the current proxy index to the source object and use stable IDs; + display text and row position are not identity. +- Models, delegates, headers, menus, actions, spinners, animations, timers, test schedulers, workers, network helpers, and + reusable editors each need an explicit owner. Persistent widgets construct/connect once; refresh/show changes state + instead of accumulating objects. +- Keep expensive parsing, aggregation, mapping, screen/network/core work off blocking GUI paths or split it into bounded + event-loop units. Reject superseded worker/reply results before mutating a live model. +- Verify behavior plus repeated refresh/show/open/close lifetime, sorted/filtered actions, model notification ranges, + cancellation/stale results, hidden-page rendering, and exact cleanup of worker/core/native resources. diff --git a/Furious/Window/AGENTS.md b/Furious/Window/AGENTS.md index 936bafc..0dc90e3 100644 --- a/Furious/Window/AGENTS.md +++ b/Furious/Window/AGENTS.md @@ -1,17 +1,20 @@ # Window and page guidance -- `MainWindow` owns built-in pages/navigation and their persistent Qt tree. Plugin pages are registered through - `PluginNavigationManager`; pages adapt owning controllers/services/repositories rather than becoming state authorities. -- Home is the initial page. Page selection and navigation expansion are session-local, with navigation initially - collapsed; do not persist either state. -- Long-lived pages create controls, timers, services, and connections once. Visibility controls lazy presentation, not - application-level collection or scheduler lifecycles. -- One-shot editors/prompts use managed transient dialogs. `TextEditorWindow` is reusable and needs a deliberate durable - owner. Close buttons call the normal `close()`/`closeEvent` path so confirmation and cleanup remain canonical. -- Use normal layouts and `AppQ*` controls. Blocking file/network/core work leaves the GUI thread, and worker results - return through owned signals without exposing secrets. -- Top-level windows use `AppQMainWindow` first-show preparation and `DEFAULT_WINDOW_SIZE`. Restore saved geometry only - after the persistent child layout exists; a successful `restoreGeometry()` is authoritative, otherwise use the - canonical default/centering path. -- Verify navigation defaults, page lazy behavior, async continuations, unsaved-close flow, geometry restore, and - repeated open/show/hide/destroy stability. +- `MainWindow` owns built-in pages, navigation, and the persistent Qt tree. Plugin pages enter through + `PluginNavigationManager`; pages adapt owning controllers/services/repositories rather than becoming competing state + authorities. Preserve public forwarding attributes/methods until their consumers migrate. +- The current composition has one shared subscription workflow owned by the server table and reused by + `SubscriptionPage`; Home owns update/connectivity/traffic services; Metrics owns endpoint inspection and derived + rendering. Those locations may evolve, but a refactor must retain one durable owner, one scheduler, and one signal path. +- Home is the initial page. Page selection and navigation expansion are session-local and initially collapsed; do not + persist them without an explicit product decision and migration. +- Long-lived pages create controls, models, timers, services, and signal connections once. Visibility may pause or defer + rendering, but never application-level collection, log draining, subscription scheduling, or request ownership. +- One-shot editors/prompts use managed transient dialogs. `TextEditorWindow` is intentionally reusable and needs one + durable owner; normal close/`closeEvent` remains the authority for unsaved confirmation, hiding, and final owner cleanup. +- Use normal layouts and `AppQ*` controls. File/network/core work leaves or cooperatively yields the GUI thread, and + results return through owned signals without exposing secret documents in diagnostics. +- Top-level windows use `AppQMainWindow` first-show preparation and declarative default sizes. Restore saved geometry only + after persistent children/layout exist; a valid saved geometry wins, otherwise use the canonical default/centering path. +- Verify navigation defaults/plugin placement, lazy rendering versus continuous collection, shared-service ownership, + async continuations, unsaved-close behavior, geometry migration/restore, and repeated open/show/hide/destroy stability. diff --git a/Icons/AGENTS.md b/Icons/AGENTS.md index bb8ffb1..f7663b7 100644 --- a/Icons/AGENTS.md +++ b/Icons/AGENTS.md @@ -2,7 +2,10 @@ - Reuse an existing semantic icon before adding another. Source SVGs remain compact vectors without scripts, remote resources, embedded rasters, editor metadata, or hard-coded backgrounds. -- Follow the established monochrome/tint convention; add theme variants only when tinting cannot express the design. -- Preserve resource paths and licensing. A rename/add/remove updates every consumer and regenerates - `Furious/Frozenlib/AppResources.py`; never hand-edit that generated module. -- Verify both themes, high-DPI rendering, and icon size/alignment in the actual Fluent control. +- Follow the established monochrome/tint convention; add a theme-specific variant only when semantic tinting cannot + express the design. Preserve accessible meaning rather than relying on color alone. +- Preserve upstream licensing and the `Resources.qrc` alias contract. Add/remove/rename updates the manifest and every + consumer, then regenerates `Furious/Frozenlib/AppResources.py` with the compatible PySide6 resource compiler; never + edit generated resource code. +- Verify manifest uniqueness, both themes, high-DPI rendering, and size/alignment in the actual `AppQ*` control and, when + relevant, tray/platform packaging. diff --git a/tests/AGENTS.md b/tests/AGENTS.md index 9f9707a..144dec6 100644 --- a/tests/AGENTS.md +++ b/tests/AGENTS.md @@ -3,37 +3,36 @@ ## Isolation - Tests never affect a running Furious instance or production data. Set `QT_QPA_PLATFORM=offscreen` before Qt import, - use `tests/support.py` for one small application and temporary INI-backed settings, and mock every external service. -- Never mutate real proxy, DNS, routing, TUN, startup registration, tray, interfaces, or host processes. Own exact test - threads/children/handles, give waits bounded timeouts, and clean up only resources created by the test. -- Normal tests need no network or installed proxy core. Keep packaged/manual smoke procedures explicit and disposable. + use `tests/support.py` for one deliberately small application and temporary INI-backed QSettings identity, and mock + every external/network/host service. +- Never mutate real proxy, DNS, routing, TUN, startup registration, tray, interfaces, desktop windows, or host processes. + Own exact test threads/children/replies/handles, give waits bounded timeouts, and clean only resources created by the test. +- Normal tests require neither network access nor installed proxy cores. Real Qt/process boundaries use hermetic child + scripts; packaged/manual smoke procedures are explicit and run only in disposable profiles/environments. -## Test contracts +## Contract design -- Test public behavior and architecture contracts rather than private layout trivia. Cover success, invalid input, - timeout/cancel, partial/stale completion, cleanup, and compatible persisted input where applicable. +- Test public behavior and architectural contracts, not private widget coordinates or incidental implementation. Cover + success, invalid input, timeout/cancel, partial/stale completion, cleanup, and compatible persisted input where relevant. - For staged mutations, fail immediately before commit and assert live plus persisted state is unchanged. Test post-commit side-effect failure separately without claiming rollback. -- Keep persisted configuration and runtime-copy assertions distinct. TUN preservation, generated TUN, fallback, and - proxy-only stripping must not share a helper that erases their differences. -- Deterministic logic/UI belongs in normal tiers. Put repeated object/handle/thread/process/RSS trends in stress tiers; - very heavy work requires `FURIOUS_VERY_HEAVY_TESTS=1`. -- Prefer exact ownership, destroyed-signal, registry, thread, handle, and child assertions over timing or arbitrary RSS - thresholds. `gc.collect()` is diagnostic at batch boundaries, never a production fix or per-cycle requirement. +- Keep persisted profile and runtime-copy assertions distinct. TUN preservation, generated native TUN, application + fallback, malformed explicit TUN, and proxy-only stripping are separate cases and must not share a helper that erases + their differences. +- Deterministic logic/UI belongs in normal tiers. Repeated object/handle/thread/process/RSS trends belong in stress tiers; + the release-confidence tier requires `FURIOUS_VERY_HEAVY_TESTS=1`. +- Prefer exact state, signal, destroyed, weak-reference, registry, thread, handle, and child assertions over arbitrary + timing/RSS thresholds. `gc.collect()` is diagnostic at batch boundaries, never a production fix or per-cycle crutch. -## Real lifecycle boundaries +## Real lifecycle boundaries and maintenance -- Bugs involving Qt event loops, native callbacks, interpreter finalization, subprocess teardown, or wrapper destruction - require a focused integration test using the smallest real boundary that failed. -- Run lifecycle regressions in a clean child process with offscreen Qt, temporary identity/settings, singleton IPC and - tray/restoration disabled, and all host/network integration mocked. Exercise the public shutdown path and repeat when - intermittent. -- Automated tests cannot flash, focus, inspect, signal, or modify the desktop or a potentially running Furious process. - Visible windows are limited to explicit manual smoke procedures. -- Update `tests/README.md` when modules, tiers, commands, or environment requirements change; examples use active - `python`. - -## Review - -- Flag production state/host mutation, external network dependence, process-name cleanup, unbounded waits, shared - mutable fixtures, and tests that assert storage when runtime output is the actual contract. +- Bugs involving native Qt event loops/callbacks, interpreter finalization, subprocess teardown, wrapper destruction, or + Nuitka callback retention require the smallest real integration boundary that reproduces them. Isolate it in a child + with temporary settings, singleton/tray/restoration disabled, and all host/network mutation mocked. +- Exercise public shutdown and repeat intermittent lifecycles. Automated tests cannot focus, flash, inspect, signal, or + modify the desktop or a potentially running Furious process. +- Update `tests/README.md` whenever modules, coverage ownership, tiers, commands, opt-ins, or environment requirements + change. Run the narrow module first, then its documented tier; the final unittest status/exit code is authoritative + even when negative-path diagnostics are expected. +- Review for production state/host mutation, external network dependence, process-name cleanup, unbounded waits, shared + mutable fixtures, hidden test-order dependence, and assertions against storage when runtime output is the true contract.