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