Add scoped repository guidance

Signed-off-by: Loren Eteval <loren.eteval@proton.me>
This commit is contained in:
Loren Eteval
2026-08-20 19:35:06 +08:00
parent 0d3d396b74
commit 03631120da
21 changed files with 559 additions and 203 deletions
+56 -27
View File
@@ -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.
+39 -34
View File
@@ -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.
+19
View File
@@ -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.
+23
View File
@@ -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.
+29 -33
View File
@@ -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.
+22
View File
@@ -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.
+18
View File
@@ -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.
+15
View File
@@ -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.
+16 -53
View File
@@ -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.
+34
View File
@@ -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.
+20
View File
@@ -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.
+20
View File
@@ -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.
+21 -20
View File
@@ -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.
+49
View File
@@ -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.
+16 -21
View File
@@ -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.
+42
View File
@@ -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.
+16
View File
@@ -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.
+23
View File
@@ -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.
+24
View File
@@ -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.
+16
View File
@@ -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.
+41 -15
View File
@@ -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.