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