diff --git a/AGENTS.md b/AGENTS.md index 0eaf3c6..877ad06 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,78 +1,58 @@ # Furious repository guidance -## Working method +## Work from the current tree -- Treat the checked-out tree as authoritative. Preserve unrelated staged and unstaged work; do not revive deleted - experiments or infer architecture from old history. -- If `.codegraph/` exists, use `codegraph explore` before broad text search or file reading. Use `rg` for exact - follow-up searches. -- Before Python work, inspect the repository root for `.venv*` or `venv*` and prefer its interpreter when usable. Do not - create or modify an environment unless required. -- Keep edits focused. Preserve GPL headers, `from __future__` placement, import grouping, repository naming style, and - public compatibility unless a deliberate migration is part of the task. Treat curated package `__init__` exports, - plugin API dataclasses, persisted keys, and semantic exit codes as compatibility surfaces. +- Treat the checked-out tree, including unstaged work, as authoritative. Preserve unrelated changes and do not revive + deleted experiments from history. +- If `.codegraph/` exists, use it for structural questions before broad searches; use `rg` for exact follow-up. +- 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, + migrations, or semantic exit codes. -## Pythonic design +## Design and boundaries -- Prefer the simplest design that makes ownership, state transitions, failure, and side effects explicit. Readability - and one canonical path beat clever indirection or parallel implementations. -- Keep policy close to the layer that owns it: models describe data, repositories persist domain collections, - `AppSettings` persists preferences, services own workflows and temporary resources, controllers own shared state - machines/orchestration, plugins/backends own protocol-specific behavior, `Application` composes the process, and UI - adapts those APIs. -- Make invalid states and boundary failures visible with specific return values, result objects, or exceptions. Catch - broadly only at a genuine isolation boundary, log actionable context, and do not silently convert explicit user input - into a different behavior. -- Bound external work where the workflow requires responsiveness: network requests, subprocess startup/shutdown, - thread joins, and host commands need caller-chosen timeouts or a documented non-GUI execution context. Low-level - wrappers such as `runExternalCommand()` intentionally do not invent a universal timeout. Cleanup must be idempotent, - own exact resources, and never search by process name. -- Prefer immutable metadata, pure transformations, dependency injection, and explicit runtime copies. Avoid global - mutable state, hidden mutation, duplicated caches, and UI-owned business state. +- 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. +- 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. -## Repository invariants +## Errors and external input -- Treat persisted user configuration as input. Connection, routing, testing, logging, TUN, and statistics preparation - must not mutate it implicitly; use explicit runtime/derived state unless an API is documented as mutating storage. -- Prefer plugin capabilities/factories over protocol or core conditionals in shared managers. Registries may strongly - own process-lifetime plugins, capability providers, factories, descriptors, and metadata; they must not retain - transient UI or active runtime instances. -- A `ServerProfile` combines profile metadata with one persisted connection/configuration document. `CoreRuntime` means - one managed proxy-core lifecycle regardless of whether its implementation uses a subprocess, multiprocessing, or an - in-process binding. Reserve process terminology for actual operating-system processes and handles. -- Application-wide controllers and repositories may be process-lifetime. Transient UI, network replies, timers, - callbacks, and temporary processes must not become accidental global state. -- Keep platform mutation behind `Frozenlib`/runtime abstractions so unsupported platforms remain safe to import and - tests can fully mock host operations. -- Treat secrets, subscription payloads, paths, URLs, and plugin data as untrusted input. Do not log credentials or full - sensitive configurations; validate before host or process use. +- 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 files and translations +## Generated and packaged artifacts -- `Furious/Frozenlib/AppResources.py` and `Furious/Externals/GenTranslation.py` are generated. Change their source - inputs and run the existing generator instead of hand-maintaining them. -- Add user-facing text through translation-aware controls and `_()` extraction conventions, then run `Translation.py`. -- `_()` normally receives a static string literal. The sole dynamic exception is an f-string made only from bare names - in `Furious.Frozenlib.Constants`; the extractor resolves those constants. Runtime values, attributes, calls, format - specifications, and `.format(...)` inside `_()` are unsupported. -- Curly braces in extracted strings are reserved for application-constant substitution, not ordinary runtime - placeholders. -- Keep both source execution and the `Deploy.py`/Nuitka build viable. Plugin discovery and optional heavy imports must - remain statically discoverable or explicitly included without introducing import-time application/UI construction. +- `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. -## Verification and review +## Verification -- Run the narrowest relevant tests first, then the affected tier in `tests/README.md`. Tests must not touch production - settings, networking, routing, TUN, startup registration, or unrelated processes. -- Format only touched Python files with the repository Black configuration and run Black check mode on those files. -- For backend/process/platform work, verify error cleanup and bounded shutdown. For Qt ownership work, follow - `Furious/AGENTS.md` and `Furious/Qt/AGENTS.md`, use the `manage-qt-pyside6-lifetimes` skill, and run the relevant - lifetime tests. -- Windows 7 release bindings that require newer Go runtimes must use locally built wheels produced with the patched - `go-win7` toolchain; an ordinary modern PyPI wheel does not establish Windows 7 compatibility. A binding built with an - official Go release that still supports Windows 7 may use a verified prebuilt wheel. -- Architecture-specific Windows release jobs must verify the active Python, installed native extensions, and packaged - executable architecture before publishing uniquely named artifacts. -- Review for duplicated state authorities, mutation of persisted configuration during runtime preparation, broad - swallowed errors, unbounded waits/caches, generated-file edits without regeneration, and protocol branches that belong - in a capability. +- Run the narrowest relevant tests, then the affected tier documented in `tests/README.md`. Tests never touch production + settings, networking, routing, TUN, startup registration, unrelated processes, or external services. +- 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. +- 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. diff --git a/Furious/AGENTS.md b/Furious/AGENTS.md index c7a4f1e..4d52b0a 100644 --- a/Furious/AGENTS.md +++ b/Furious/AGENTS.md @@ -1,51 +1,30 @@ # Furious package guidance -## Layering and state +## Architecture -- `Application` is the deliberate broad composition layer. Elsewhere, depend on the narrowest lower-level contract and - avoid circular imports or reaching through a page/window when a controller, service, repository, or plugin capability - owns the operation. -- Existing UI code has compatibility paths that reach repositories or application globals. Do not extend that coupling - when a controller/service API can be introduced cleanly; migrate incrementally rather than creating a second state - authority. -- Long-lived objects may be exposed through `Frozenlib.Globals`, but accessors must tolerate partial startup and - shutdown. Never place transient dialogs, replies, workers, or editor instances in application globals. -- Keep GUI-thread work short. Worker callbacks publish immutable/result data to the GUI thread; they do not mutate - widgets directly. +- `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. +- Keep GUI-thread work short. Workers publish result data through the established Qt boundary and never mutate widgets + directly. -## Qt/PySide6 lifetime invariants +## Qt ownership -Classify every dynamic Qt object before choosing ownership: +- 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. -- **Application-lifetime:** main window, navigation pages, shared controllers/managers. Give them one durable - application owner and idempotent cleanup. -- **Reusable:** intentionally hidden and shown again. Retain one explicit Python owner and release it deliberately at - final shutdown. -- **Transient:** editors, prompts, progress windows, and one-shot dialogs. Use a suitable parent plus normal - close/deferred deletion; do not retain them after completion. +## Presentation -Use `AppQTransientDialog` for one-shot dialogs. `AppQDialog.open()` and `AppQMessageBox.open()` retain asynchronous -dialogs until completion, so local variables are safe; connect `finished` before `open()`. Use `exec()` only where -synchronous control flow is required. Do not add `WA_DeleteOnClose` universally or use `gc.collect()` as a production -fix. - -QObject parent ownership and Python references are separate concerns. Parent timers/models/delegates where appropriate, -stop/disconnect owned resources on teardown when auto-disconnection is insufficient, remove event filters when lifetimes -differ, and clear references after `destroyed`. Never cache a transient QObject instance or an instance method with -unbounded `lru_cache`. - -## UI and translation - -- Reuse `AppQ*` Fluent/theme/translation-aware primitives. Prefer layout composition over fixed geometry and preserve - keyboard, focus, shortcut, accessibility, and theme behavior. -- Pass source text at construction when an `AppQ*` control already retranslates it; avoid parallel manual - `retranslate()` logic. -- UI shows errors at the interaction boundary, while services/controllers provide structured, testable failures. Do not - broadly catch and hide deleted-wrapper or worker failures. - -## Verification - -- For changed transient/reusable UI, test open/close/show cycles, weak-reference clearing or deliberate retention, - signal/timer stability, and asynchronous completion. -- Use offscreen Qt and the isolated settings helpers documented in `tests/README.md`. Distinguish allocator high-water - behavior from linear live-object/resource growth. +- 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. diff --git a/Furious/Actions/AGENTS.md b/Furious/Actions/AGENTS.md index 921c99f..1859f57 100644 --- a/Furious/Actions/AGENTS.md +++ b/Furious/Actions/AGENTS.md @@ -1,21 +1,17 @@ -# Actions guidance +# Action guidance -- Actions are thin presentation adapters: resolve current controller/service state at trigger time, invoke the owning - API, and present the result. Do not make an action a second owner of connection, routing, repository, or import state. -- Reuse shared `QAction` command logic for menus/buttons. Keep shortcuts, check state, enabled state, translation, and - callback semantics synchronized from one source. -- `AppQAction.callback` is intentionally a strong reference. Scope actions to a suitable QObject owner; an - application-lifetime action must not retain a transient bound method or closure. -- Import through `profileFromAny`/plugin capabilities. Treat clipboard, file, URI, QR, and subscription text as - untrusted; report per-item failures without logging credentials or whole secret-bearing payloads. -- Long or batched work must yield safely or use an owned worker/progress dialog. Do not sleep the GUI thread. -- Asynchronous prompts/editors use managed `open()` lifetime and connect completion before opening. Rebuilt dynamic - menus must release obsolete `QMenu`/`QAction` objects rather than accumulating them. -- Preserve structured error semantics in message boxes: error title becomes the visible `heading`, the main message - becomes `text`, and diagnostics become `informativeText`; native `windowTitle` remains metadata. +## Scope and contracts -## Code review rules +- 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. -- Flag duplicated controller logic, captured transient widgets, unsanitized sensitive logging, and action-local - persistence changes. -- Verify repeated triggering does not multiply dialogs, callbacks, menus, or workers. +## Verification + +- Verify command state and results plus repeated triggering: dialogs, dynamic menus, callbacks, and workers must return + to baseline. diff --git a/Furious/Application/AGENTS.md b/Furious/Application/AGENTS.md index 2067d2f..72005b6 100644 --- a/Furious/Application/AGENTS.md +++ b/Furious/Application/AGENTS.md @@ -1,28 +1,20 @@ # Application composition guidance -- `DesktopApplication` is the composition root. It durably owns process-lifetime controllers, logging, the main window, - tray, thread pool, singleton IPC endpoint, cleanup stack, and plugin-registry lifecycle. `MainWindow` owns the built-in - page/widget tree and page-level managers through normal Qt parentage; do not duplicate those owners in the application. -- Keep bootstrap order explicit: environment/plugins and storage before consumers, controllers/services before UI, and - restoration only after all dependencies exist. Accessors must tolerate partial startup and cleanup. -- Each successful startup stage registers its exact cleanup immediately. Partial startup and final shutdown use the - same reverse-order, failure-isolating cleanup stack, and cleanup is idempotent without relying on destructor timing. -- Keep graceful shutdown distinct from the final base-Qt event-loop exit. Delayed forced-exit callbacks must never - recursively re-enter application cleanup. -- Serialize single-instance election with a short-lived cross-process lock. `QLocalServer.listen()` is the endpoint - claim on Unix, but Qt permits duplicate same-name local servers on Windows. Never remove a possibly stale endpoint - until the election lock is held and endpoint reachability and listening have both been rechecked. -- The tray owns its long-lived actions/menus and reflects controller state. Dynamic submenu rebuilds must not retain - stale actions or menus. A system tray is an optional desktop capability, not proof that the operating system is - supported; when it is unavailable, show the main window and let closing the last window quit the application. -- Application code may import higher layers to compose them; lower layers must not import `DesktopApplication` to obtain - dependencies when injection or a narrow global accessor suffices. -- Keep blocking startup checks bounded and event-loop safe. A startup failure must leave enough runtime available to - report the error and still clean up. +## Ownership 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. ## Verification -- Test partial initialization, repeated cleanup, singleton IPC commands, tray menu rebuilding, startup restoration, and - failure before the main window exists. -- Do not start the production singleton/host mutation in ordinary tests; compose fakes through the established - boundaries. +- Test partial initialization, singleton commands/races, tray rebuilding, restoration, and repeated cleanup with host + integration mocked. diff --git a/Furious/Backends/AGENTS.md b/Furious/Backends/AGENTS.md index c559d92..92c01d5 100644 --- a/Furious/Backends/AGENTS.md +++ b/Furious/Backends/AGENTS.md @@ -1,50 +1,32 @@ -# Backend and core-integration guidance +# Backend guidance -- A backend owns protocol parsing/export, editor factories, runtime materialization, process integration, statistics, - validation, and native-TUN capability for its core. Register these through plugin capabilities instead of adding - shared-manager conditionals. -- The JSON/document submitted to a core is the runtime authority. Build it from a deep/runtime copy; never mutate the - persisted profile while preparing connection, routing, logging, testing, or TUN state. -- Preserve full user-authored core documents and unknown supported fields. Report a lossless-compatibility failure - instead of silently compiling or deleting unsupported configuration. +## Scope and extension -## Structured editor contract +- 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. -- `factoryToInput()` is normally observational: loading an editor must not add defaults or otherwise mutate the - configuration document unless that backend has a documented compatibility normalization, such as Xray transport - aliases. -- `inputToFactory()` writes only fields represented by the editor and returns whether it actually changed the - document. Do not materialize an absent effective default merely because the editor displays it. -- Display unknown future string values exactly and preserve them on an untouched load/save round trip. Editing one - known leaf must preserve unknown fields and unknown siblings elsewhere in the same object. -- A deliberate user switch to a supported tagged variant may replace the incompatible active variant. Keep unrelated - extension fields, and do not treat a fallback page used for display as a user selection. +## Structured editors -## Native TUN policy +- 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. -For a normal connection: +## Native TUN and runtime ownership -- If the Furious native-TUN option is enabled, replace any runtime native TUN with the generated TUN; mark TUN handled - and do not start application tun2socks. -- If the option is disabled and the user document contains native TUN, preserve it unchanged; mark TUN handled and do - not start tun2socks. -- If neither exists, do not inject native TUN; global TUN mode may use tun2socks. -- Proxy-only operations such as speed/latency tests explicitly strip native TUN from their own temporary copy. - -Never remove an explicit user TUN merely because an application toggle is off, and never run native TUN plus application -tun2socks together. - -## Runtime and UI - -- Core runtimes expose actionable `startError()`, exact resource ownership, bounded startup/shutdown, and deterministic - cleanup. Process-backed runtimes additionally own and reap their exact child process. Keep platform exit codes - semantically intact. -- Keep runtime modules importable without constructing editor widgets. Use literal, discoverable lazy - imports/registrations so Nuitka includes every editor family without per-editor command changes. -- Backend editors follow `Furious/Qt/AGENTS.md`; factories create fresh transient editors and registries retain - factories/classes, not editor instances. +- 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. ## Verification -- Test URI/mapping round trips, malformed input, runtime document equality, original-document immutability, all - native-TUN matrix cases, proxy-only stripping, failed core startup, and cleanup. +- 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. diff --git a/Furious/Backends/ExternalCore/AGENTS.md b/Furious/Backends/ExternalCore/AGENTS.md index bcd388e..e70d45c 100644 --- a/Furious/Backends/ExternalCore/AGENTS.md +++ b/Furious/Backends/ExternalCore/AGENTS.md @@ -1,25 +1,9 @@ -# External Core backend guidance +# External Core guidance -## Configuration contract - -- External Core is protocol-agnostic. Keep executable path, working directory, argument vector, environment mapping, - proxy endpoints, shutdown timeout, and application-tun2socks choice as distinct fields. -- The editor projects these known fields but must preserve unknown top-level fields. Loading is observational and an - untouched save must not normalize paths, arguments, environment variables, or future fields. -- Execute with `shell=False`. Never concatenate arguments into a shell command or log credentials/environment secrets. - -## Runtime and TUN ownership - -- Own the exact `Popen` instance and its reader/watcher resources. Startup failure and shutdown must close callbacks, - terminate, kill if necessary, and reap the exact child within configured bounds. -- Application tun2socks is explicit and requires a valid remote address. This backend does not inject a native core TUN - block or infer protocol-specific behavior. -- Validation dialogs are transient asynchronous UI: use the established `open()` ownership path and release them on - destruction without retaining them in backend or plugin registries. - -## Code review and verification - -- Flag shell execution, ambiguous string arguments, inherited transient callbacks, unknown-field loss, and unbounded - child-process cleanup. -- Use fully mocked subprocess and host-network operations. Test mapping round trips, unknown-field preservation, - validation failures, output-reader shutdown, tun2socks validation, and repeated editor/dialog destruction. +- 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. diff --git a/Furious/Backends/Hysteria1/AGENTS.md b/Furious/Backends/Hysteria1/AGENTS.md index 64762dd..382f00d 100644 --- a/Furious/Backends/Hysteria1/AGENTS.md +++ b/Furious/Backends/Hysteria1/AGENTS.md @@ -1,22 +1,8 @@ -# Hysteria1 backend guidance +# Hysteria1 guidance -## Compatibility boundary - -- Hysteria1 is an independent legacy backend with its own flat client schema and `hysteria://` share-link behavior. - Do not import Hysteria2 nested configuration, obfuscation, or native-TUN assumptions into it. -- Its structured editor follows the shared observational-load/minimal-write contract. Unknown future string values in - combo-backed fields remain visible and survive an untouched round trip. -- Preserve existing tolerant handling of malformed legacy field types unless a deliberate compatibility migration is - part of the task; do not silently rewrite valid user values while loading. - -## Runtime and assets - -- Runtime setup owns Hysteria1-specific MMDB, geosite, and rule assets. Keep asset preparation and failure cleanup in - this backend rather than shared connection managers. -- Proxy-only tests operate on a copy, set their temporary proxy listener, and must not alter the stored profile. - -## Code review and verification - -- Flag accidental reuse of Hysteria2 keys, loss of unknown flat fields, or editor factories retained as live widgets. -- Test URI and mapping compatibility, unknown combo values, runtime document immutability, asset failures, process - cleanup, and transient editor destruction. +- 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. diff --git a/Furious/Backends/Hysteria2/AGENTS.md b/Furious/Backends/Hysteria2/AGENTS.md index 3646725..88637e3 100644 --- a/Furious/Backends/Hysteria2/AGENTS.md +++ b/Furious/Backends/Hysteria2/AGENTS.md @@ -1,29 +1,11 @@ -# Hysteria2 backend guidance +# Hysteria2 guidance -## Native document and structured editor - -- The persisted Hysteria2 client document is the configuration submitted to the embedded core. The compact editor is - a partial projection, not a compiler or schema normalizer. -- Keep upstream field names and values exact. In particular, `realm.ipMode` uses the native values `dual`, `v4`, and - `v6`; an absent value has the effective dual-stack default. Unknown future strings remain visible and untouched. -- Optional nested controls edit only their leaf. Preserve unknown siblings, and do not create optional groups or - effective Gecko packet defaults until the user actually changes the represented value. -- `obfs.type` selects a tagged subtype object. An unknown type must remain visible and preserve its subtype on an - untouched round trip. An explicit user switch to a known type may remove the previously active incompatible subtype, - while retaining unrelated extension data. - -## Runtime, statistics, and TUN - -- Runtime materialization passes a derived full Hysteria2 document to `startFromJSON`; do not translate it through an - Xray-shaped intermediate representation. -- With native TUN enabled, replace the runtime `tun` block with the generated block. With it disabled, preserve and - recognize a user `tun` block. Download/probe configurations explicitly omit TUN. -- Keep management/statistics requests bounded and separate from GUI objects. Runtime and worker ownership follows the - parent backend and Qt lifetime guidance. - -## Code review and verification - -- Flag `_modified` shadow state, dynamic combo-item accumulation, fallback pages mistaken for user selections, and - nested writes that replace an entire user object. -- Test known and unknown values, nested sibling preservation, obfs subtype switching, absent-default preservation, - native-TUN matrices, proxy-only stripping, and editor destruction. +- 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. diff --git a/Furious/Backends/Xray/AGENTS.md b/Furious/Backends/Xray/AGENTS.md index f625698..faa112e 100644 --- a/Furious/Backends/Xray/AGENTS.md +++ b/Furious/Backends/Xray/AGENTS.md @@ -1,27 +1,12 @@ -# Xray backend guidance +# Xray guidance -## Documents and editors - -- The full Xray JSON document is authoritative. Structured editors project only the selected `proxy` outbound, - transport, and TLS fields; preserve every unrelated inbound, outbound, routing field, and extension. -- Transport and security loading preserves unknown `network` or `security` strings so they remain visible and survive - an untouched round trip. -- `http`, `gun`, and `mkcp` are legacy transport aliases that the editor intentionally normalizes to `h2`, `grpc`, and - `kcp` while loading. Keep this compatibility rewrite explicit and covered by tests. -- Selecting a different known transport or security mode may replace incompatible represented settings. Preserve - unknown sibling settings that the selected editor does not own. - -## Runtime, routing, and TUN - -- Prepare routing, logging, tests, and native TUN only on runtime copies. Keep the selected profile document intact. -- With Xray native TUN enabled, replace runtime TUN inbounds with the generated inbound. With it disabled, preserve and - recognize user TUN inbounds. Proxy-only tests explicitly strip TUN. -- Xray API statistics, routing assets, and transient asset/routing windows belong to this backend. Keep window owners - explicit and never register live editor or dialog instances globally. - -## Code review and verification - -- Flag editor loading that mutates configuration outside the documented transport-alias normalization or materializes - unrelated defaults. -- Test URI/mapping round trips, unknown transport/security preservation, routing and TUN runtime-copy behavior, asset - failure cleanup, and transient editor/window destruction. +- 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. diff --git a/Furious/Controllers/AGENTS.md b/Furious/Controllers/AGENTS.md index 70248be..a36b060 100644 --- a/Furious/Controllers/AGENTS.md +++ b/Furious/Controllers/AGENTS.md @@ -1,22 +1,17 @@ # Controller guidance -- Controllers are process-lifetime state authorities. Expose observable state/transitions and orchestrate - repositories/services; do not retain transient widgets or duplicate service resource ownership. -- `ConnectionController` owns the connection state machine and interaction gating. Stable and transitional states, - failures, unexpected exits, reconnect preference, `runtimes` snapshots, and signal ordering must remain explicit. - Runtime managers are injected for tests. -- `RoutingController` owns selected/active routing semantics. Distinguish repository selection from the routing actually - applied to a live connection; derive runtime routing through backend capabilities. -- `SettingsController` validates, persists, and applies settings through service-level APIs where practical. Host - registration or administrator-dependent changes persist only after success; UI pages should not implement settings - policy. -- Controller signals carry state/results, not widget callbacks. Long work belongs in services/workers, and GUI-thread - entry points must not perform unbounded waits. -- Treat partial startup/shutdown as normal: global dependencies may be absent and cleanup methods must be repeatable. +## Scope and ownership -## Code review rules +- 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. -- Flag direct ownership of dialogs/pages, duplicated connection/routing state, settings saved after failed side effects, - and protocol branches that belong in a capability. -- Test success, validation failure, startup failure, unexpected exit, signal order, restoration, cancellation, and - idempotent shutdown. +## Verification + +- Test transitions and signal counts for success, validation/start failure, cancellation, unexpected exit, restoration, + failed settings effects, and repeatable shutdown. diff --git a/Furious/Core/AGENTS.md b/Furious/Core/AGENTS.md index b673e8a..00ee6ee 100644 --- a/Furious/Core/AGENTS.md +++ b/Furious/Core/AGENTS.md @@ -1,22 +1,17 @@ -# Process-backed core-runtime guidance +# Process-backed runtime guidance -- This package provides low-level process-backed `CoreRuntime`, queue, output-redirection, and tun2socks primitives. It - must not own controller, repository, page, or protocol policy. -- Own exact child `Process`/handle objects. Startup validates launch specs and readiness; shutdown uses bounded - terminate/join/kill escalation, reaps the child, stops timers/queues, clears callbacks, and is idempotent. -- Preserve platform exit codes and expose actionable startup errors. Do not convert serialization/start failures to an - unexplained success or “Unknown error” when context exists. -- Child-output transports are bounded and non-blocking for producers. Drain them in bounded batches regardless of page - visibility: use a short interval while messages flow and back off to a finite maximum interval while idle. Truncate - oversized messages and drop excess burst output at the documented queue boundary rather than retaining it - indefinitely. Presentation may remain lazy after collection. -- Process targets do not touch GUI objects. Queue/monitor callbacks cross to the owning Qt thread through the - established timer/signal boundary. -- A timer without a QObject parent requires an explicit durable Python owner and `dispose()` path. Never rely on wrapper - finalization for process or timer cleanup. -- Temporary output files/streams close on every success and error path; avoid unbounded queues and blocking reads. +## 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. ## Verification -- Test invalid specs, failed spawn, early exit, normal output, bounded stop escalation, repeated dispose, exact-child - cleanup, and no residual timers/threads/handles. +- 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. diff --git a/Furious/Extensions/AGENTS.md b/Furious/Extensions/AGENTS.md deleted file mode 100644 index 075fb66..0000000 --- a/Furious/Extensions/AGENTS.md +++ /dev/null @@ -1,15 +0,0 @@ -# Bundled extension guidance - -- Extensions are bundled plugins, not exceptions to the plugin contract. Declare stable metadata and contribute - capability objects through `Furious.Plugins` APIs. -- Store factories, handlers, descriptors, and immutable metadata; never retain transient UI or active runtime instances. -- Subscription decoders recognize and normalize one representation. Treat remote payloads as untrusted, return “not - matched” distinctly from “matched but invalid,” and leave profile materialization/import policy to the shared - pipeline. -- Imports must stay lightweight and deterministic so plugin discovery and Nuitka inclusion work without side effects. -- Initialization/shutdown must support registry rollback and repeated process cleanup. - -## Verification - -- Test discovery order, malformed/ambiguous payloads, capability registration, rollback, and shutdown without network or - production state. diff --git a/Furious/Externals/AGENTS.md b/Furious/Externals/AGENTS.md index 370e8fa..95aeff6 100644 --- a/Furious/Externals/AGENTS.md +++ b/Furious/Externals/AGENTS.md @@ -1,19 +1,11 @@ # Generated translation catalog guidance -- `GenTranslation.py` is generated by the repository-root `Translation.py`; do not hand-edit it. Change source strings - or the translation data and regenerate. -- Each catalog entry uses this key order: `source`, supported language keys in the generator's canonical order, then - `isReviewed`. -- `source` is a deduplicated list of fully qualified Python module names discovered by the generator. Preserve generated - ordering and remove stale sources through regeneration, not manual cleanup. -- `_()`/`gettext()` extraction accepts static literals. The only f-string exception consists exclusively of bare names - defined in `Furious.Frozenlib.Constants`; the extractor substitutes those values before catalog lookup. -- Do not put runtime values, attribute expressions, calls, format specifications, `.format(...)`, or ordinary brace - placeholders inside a translatable expression. -- New or changed translations remain unreviewed until a human verifies meaning. Preserve Unicode, HTML/newline - semantics, and natural RU/ZH wording rather than literal word order. - -## Verification - -- Run `Translation.py` for the affected languages, inspect the generated diff for source/key ordering and stale entries, - and ensure a second generation is stable. +- `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. diff --git a/Furious/Frozenlib/AGENTS.md b/Furious/Frozenlib/AGENTS.md index 96376f5..a964956 100644 --- a/Furious/Frozenlib/AGENTS.md +++ b/Furious/Frozenlib/AGENTS.md @@ -1,37 +1,26 @@ -# Frozenlib foundation guidance +# Frozenlib guidance -- `Frozenlib` is the compatibility/platform foundation and a broad public import surface. Keep imports cheap, - cross-platform, and free of GUI/application construction side effects. Preserve exported names unless a migration - covers all wildcard consumers. -- `Constants`, enums, version, and pure helpers stay dependency-light. `Globals` contains narrow accessors for - deliberate application-lifetime owners and must return safely during partial startup/shutdown. -- `AppSettings` is the centralized settings boundary. Preserve defaults, value types, migrations, and compatibility - aliases; write requested state only after required host side effects succeed. +## Foundation and compatibility -## Platform and process safety +- `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. +- `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. -- Host mutation (proxy, DNS, startup registration, session, routing) is platform-isolated, returns/logs actionable - failure, and can be fully mocked. `runExternalCommand()` intentionally delegates timeout policy to each caller; - callers that require bounded execution must pass a timeout or run in a documented non-GUI context. Never test against - real host state. -- Own exact daemon threads/processes. Clear dead references so restart is possible; cleanup is bounded and idempotent. - Do not suppress a failed shutdown or leave persisted state claiming success. -- `parseHostPort` and other externally keyed caches must be bounded. Unbounded caches are limited to finite application - metadata and must not capture QObject instances or bound methods. -- Rate limiting on GUI-callable paths must not sleep the caller. Network helpers support IPv4/IPv6 and use bounded - resolution/connect operations. +## Host and resource ownership -## Mixins and resources +- 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. +- 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. -- Weak instance pools must not be paired with hidden strong references or callbacks that capture the target. Remove dead - entries and tolerate already-deleted Qt wrappers. -- Context managers restore the exact prior enabled/signal-block state and remain safe when nested. -- `AppResources.py` is generated from resource inputs. Do not hand-edit generated bytes or bypass the - resource-generation workflow. +## Verification -## Code review rules - -- Flag GUI/high-level imports, caller paths that can block the GUI or shutdown without an intentional bound, state - persisted after host failure, broad process-name cleanup, sensitive logging, unbounded external-input caches, and - globals holding transient objects. Do not add a universal timeout to `runExternalCommand()`. -- Run direct mocked platform/helper tests plus affected controller/service tests. +- 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. diff --git a/Furious/Interface/AGENTS.md b/Furious/Interface/AGENTS.md index a32b34f..9e96f27 100644 --- a/Furious/Interface/AGENTS.md +++ b/Furious/Interface/AGENTS.md @@ -1,20 +1,13 @@ -# Interface contract guidance +# Interface guidance -- This package defines small, low-level contracts shared across layers. Keep imports cheap and avoid depending on UI, - controllers, services, repositories, or concrete backends. -- Abstract methods state observable behavior, ownership, mutation, and failure contracts; concrete implementations may - add detail but must not weaken them. -- `CoreRuntime` is the canonical mechanism-neutral lifecycle contract. Preserve its semantic exit codes, `startError()`, - callback order, serialization behavior, and bounded stop expectations. -- Do not introduce mechanism-specific aliases for `CoreRuntime` or mistake the semantic runtime contract for an - operating-system process. -- Serialization uses the shared `Furious.Models` encoders. Preserve supported dict subclasses such as - `CoreConfiguration`; return/raise behavior must be documented and failures must retain useful diagnostics. -- Storage contracts intentionally expose live mutable collections. State that explicitly—do not label them copies—and - keep persistence/ordering semantics in concrete repositories. -- Editor bindings translate between widgets and configuration but do not decide process/runtime policy. - -## Verification - -- Contract tests cover representative implementations, serialization failures, lifecycle/error behavior, mutable-storage - semantics, and import independence. +- 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. diff --git a/Furious/Models/AGENTS.md b/Furious/Models/AGENTS.md index c21e059..49ef6b0 100644 --- a/Furious/Models/AGENTS.md +++ b/Furious/Models/AGENTS.md @@ -1,20 +1,13 @@ # Model guidance -- Models are core-neutral Python data and transformations. Do not import Qt, application globals, controllers, - repositories, services, or concrete backend plugins. -- Preserve meaningful user configuration and unknown metadata fields across load/save/copy. Promote legacy fields - through explicit, backward-compatible migrations without ambiguous duplicates. -- `ServerProfile` separates connection data from metadata. Stable profile IDs, subscription ownership/key fields, - display metadata, and connection fingerprints each have distinct semantics; do not substitute row position or display - text for identity. -- Fingerprints must be deterministic for supported JSON-compatible connection documents. If compatibility requires a - fallback, make its stability/diagnostics explicit rather than silently changing identity across runs. -- Encoders are the canonical JSON/ujson boundary. Preserve supported mappings/dict subclasses and surface useful failure - context at the caller boundary. -- `toURI()` dispatch belongs to plugin protocol capabilities; model methods may remain compatibility shims but must not - grow new protocol-specific branches. - -## Verification - -- Test legacy/current mappings, unknown-field round trips, copies, deterministic identity, subscription migration, - malformed serialization, and plugin-based export. +- 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. diff --git a/Furious/Plugins/AGENTS.md b/Furious/Plugins/AGENTS.md index 7476cfd..0a7398f 100644 --- a/Furious/Plugins/AGENTS.md +++ b/Furious/Plugins/AGENTS.md @@ -1,28 +1,20 @@ -# Plugin architecture guidance +# Plugin guidance -- `Plugins.API` defines stable capability contracts; `Registry` owns registration, selection, materialization, - initialization, rollback, and reverse-order shutdown. -- Use `CoreRuntimeFactory`, `CoreRuntimeRequest`, and `CoreRuntimeLaunch` consistently across capabilities, registry - dispatch, statistics providers, and tests. -- Plugins contribute factories, handlers, descriptors, immutable metadata, and service providers—not live transient - widgets, active core instances, or controller state. -- The registry intentionally keeps registered plugin and capability-provider objects strongly reachable for the - registry lifetime. Their initialization/shutdown contract must release any resources they acquire; this ownership is - not permission to cache factory-created UI or runtimes. -- Protocol parse/export/editor, backend runtime, routing/TUN, statistics, subscription decoding, and navigation behavior - belongs behind capabilities. Shared code must not add core-name conditionals when capability dispatch can express the - policy. -- Registration is deterministic and validates every capability family through focused validators before committing - any index changes. A plugin failure is isolated, logged with plugin/capability context, and does not corrupt already - registered providers. -- Initialization is transactional: partially initialized plugins are rolled back; shutdown is reverse-order and - idempotent. -- Keep discovery imports literal and side-effect-light for source and Nuitka builds. Avoid circular dependencies from - API/model layers into concrete plugins. -- UI factories create fresh owned widgets. Invalid QObject results are explicitly destroyed; registries never retain - rejected objects. +## Registry and extension contract + +- `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. ## Verification -- Test order, duplicate/invalid registration, dynamic dispatch, rollback, shutdown, compiled discovery, and factories - returning invalid objects. +- Test discovery/order, duplicates and invalid capabilities, dispatch, rollback/shutdown, compiled inclusion, and + factories returning invalid objects. diff --git a/Furious/Qt/AGENTS.md b/Furious/Qt/AGENTS.md index 787af3e..810be29 100644 --- a/Furious/Qt/AGENTS.md +++ b/Furious/Qt/AGENTS.md @@ -1,64 +1,38 @@ # Qt foundation guidance -Use the `manage-qt-pyside6-lifetimes` skill for any Qt ownership or lifecycle change. +Use the `manage-qt-pyside6-lifetimes` skill for Qt ownership or lifecycle work. -## Public UI primitives +## Canonical UI surface -- `Furious.Qt` is the canonical Fluent/theme/translation-aware UI surface. Extend/reuse `AppQ*` controls instead of - styling raw Qt controls at call sites. -- Construction-time source text should be retained by the control for retranslation. Do not duplicate manual - `retranslate()` code where an `AppQ*` widget/action/menu already supports it. -- `AppQMessageBox.windowTitle()` is native window-manager/accessibility metadata and is not rendered by the frameless - Fluent surface. Put an optional visible semantic title in `heading`, the primary explanation in `text`, and supporting - details in `informativeText`. Never rely on `setWindowTitle()` alone for user-visible information, and do not promote a - generic application-name title into a visible heading. -- Preserve public exports and wildcard-import compatibility carefully; keep optional/heavy facilities lazily imported - where practical. -- `AppStyleSheet` remains the sole public stylesheet authority. Internal `StyleSheets` modules are data-oriented QSS - fragments that consume the centralized semantic palette; application consumers must not import fragments directly. -- For composite controls, let the outer widget own its frame, radius, and focus indication. Sub-control hover/pressed - fills must remain inside that frame (normally through padding/content origin), and state-order changes should be - verified in both application themes. +- 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. +- 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()` bypasses `AppQDialog.open()`, so its separate message-box registry is its actual async owner; + keep that registry and the base destroyed cleanup balanced. +- `AppStyleSheet` is the public style authority. `StyleSheets` contains internal QSS fragments that consume the semantic + palette; application code does not import fragments directly. -## Ownership and destruction +## Lifetime and threading -- `AppQDialog` owns platform-neutral presentation geometry: simple subclasses declare `DEFAULT_DIALOG_SIZE` or - `FIXED_DIALOG_SIZE`; procedural `prepareInitialGeometry()` runs only at first presentation after subclass construction. - Do not call subclass geometry hooks from constructors or manipulate private first-show state. Dialogs retain the - established center-on-each-presentation behavior unless a specialized class explicitly owns another centering policy. -- `AppQDialog` provides an asynchronous open-dialog registry. `AppQMessageBox` has an additional registry cleanup layer; - it is redundant but harmless. Keep both registries balanced if either implementation changes. -- `AppQTransientDialog` is delete-on-close. Connect completion before `open()` and never access it after destruction. - Reusable dialogs/windows instead need a durable owner and must not be delete-on-close. -- In Nuitka/PySide6 builds, compiled bound methods passed directly to `SignalInstance.connect()` may be protected for the - process lifetime. Connect signals to transient receivers with `connectWeakly(signal, receiver, 'methodName')`; do not - replace this with a closure that strongly captures the receiver. -- `AppQAction.callback` is a deliberate strong reference; owner scope must be no longer than the callback receiver. - Application-lifetime actions cannot capture transient bound methods. -- Parent child widgets/models/delegates/timers where their lifetimes match. Explicitly stop/disconnect timers, remove - mismatched event filters, abort/delete network replies, and clear references when wrappers can outlive C++ objects. -- Do not cache QObject instances or instance-bound methods in global/unbounded caches. Weak callbacks must not close - over their target. +- 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. -## Threading and event loops +## Presentation and verification -- Only the GUI thread mutates widgets. Workers return immutable/result data through queued signals or established - managers. -- Never sleep or perform unbounded host/network/process work in a slot. Timers and debounce/coalescing must have one - owner and must not multiply across show/hide cycles. -- `exec()` is reserved for genuinely synchronous control flow; prefer managed `open()` when continuation logic can live - in `finished` callbacks. - -## Network replies and presentation - -- Give each reply one manager/context owner. Where a newer request supersedes an older one, use a generation/version or - equivalent identity check; independent requests do not need a synthetic generation. Handle success/error/abort once - and call `deleteLater()` on every terminal path. Do not attach ad-hoc application attributes to third-party Qt - objects when a manager mapping suffices. -- Preserve keyboard, focus, shortcuts, accessibility, light/dark theme, layout responsiveness, and translated-text - growth. - -## Verification - -- Run focused UI behavior and lifetime tests. Repeated open/close/show/refresh cycles must stabilize object, signal, - timer, reply, handle, and callback counts. +- 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. +- 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. diff --git a/Furious/Repository/AGENTS.md b/Furious/Repository/AGENTS.md index 8b210ca..e294d96 100644 --- a/Furious/Repository/AGENTS.md +++ b/Furious/Repository/AGENTS.md @@ -1,24 +1,13 @@ # Repository guidance -- Repositories are the persistence authority for domain collections and documents such as profiles, subscriptions, - routing configurations, and TUN settings. Application preferences and current selections may use `AppSettings` at - their owning controller/application boundary. Keep Qt/UI/workflow concerns out of repository implementations. -- Preserve stable IDs, ordering, unknown compatible fields, legacy migrations, and subscription ownership. Display names - and row indexes are not identities. -- Subscription synchronization may update only profiles explicitly managed by that subscription and identified by stable - profile keys. Never delete user-created or another subscription's data. -- Public collection access currently returns live mutable application-owned collections for compatibility. Document this - honestly and avoid introducing a second cached copy; new mutations should flow through named repository operations - when practical. -- Load failures retain recoverable data where possible and log actionable context. Never silently replace malformed - explicit configuration with unrelated defaults when doing so loses user intent. -- If persisted data cannot be decoded, automatic shutdown cleanup must not overwrite the unreadable value with an empty - fallback. A deliberate non-empty repository mutation or explicit sync may replace it as a recovery action. -- Prepare fallible reconciliation work—identity calculation, metadata normalization, and the final collection—before - mutating live repository objects. The commit phase should contain only deterministic assignments. -- Singleton repository caches are application-lifetime finite objects and must be reset/sandboxed in tests. - -## Verification - -- Test old/current schemas, unknown fields, ordering, stable identity, subscription isolation, malformed persisted data, - round trips, and isolated temporary settings. +- 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. diff --git a/Furious/Service/AGENTS.md b/Furious/Service/AGENTS.md index 6096ed7..e7c0955 100644 --- a/Furious/Service/AGENTS.md +++ b/Furious/Service/AGENTS.md @@ -1,62 +1,34 @@ # Service guidance -- Services own workflows and temporary resources; controllers own shared application state. Services may use Qt for - asynchronous I/O/signals but must not own pages or encode presentation policy. -- Never instantiate `QObject` services such as network-access managers at module import time. Acquire them after the - application exists, give them one explicit service/application owner, and release them during that owner's cleanup. -- Inject repositories, runtimes, clients, clocks, and callbacks where practical. One service owns each worker, reply, - timer, executor, process, and cache; cleanup is bounded and idempotent. +## Scope and ownership -## Connection and configuration +- 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. -- `ConnectionManager` coordinates plugin capabilities, core runtimes, proxy/TUN/routing, and rollback. Its `runtimes` - collection contains semantic runtime owners, not necessarily operating-system processes. It consumes runtime copies - and never mutates persisted profiles. -- Preserve the backend native-TUN decision reported by capabilities. Start application tun2socks only when no native TUN - is handled; proxy-only operations strip TUN explicitly. -- Connection startup is one private staged attempt over a runtime configuration copy. Track each exact runtime as soon - as it is acquired; failures roll back only that attempt in reverse order, and commit transfers ownership to the - manager's normal lifecycle. -- Startup either reaches a valid running state or rolls back every process/host mutation with an actionable error. - Shutdown and unexpected-exit paths are bounded and safe to repeat. +## Runtime and asynchronous work -## Async, network, and background work +- `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. -- Each asynchronous workflow defines one explicit ownership and supersession policy: a generation/version where stale - completion is possible, or exact reply/future ownership where requests are independent. Partial results may publish - independently; terminal reply paths abort or finish once and schedule deletion once. -- Bound network, DNS, process, host, and worker work where the provider permits it. Executor callbacks use weak or - otherwise bounded ownership and must not retain a manager forever after shutdown; GUI updates cross via signals. -- Log storage has independent count, total-character, and per-entry hard bounds even when automatic clearing is - disabled. High-rate producers must coalesce GUI notifications; hiding a page may defer rendering but must never defer - draining a process pipe or bounded transport queue. -- Metric history has both a time horizon and a defensive sample-count ceiling. Derive graph buckets on demand rather - than retaining a second ever-growing history. -- Every network reply has a finite transfer timeout unless a documented caller supplies a stricter one. Track replies - by exact object identity, remove every context on the terminal path, and schedule the reply for deletion exactly once. -- Per-subscription versions exist only while the persisted subscription, its timer, or an active reply needs them; - repeated create/delete cycles must not grow bookkeeping dictionaries. -- Logging and metrics collect while pages are hidden but avoid hidden-page rendering. Raw time-series samples are - immutable; stable timestamp buckets are derived display data. -- Log retention is owned by `LogManager`, not a text document. Category pruning/reset notifications must keep lazy UI - sequence state valid. -- Endpoint inspection must enforce the active proxy path, reject stale connection results, use neutral request metadata, - and disclose actual external providers without sending profile secrets. -- `SubscriptionManager` owns download, decoding, filtering, reconciliation, persistence effects, stale-request - rejection, and stable-ID auto-update timers. Subscription views invoke commands and render semantic results; they do - not own this workflow. Remote data is untrusted. -- Long-lived service timers are created and connected once, then reconciled idempotently. Reapplying unchanged policy - must not restart a periodic countdown, reconnect its timeout, or emit a lifecycle transition log. -- Page visibility and presentation refreshes must not control application-level background scheduler lifecycles. - Reconcile schedules at service startup and when the corresponding persisted scheduling policy changes. -- Subscription reply callbacks stage decoded results only. Persist group status and reconcile profiles after the final - request-version check; one group's failure must not abort other current groups in the same completion batch. -- Treat reconnect/disconnect after subscription reconciliation as a post-commit effect. Failure there must be logged - without reporting the already-committed repository update as rolled back. +## Verification -## Code review rules - -- Flag widgets retained by services, unbounded executor/network/process work, callbacks that capture service owners - after shutdown, UI-only cache invalidation, and swallowed provider failures. -- Test success, partial/stale/timeout/cancel/failure, hidden-page behavior, repeated cleanup, and worker/reply lifetime - with fakes—never real host mutation. +- 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. diff --git a/Furious/Utility/AGENTS.md b/Furious/Utility/AGENTS.md index b6d8bd6..f4ac90c 100644 --- a/Furious/Utility/AGENTS.md +++ b/Furious/Utility/AGENTS.md @@ -1,16 +1,9 @@ # Utility process guidance -- This package contains the outer application-process wrapper, not general miscellaneous helpers. Keep its surface small - and avoid adding business/UI policy that belongs in application, controller, or service layers. -- `AppMainProcess` owns its exact child/application runner, signal handlers, exit-code translation, and crash-log - attempt. Signal/exception paths must tolerate partial application startup. -- Crash reporting is best effort but must not hide the original exception; avoid logging secrets from application - history and bound all cleanup before exit. -- Do not introduce process-name cleanup or unmanaged `multiprocessing.Manager`/thread resources. Explicitly shut down - auxiliary process resources when no longer needed. -- Preserve semantic `ApplicationRunner.ExitCode` values and cross-platform spawn behavior. - -## Verification - -- Test normal exit, assertion/unknown exception mapping, crash-log write failure, signals before/after app creation, and - no orphaned manager/child resources. +- 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. diff --git a/Furious/Widget/AGENTS.md b/Furious/Widget/AGENTS.md index 3e9e682..d938472 100644 --- a/Furious/Widget/AGENTS.md +++ b/Furious/Widget/AGENTS.md @@ -1,25 +1,14 @@ # Reusable widget guidance -- Widgets are reusable presentation components below full pages/windows. Prefer controller/service/repository - dependencies passed explicitly; do not add new `AppMainWindow()` reach-through or duplicate application state. -- Subscription views delegate download, decoding, reconciliation, persistence effects, and recurring scheduling to - `SubscriptionManager`; keep view code limited to user intent, presentation, and repository-backed table refresh. -- Models, delegates, menus, actions, spinners, animations, timers, and network-backed helpers need explicit owners. - Persistent page widgets are constructed once; refresh/show cycles toggle state rather than recreate/connect - indefinitely. -- Resolve table/list actions through the model mapping that is current after sort/filter. Use stable repository IDs for - persisted or cross-refresh identity; preserve existing row-index compatibility only where the repository contract - still requires it. Keep model begin/end notifications and repository ordering synchronized. -- Use `AppQ*` controls and responsive layouts. Preserve translated-text growth, shortcuts, focus, accessibility, theme - changes, high-DPI rendering, and hidden-page lazy behavior. -- Expensive parsing, map/network work, metrics aggregation, and subscription synchronization must not freeze the GUI. - Batch, defer, or offload work according to the Qt API involved; publish worker results through owned signals and - reject stale completions where requests can be superseded. -- Transient editors/progress dialogs use managed `open()` lifetime; reusable top-level windows retain one explicit - owner. - -## Code review rules - -- Flag direct global/window reach-through, duplicated persistence logic, row-index identity, repeated signal/timer - creation, hidden-page rendering, and callbacks retaining closed widgets. -- Run relevant behavior plus Qt lifetime tests for repeated refresh/show/open/close cycles. +- 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. diff --git a/Furious/Window/AGENTS.md b/Furious/Window/AGENTS.md index 7f47c84..936bafc 100644 --- a/Furious/Window/AGENTS.md +++ b/Furious/Window/AGENTS.md @@ -1,33 +1,17 @@ # Window and page guidance -- `MainWindow` owns long-lived built-in pages, navigation, page-level managers, and compatibility aliases. Pages remain - stable for the window lifetime; plugin pages are owned by navigation plus `PluginNavigationManager`. -- Main-window page selection and navigation expansion are session-local. Register Home first as the canonical initial - page and keep new navigation views collapsed; never persist or restore either state from a previous process. -- Pages and dialogs adapt controllers/services/repositories to user interaction. Do not create a competing state - authority; prefer controller/service APIs over new cross-page reach-through. -- Long-lived pages create controls, timers, and signal connections once. Show/hide activates lazy rendering or refresh - intent without multiplying objects or background work. -- One-shot editors/prompts inherit `AppQTransientDialog`/`AppQMessageBox`, connect `finished` before `open()`, and are - not retained afterward. Reusable `TextEditorWindow` keeps an explicit owner, survives normal close/show, and is - destroyed only by that owner. -- Close buttons route through the normal `close()`/`closeEvent` path so save/discard/cancel and cleanup remain - canonical. Base event handlers are called unless intentionally documented. -- Keep file/network/core work bounded and off the GUI thread when it can block. Present structured failures without - swallowing them or exposing secrets. -- Use Fluent `AppQ*` controls, normal layouts, translation-aware construction, and theme callbacks. Preserve shortcuts, - default/escape actions, focus, resizing, navigation overlay behavior, and light/dark presentation. -- `AppQMainWindow` owns platform-neutral first-show preparation, centering, and shown-wrapper retention. Subclasses - declare stable defaults through `DEFAULT_WINDOW_SIZE`; persistent windows override `prepareInitialGeometry()` only - for product-owned restoration/migration and call `restoreInitialGeometry()` so a valid saved position suppresses - centering. Never call overridable geometry hooks during construction or manipulate private first-show flags. -- Restore composed top-level window geometry only after its persistent child layout exists. Treat Qt's explicit - `restoreGeometry()` result as authoritative; missing or invalid data uses the canonical application default and must - not be inferred from magic window dimensions. Repeated show/hide cycles preserve the live window geometry. - -## Code review rules - -- Flag local top-level windows shown without a durable owner, transient dialogs cached by pages/controllers, repeated - show-time connections, direct worker-thread UI mutation, and manual translation already handled by controls. -- Test navigation/page restoration, async dialog continuations, unsaved-close flow, hidden-page behavior, and repeated - lifecycle stability. +- `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. diff --git a/Icons/AGENTS.md b/Icons/AGENTS.md index 2249f00..bb8ffb1 100644 --- a/Icons/AGENTS.md +++ b/Icons/AGENTS.md @@ -1,16 +1,8 @@ -# Icon asset guidance +# Icon source guidance -- This tree contains source assets consumed by the Qt resource workflow. Reuse an existing semantic icon before adding a - near-duplicate. -- Keep SVGs vector, compact, theme-compatible, and free of embedded raster data, scripts, remote resources, editor - metadata, or hard-coded backgrounds. -- Follow the established monochrome/current-color convention for UI icons; add explicit light/dark variants only when - the design cannot be expressed through tinting. -- Preserve upstream license/attribution requirements and stable resource paths. Renaming/removing an asset requires - updating all references and regenerating `Furious/Frozenlib/AppResources.py` through the existing workflow. -- Do not hand-edit the generated resource module. - -## Verification - -- Search all references, regenerate resources, test light/dark and high-DPI rendering, and visually check icon - size/alignment in the target Fluent control. +- 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. diff --git a/tests/AGENTS.md b/tests/AGENTS.md index 6598399..9f9707a 100644 --- a/tests/AGENTS.md +++ b/tests/AGENTS.md @@ -2,57 +2,38 @@ ## Isolation -- Tests must never affect a running Furious instance or production data. Set `QT_QPA_PLATFORM=offscreen` before Qt - import and use `tests/support.py` for one small application and temporary INI-backed settings. -- Never mutate real proxy, DNS, routing, TUN, startup registration, tray, network interfaces, or external services. - Patch host/network APIs and inject fake controllers, runtimes, repositories, clocks, and clients. -- Own exact subprocess handles/PIDs and threads. Use bounded waits and clean up only resources the test created; never - discover or terminate by process name. -- Normal tests require no external network or installed proxy core. Keep packaged/manual smoke procedures explicit and - disposable. +- 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. -## Test design +## Test contracts -- Test public behavior and architectural contracts, not implementation trivia. Cover success, validation failure, - timeout, cancel, partial/stale result, cleanup, and backward-compatible persisted input. -- For multi-step mutations, inject a failure immediately before the commit point and assert that live objects and - persisted bytes remain unchanged; separately test post-commit side-effect failures without pretending rollback. -- Use canonical `CoreRuntime` vocabulary in runtime factories, registry fixtures, and traffic-statistics providers. -- Separate persisted configuration from runtime-copy assertions. Normal connection, generated native TUN, preserved user - TUN, and proxy-only stripping must not share a helper that erases their differences. -- Keep deterministic logic/UI behavior in the normal tier. Put repeated live-object, native-handle, thread, subprocess, - and RSS trends in explicit stress tiers. -- Scale cheap fake/model operations into the thousands where useful. Keep real Qt, WebEngine, and subprocess counts - proportional to their cost; workloads beyond the normal stress tier require the explicit - `FURIOUS_VERY_HEAVY_TESTS=1` opt-in. -- Warm up allocators before interpreting RSS, sample only at bounded batch boundaries, and prefer exact live-object, - registry, thread, handle, and child-process ownership assertions over arbitrary memory thresholds. -- Qt lifetime tests use real close/deferred-delete paths, weak references, destroyed signals, and live counts. - `gc.collect()` is diagnostic at batch boundaries only; threshold increases or production collection are not leak - fixes. -- Avoid timing-only assertions. Drive events deterministically and assert final ownership/state plus absence of - duplicate connections/timers/replies. -- Update `tests/README.md` when modules, tiers, or environment requirements change. Documentation commands use the - active `python` environment. +- 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. +- 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. -## Real lifecycle and process-boundary regressions +## Real lifecycle boundaries -- Apply the highest-fidelity safe test boundary throughout the suite. Unit tests remain appropriate for deterministic - logic, but bugs involving Qt event loops, native callbacks, interpreter finalization, subprocess teardown, or wrapper - destruction need a focused integration test that exercises the real boundary that failed. -- For application-lifecycle regressions, start a clean child Python process, force `QT_QPA_PLATFORM=offscreen` before - importing Qt, construct the smallest real `QApplication`/`DesktopApplication` and real Qt window needed, run the real - event loop, request shutdown through the public path, and assert the child process exits normally. Repeat the cycle - when the defect is intermittent. -- A lifecycle child must be hermetic: bypass single-instance discovery and IPC, use temporary settings and application - identity where relevant, disable tray and startup restoration, mock all host/network integrations, and never attach - to, signal, inspect, or change a potentially running Furious instance. -- Child-process tests own only the windows, threads, handles, and processes they create. Give every wait and child a - bounded timeout, capture diagnostics, and terminate only that exact child on timeout. Never use process-name cleanup. -- Visible diagnostic windows are for an explicit manual smoke procedure only. Automated tests always use the offscreen - platform so they cannot flash, steal focus, or interact with the desktop session. +- 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`. -## Code review rules +## Review -- Flag production settings/host mutation, broad process cleanup, unbounded waits, external network dependence, shared - mutable fixtures, and tests that only check storage when runtime output is the contract. +- 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.