diff --git a/AGENTS.md b/AGENTS.md index 4f6256c..c720e44 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,38 +1,67 @@ # Furious repository guidance -## Source of truth and change discipline +## Working method -- Treat the checked-out tree as authoritative. Do not resurrect deleted experiments or infer current architecture from old commits or conversations. -- Preserve unrelated working-tree and staged changes. Keep edits scoped to the requested behavior. -- When `.codegraph/` exists, use `codegraph explore` before text search or broad file reading to locate symbols and understand call paths. Use `rg` for exact follow-up searches. -- Before running Python commands, inspect the repository root for an existing environment matching `.venv*` or `venv*`. Prefer its interpreter for project scripts, tests, formatters, generators, and dependency-backed tools whenever usable; do not create or modify an environment unless the task requires it. -- Keep the existing GPL header, `from __future__` placement, import grouping, and repository naming style in touched Python files. +- 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. -## Architecture boundaries +## Pythonic design -- Keep models/configuration documents independent of presentation. Repositories own persistence, services own workflows, controllers own application state/orchestration, and widgets/actions are thin adapters. -- Prefer plugin capabilities and backend factories over protocol/core conditionals in shared application code. -- Treat persisted user configuration as input. Connection-time, test-time, routing, logging, and statistics preparation must operate on explicit runtime copies unless an API is documented as mutating persisted state. -- Application-wide controllers and repositories may be process-lifetime objects; transient UI and temporary process resources must not become accidental global state. -- Keep platform-specific host mutation behind the existing runtime/system abstractions. A feature must remain safe to import and test on unsupported platforms. +- 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 it, services perform + workflows, controllers own application state/orchestration, plugins/backends own protocol-specific behavior, 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: network requests, subprocess startup/shutdown, thread joins, and host commands need timeouts or a + documented non-GUI execution context. Cleanup must be idempotent and own exact resources, 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. -## Generated artifacts and translations +## Repository invariants -- `Furious/Frozenlib/AppResources.py` and `Furious/Externals/GenTranslation.py` are generated artifacts. Do not hand-maintain them as ordinary source. -- Add user-facing strings through the existing translation-aware widgets/actions and `_()` extraction conventions. Regenerate translations with `Translation.py` when translation source changes. -- Pass only static string literals to `_()`. Runtime-formatted translations such as `_(f'{arg} do something...')` and `_('{arg} do something...'.format(...))` are unsupported; translate static fragments and compose dynamic values outside `_()` instead. -- Curly braces in extracted strings are reserved for application-constant substitution: `Translation.py` resolves every `{name}` through `Furious.Frozenlib.Constants`. Do not use braces as ordinary runtime-format placeholders. +- Treat persisted user configuration as input. Connection, routing, testing, logging, and statistics preparation operate + on explicit runtime copies unless an API is documented as mutating storage. +- Prefer plugin capabilities/factories over protocol or core conditionals in shared managers. Registries store classes, + factories, descriptors, and immutable metadata—not transient UI instances. +- `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. -## Verification +## Generated files and translations -- Run the narrowest relevant tests first, then the affected test tier documented in `tests/README.md`. -- Format only touched Python files with the repository Black configuration, then run Black check mode on the same files. -- For backend/process/platform changes, verify failure cleanup and bounded shutdown as well as the success path. -- For Qt ownership changes, follow `Furious/AGENTS.md`, use the `manage-qt-pyside6-lifetimes` skill, and run the relevant lifetime tests; do not use forced garbage collection as a production fix. +- `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. -## Code review rules +## Verification and review -- Flag UI code that becomes a second owner of controller/domain state. -- Flag mutation of persisted configuration during runtime preparation. -- Flag new protocol-specific branches in shared managers when a plugin capability can own the behavior. -- Flag edits to generated artifacts without the corresponding generator workflow. +- 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. +- 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. diff --git a/Furious/AGENTS.md b/Furious/AGENTS.md index e4f93d7..97bba8d 100644 --- a/Furious/AGENTS.md +++ b/Furious/AGENTS.md @@ -1,46 +1,51 @@ -# Furious application guidance +# Furious package guidance -These rules apply to all application code below `Furious/` and refine the repository-wide guidance. +## Layering and state -## State and layering +- `Models` and `Interface` define core-neutral data and contracts. `Repository` owns persistence, `Service` owns + workflows/resources, `Controllers` own shared application state, `Plugins`/`Backends` own protocol behavior, and + `Application` composes the runtime. `Qt`, `Widget`, and `Window` present that state. +- 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. -- `ConnectionController`, `RoutingController`, and `SettingsController` are the application state authorities. UI consumers observe/delegate; they do not maintain parallel state machines. -- Services and controllers may depend on models, repositories, plugin capabilities, and runtime abstractions. They must not strongly own transient dialogs, editors, message boxes, or page-local widgets. -- Keep blocking I/O, subprocess waits, DNS/network work, and heavy aggregation off the GUI thread. Marshal presentation updates back through Qt signals/slots. -- Cleanup/shutdown methods must be safe on partial startup and repeated calls. Release callbacks, timers, threads, child processes, and native handles owned by the component. +## Qt/PySide6 lifetime invariants -## Qt/PySide6 lifetime +Classify every dynamic Qt object before choosing ownership: -Every `QObject` has an intentional Python owner, Qt parent, lifetime category, and destruction path. +- **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. -Use the repository `manage-qt-pyside6-lifetimes` skill for any change or audit involving Qt object creation, ownership, signals, timers, filters, caches, windows/dialogs, memory growth, premature collection, or stale wrappers. Read its detailed lifetime reference completely before acting. +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. -- Long-lived pages/controllers are application owned and created once. -- Reusable windows retain one explicit strong owner and reset state when shown again. -- Transient dialogs/windows retain a strong owner only while visible, use the normal close/accept/reject path, and are released after destruction. Use `WA_DeleteOnClose` only for genuinely transient objects. -- A local variable followed by `.show()`/`.open()` is not sufficient ownership for an asynchronous top-level window. Conversely, parenting a closed transient dialog to an application-lifetime widget does not make its destruction correct. -- Parent timers, animations, actions, menus, models, delegates, and event filters according to their intended owner. Stop/remove them when Qt automatic teardown is not sufficient. -- Prefer QObject-bound slots. Review lambdas, closures, partials, callbacks, and long-lived senders for captures of transient UI. -- Never place transient `QObject` instances or bound instance methods in unbounded caches. Static caches may contain immutable metadata, strings, classes/factories, or application-lifetime icons. -- Weak registries must not have a parallel strong owner, callbacks that capture the target, or dead entries that accumulate. Check wrapper validity before invoking weakly registered QObjects. -- Custom `closeEvent`, `accept`, `reject`, and `done` implementations must preserve the corresponding Qt lifecycle unless intentionally documented. -- Do not update widgets from worker threads. Do not mask ownership bugs with routine `gc.collect()`, global window retention, or swallowed deleted-wrapper exceptions. +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 the application Fluent widgets, action rows, menus, design tokens, and translation-aware controls before creating one-off styling or manual retranslation code. -- Keep page/window presentation thin: existing `QAction` or controller/service logic should remain the behavior source when controls are rearranged. -- When replacing a dialog/menu/window implementation, preserve shortcuts, default/escape results, enabled/checkable state, translations, and explicit transient/reusable lifetime semantics. +- 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. -## Required verification +## Verification -- For transient UI changes, exercise repeated create/open/close cycles and verify `destroyed`, weak-reference clearing, and stable live-object counts. -- For reusable windows, verify normal close/show reuse and explicit owner destruction without duplicated actions or signals. -- Run `tests.test_qt_lifetime` for ownership changes and the relevant UI behavior tests; use the explicit stress tier only when the change warrants it. - -## Code review rules - -- Flag controllers/services/registries retaining transient widgets or widget-bound callbacks. -- Flag asynchronous top-level windows without a durable Python owner. -- Flag parentless active timers, stale event filters, instance-method caches on transient UI, or close handlers that only hide a transient object. -- Flag direct widget mutation from non-GUI threads. +- 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. diff --git a/Furious/Actions/AGENTS.md b/Furious/Actions/AGENTS.md new file mode 100644 index 0000000..8aa4591 --- /dev/null +++ b/Furious/Actions/AGENTS.md @@ -0,0 +1,19 @@ +# Actions 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. + +## Code review rules + +- 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. diff --git a/Furious/Application/AGENTS.md b/Furious/Application/AGENTS.md new file mode 100644 index 0000000..02a4aa8 --- /dev/null +++ b/Furious/Application/AGENTS.md @@ -0,0 +1,23 @@ +# Application composition guidance + +- `DesktopApplication` is the composition root. It creates and durably owns application-lifetime controllers, + repositories, services, pages, main window, tray, thread pool, IPC server, and plugin registry integration. +- 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. +- The tray owns its long-lived actions/menus and reflects controller state. Dynamic submenu rebuilds must not retain + stale actions or menus. +- 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. + +## 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. diff --git a/Furious/Backends/AGENTS.md b/Furious/Backends/AGENTS.md index e7df26c..f2f0258 100644 --- a/Furious/Backends/AGENTS.md +++ b/Furious/Backends/AGENTS.md @@ -1,42 +1,38 @@ -# Backend and core-runtime guidance +# Backend and core-integration guidance -These rules apply to bundled backend implementations and refine `Furious/AGENTS.md`. +- 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. -## Configuration ownership +## Native TUN policy -- The configuration passed to a core is authoritative. Preserve the persisted/original document and apply connection, routing, logging, statistics, and test preparation to a runtime/deep copy. -- Do not silently repair or delete explicit user core configuration and switch networking modes. Let the core or normal validation surface malformed user configuration. -- Protocol-specific parsing, editing, export, runtime construction, and compatibility belong to the backend/plugin capability, not shared connection UI or `ConnectionManager` conditionals. +For a normal connection: -## Native TUN contract +- 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. -`KernelFactory.prepareTUN()` is the normal-connection ownership decision: +Never remove an explicit user TUN merely because an application toggle is off, and never run native TUN plus application +tun2socks together. -- Native-TUN option enabled: Furious-generated native TUN is authoritative in the runtime copy. Replace existing runtime native TUN, report handled, and do not start application tun2socks. -- If requested managed native TUN cannot be prepared safely, fail the connection with a useful error; never silently change to application tun2socks. -- Native-TUN option disabled with explicit user native TUN: preserve it unchanged, report handled (including malformed explicit values), and do not start application tun2socks. -- Native-TUN option disabled with no native TUN: do not inject native TUN; application tun2socks may be selected when global TUN mode requests it. -- Never allow backend native TUN and application tun2socks to run together. +## Runtime and UI -Proxy-only operations such as download-speed tests must derive a separate configuration and explicitly strip/omit native TUN. Do not weaken normal-connection preservation to satisfy a probe/test workflow. Enabling the managed native-TUN option is the explicit user choice that permits replacement in the runtime copy; toggling it off never removes custom native TUN. +- 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. -## Core lifecycle +## Verification -- Factories return prepared kernel launches; process objects own only their exact child/process resources and callbacks. -- Startup failures must expose a useful start error and clean partially created resources. Shutdown must be bounded, reap exact owned children, release readers/queues/timers, and be idempotent. -- Do not use shell expansion for core commands. Do not log credentials, full arguments, secrets, or environment values. -- Keep lazy editor imports as literal imports inside factories/providers so Qt initialization stays lazy and Nuitka can discover dependencies. - -## Required verification - -- Run `tests.test_native_tun_semantics` after changing native-TUN preparation or application tun2socks selection. -- Test both persisted-document immutability and the exact runtime document submitted to the core. -- Test normal connection and proxy-only preparation independently; do not share a helper that erases their policy difference. -- For process changes, run the relevant external/process stress tests and verify failure cleanup. - -## Code review rules - -- Flag removal of custom native TUN while its backend-managed toggle is disabled. -- Flag a handled native-TUN path that can still instantiate application tun2socks. -- Flag test/probe TUN stripping implemented inside the normal connection policy. -- Flag unbounded process waits, orphaned reader threads/handles, or errors hidden behind a generic fallback. +- 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. diff --git a/Furious/Controllers/AGENTS.md b/Furious/Controllers/AGENTS.md new file mode 100644 index 0000000..70248be --- /dev/null +++ b/Furious/Controllers/AGENTS.md @@ -0,0 +1,22 @@ +# 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. + +## Code review rules + +- 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. diff --git a/Furious/Core/AGENTS.md b/Furious/Core/AGENTS.md new file mode 100644 index 0000000..82e5d50 --- /dev/null +++ b/Furious/Core/AGENTS.md @@ -0,0 +1,18 @@ +# Process-backed core-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. +- 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. + +## Verification + +- Test invalid specs, failed spawn, early exit, normal output, bounded stop escalation, repeated dispose, exact-child + cleanup, and no residual timers/threads/handles. diff --git a/Furious/Extensions/AGENTS.md b/Furious/Extensions/AGENTS.md new file mode 100644 index 0000000..075fb66 --- /dev/null +++ b/Furious/Extensions/AGENTS.md @@ -0,0 +1,15 @@ +# 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 9dbd80d..1b4d2f7 100644 --- a/Furious/Externals/AGENTS.md +++ b/Furious/Externals/AGENTS.md @@ -1,56 +1,19 @@ -# Translation catalog guidance +# Generated translation catalog guidance -These instructions apply to `Furious/Externals/` and refine the repository-level generated-artifact and translation -rules. +- `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 sorted, deduplicated list of fully qualified Python module names that contain the extracted source text. + 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. -## Generated catalog ownership +## Verification -- `GenTranslation.py` is generated by the repository-root `Translation.py`; do not hand-maintain catalog entries for - routine source changes. -- Add or change extracted source strings at their call sites, run the translation generator for every supported - language, review the translations, and regenerate the catalog. -- If generated record structure or key ordering needs correction, fix `Translation.py` rather than applying a one-off - rewrite to `GenTranslation.py`. - -## Translation record structure - -- `TRANSLATION` maps each exact English source string to one record. -- `source` is the generator-maintained list of fully qualified modules where that string is extracted. -- Each supported non-English language has one abbreviation key, such as `RU` or `ZH`, whose value is the reviewed - user-facing translation. -- `isReviewed` is the string `"True"` or `"False"`, not a JSON/Python boolean. Mark it `"True"` only after every - language value in the record has been reviewed. -- Preserve this preferred serialized key sequence in every record: `source`, language 1, language 2, ..., `isReviewed`. - Keep `source` first and `isReviewed` last; use the catalog's stable language order between them. -- Do not add ad-hoc metadata fields that the generator and runtime do not understand. - -For example: - -```python -"Delete": { - "source": [ - "Furious.Backends...", - "Furious.Backends...", - ], - "RU": "Удалить", - "ZH": "删除", - "isReviewed": "True" -} -``` - -## Extraction and review rules - -- Keep translation source keys as literal strings discoverable by the existing `_()` extractor. Do not pass formatted - strings, f-strings, or `.format(...)` results to `_()`. -- Braces in extracted strings are reserved for application-constant substitution by `Translation.py`; they are not - general runtime-format placeholders. -- Let the generator derive `source`; do not fabricate or retain stale module names manually. -- Review natural terminology and meaning in every supported language, not only literal word correspondence. Do not - approve source-language placeholders as completed translations. - -## Code review rules - -- Flag direct catalog edits that should have been made through source extraction and regeneration. -- Flag records whose key sequence is not `source`, language keys, then `isReviewed`. -- Flag `"isReviewed": "True"` when any language is missing or unreviewed. -- Flag dynamic formatting inside `_()` and changes that treat braces as ordinary formatting syntax. +- 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. diff --git a/Furious/Frozenlib/AGENTS.md b/Furious/Frozenlib/AGENTS.md new file mode 100644 index 0000000..cdf0434 --- /dev/null +++ b/Furious/Frozenlib/AGENTS.md @@ -0,0 +1,34 @@ +# Frozenlib foundation 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. + +## Platform and process safety + +- Host mutation (proxy, DNS, startup registration, session, routing) is platform-isolated, returns/logs actionable + failure, uses bounded commands/joins, and can be fully mocked. 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. + +## Mixins and resources + +- 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. + +## Code review rules + +- Flag GUI/high-level imports, unbounded host commands or joins, state persisted after host failure, broad process-name + cleanup, sensitive logging, unbounded external-input caches, and globals holding transient objects. +- Run direct mocked platform/helper tests plus affected controller/service tests. diff --git a/Furious/Interface/AGENTS.md b/Furious/Interface/AGENTS.md new file mode 100644 index 0000000..af4edfd --- /dev/null +++ b/Furious/Interface/AGENTS.md @@ -0,0 +1,20 @@ +# Interface contract 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. diff --git a/Furious/Models/AGENTS.md b/Furious/Models/AGENTS.md new file mode 100644 index 0000000..c21e059 --- /dev/null +++ b/Furious/Models/AGENTS.md @@ -0,0 +1,20 @@ +# 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. diff --git a/Furious/Plugins/AGENTS.md b/Furious/Plugins/AGENTS.md index f8b3b84..d2b35c6 100644 --- a/Furious/Plugins/AGENTS.md +++ b/Furious/Plugins/AGENTS.md @@ -1,24 +1,25 @@ # Plugin architecture guidance -These rules apply to plugin APIs, registries, discovery, and capability dispatch. +- `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. +- 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. -## Capability model +## Verification -- Plugins declare metadata and stable capability objects. Registries index protocol handlers, editor providers, kernel factories, traffic providers, settings/page providers, and subscription decoders by stable IDs. -- Register classes/factories/descriptors and immutable metadata, not transient editors, dialogs, menus, or pages. A provider may create UI on demand, with ownership left to the UI caller. -- Put protocol/core behavior behind the closest capability. Shared UI/services query the registry rather than branching on protocol names or importing official backend internals. -- Keep headless discovery free of eager Qt editor/window imports. Use literal lazy imports in editor factories so static packagers still see every dependency. - -## Registry lifecycle and compatibility - -- Validate all IDs, schemes, configuration types, kernel types, and duplicates before committing registration. A failed registration must roll back every index and initialized resource. -- The registry owns plugin initialization and shuts plugins down once in reverse registration order. Plugin shutdown must tolerate partial initialization. -- Avoid package-level import cycles: API/model layers stay lower-level; UI-specific imports occur only when a presentation capability is invoked. -- Subscription decoders return data/configuration, never executable or live UI/runtime objects. Keep untrusted subscription payloads outside executable-core capabilities. - -## Code review rules - -- Flag live transient `QObject` instances stored in a plugin/capability registry. -- Flag new global protocol conditionals or direct official-plugin imports from shared services/UI. -- Flag dynamic string imports that Nuitka cannot discover when literal lazy factories are practical. -- Flag partially registered plugins after validation/initialization failure or non-idempotent shutdown. +- Test order, duplicate/invalid registration, dynamic dispatch, rollback, shutdown, compiled discovery, and factories + returning invalid objects. diff --git a/Furious/Qt/AGENTS.md b/Furious/Qt/AGENTS.md new file mode 100644 index 0000000..70e3c4c --- /dev/null +++ b/Furious/Qt/AGENTS.md @@ -0,0 +1,49 @@ +# Qt foundation guidance + +Use the `manage-qt-pyside6-lifetimes` skill for any Qt ownership or lifecycle change. + +## Public UI primitives + +- `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. +- 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. + +## Ownership and destruction + +- `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. +- `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. + +## Threading and event loops + +- 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, reject stale generations, handle success/error/abort exactly 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. diff --git a/Furious/Repository/AGENTS.md b/Furious/Repository/AGENTS.md index 33133c5..4debca7 100644 --- a/Furious/Repository/AGENTS.md +++ b/Furious/Repository/AGENTS.md @@ -1,24 +1,19 @@ -# Repository and persistence guidance +# Repository guidance -These rules apply to storage adapters and persisted application data. +- Repositories are the only persistence authority for profiles, subscriptions, routing, and TUN settings. 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. +- Singleton repository caches are application-lifetime finite objects and must be reset/sandboxed in tests. -## Persistence contracts +## Verification -- Repository objects are the persistence boundary. UI/controllers/services use repository APIs rather than writing their own `QSettings` keys for domain records. -- Preserve stable identities: profile IDs identify profiles, subscription group IDs identify sources, and subscription profile keys link upstream members to their owner. Do not match ownership by display name, URL, or table position. -- Subscription synchronization is group scoped. Updating/removing one group must not affect manual profiles or profiles owned by another group; retained profiles keep local metadata and stable IDs. -- Migrations must be conservative and non-destructive. Keep legacy keys registered/read when required, normalize incompatible metadata safely, and preserve unrelated/unknown data where the current format supports it. -- Copies intended as manual profiles receive a new identity and clear subscription ownership. Routine edits must not accidentally detach or reassign ownership. -- Application-lifetime cached repositories are intentional; do not place transient UI or short-lived runtime resources in them. - -## Required verification - -- Run repository/model tests with the isolated temporary `QSettings` namespace. -- Add migration tests for legacy input and round-trip tests for current data before changing serialized shapes. -- For subscription changes, test add/update/remove, stable order/identity, other-group isolation, and manual-profile preservation. - -## Code review rules - -- Flag persistence writes outside the repository/settings abstraction for domain data. -- Flag subscription reconciliation based only on names, URLs, or row indices. -- Flag migrations that delete unrelated data or overwrite a legacy value before successful conversion. +- Test old/current schemas, unknown fields, ordering, stable identity, subscription isolation, malformed persisted data, + round trips, and isolated temporary settings. diff --git a/Furious/Service/AGENTS.md b/Furious/Service/AGENTS.md new file mode 100644 index 0000000..4b8d449 --- /dev/null +++ b/Furious/Service/AGENTS.md @@ -0,0 +1,42 @@ +# 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. +- 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. + +## Connection and configuration + +- `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. + +## Async, network, and background work + +- Each asynchronous request has an explicit generation/context. Partial results may publish independently, while + stale/aborted replies are ignored and deleted exactly once. +- Timeouts apply to network, DNS, executor, process, and host work. Executor callbacks must not retain a manager forever + after shutdown; GUI updates cross via signals. +- 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. + +## 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. diff --git a/Furious/Utility/AGENTS.md b/Furious/Utility/AGENTS.md new file mode 100644 index 0000000..b6d8bd6 --- /dev/null +++ b/Furious/Utility/AGENTS.md @@ -0,0 +1,16 @@ +# 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. diff --git a/Furious/Widget/AGENTS.md b/Furious/Widget/AGENTS.md new file mode 100644 index 0000000..e328148 --- /dev/null +++ b/Furious/Widget/AGENTS.md @@ -0,0 +1,23 @@ +# 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. +- Table selection operates on stable repository IDs, not visual rows after sort/filter. Keep model begin/end + notifications, timer collections, and repository order 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, syntax highlighting, map/network work, metrics aggregation, and subscription synchronization must + not freeze the GUI. Publish results through owned signals and reject stale generations. +- 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. diff --git a/Furious/Window/AGENTS.md b/Furious/Window/AGENTS.md new file mode 100644 index 0000000..9f18dbb --- /dev/null +++ b/Furious/Window/AGENTS.md @@ -0,0 +1,24 @@ +# 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`. +- 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. + +## 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. diff --git a/Icons/AGENTS.md b/Icons/AGENTS.md new file mode 100644 index 0000000..2249f00 --- /dev/null +++ b/Icons/AGENTS.md @@ -0,0 +1,16 @@ +# Icon asset 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. diff --git a/tests/AGENTS.md b/tests/AGENTS.md index 24bbb8c..e379b3a 100644 --- a/tests/AGENTS.md +++ b/tests/AGENTS.md @@ -1,25 +1,51 @@ # Furious test guidance -These rules apply to the isolated `unittest` suite and refine repository guidance. - ## Isolation -- Tests must never affect an already-running Furious instance or production user data. Set `QT_QPA_PLATFORM=offscreen` before importing Qt and use helpers in `tests/support.py`. -- Route `QSettings` to the temporary INI sandbox. Never read/write the production organization/application namespace. -- Do not change the real system proxy, routing table, TUN devices, startup registration, tray, or network interfaces. Patch host-mutation APIs and inject fake controllers/managers. -- Do not discover, signal, terminate, or clean up processes by name. Own exact subprocess handles/PIDs and threads, use bounded waits, and reap only resources the test created. -- Tests must not require external network access or installed proxy-core executables unless explicitly marked/documented as an optional smoke procedure. +- 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. ## Test design -- Keep deterministic logic/behavior tests in the normal tier. Put repeated live-object/native-handle/RSS checks in the explicit stress tier. -- Test persisted input and runtime copies separately for configuration preparation. A helper must not make normal connection and proxy-only behavior accidentally identical. -- Qt lifetime tests use real close/deferred-delete paths, weak references, and live-object counts. `gc.collect()` is allowed only at diagnostic batch boundaries, never as production behavior or once-per-operation masking. -- Threshold increases are not a leak fix. Investigate linear object/handle growth and distinguish it from allocator/native high-water caching. -- Update `tests/README.md` when adding a test module or changing tiers/required environment setup. Commands in documentation use the active `python` environment. +- Test public behavior and architectural contracts, not implementation trivia. Cover success, validation failure, + timeout, cancel, partial/stale result, cleanup, and backward-compatible persisted input. +- 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. +- 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. + +## Real lifecycle and process-boundary regressions + +- 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. ## Code review rules -- Flag any test that can touch production `QSettings` or host networking. -- Flag broad process cleanup, unbounded waits, timing-only assertions, or dependence on an existing GUI session. -- Flag tests that assert only stored configuration when the bug concerns the runtime document submitted to a core. +- 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.